diff --git a/API_CONFIG_FINAL_SUMMARY.md b/API_CONFIG_FINAL_SUMMARY.md deleted file mode 100644 index be832b1..0000000 --- a/API_CONFIG_FINAL_SUMMARY.md +++ /dev/null @@ -1,390 +0,0 @@ -# ✅ API 配置功能 - 完成清单 - -## 📦 已完成的功能模块 - -### **1. 后端实现** ✅ - -#### **工作流管理服务** -- ✅ `backend/services/comfyui_workflow_manager.py` (173行) - - 列出所有工作流 - - 上传工作流(带验证) - - 删除工作流(保护默认文件) - - 加载工作流 - - 提示词替换功能 - -#### **API 端点** (6个) -- ✅ `GET /api/api-config/comfyui/workflows` - 获取工作流列表 -- ✅ `POST /api/api-config/comfyui/workflows/upload` - 上传工作流 -- ✅ `DELETE /api/api-config/comfyui/workflows/{filename}` - 删除工作流 -- ✅ `GET /api/api-config/comfyui/workflows/{filename}` - 获取工作流详情 -- ✅ `POST /api/api-config/test-comfyui-connection` - 测试 ComfyUI 连接 -- ✅ `POST /api/api-config/test-cloud-connection` - 测试云端 API 连接 - -#### **默认工作流** -- ✅ `backend/data/comfyui_workflows/default_txt2img.json` - - 标准 ComfyUI API 格式 - - 7个节点(KSampler、CheckpointLoader、EmptyLatentImage、CLIPTextEncode x2、VAEDecode、SaveImage) - - 包含 `_meta` 元数据 - - 中文节点标题 - ---- - -### **2. 前端实现** ✅ - -#### **核心组件** -- ✅ `ComfyUIWorkflowManager.jsx` (179行) - - 工作流列表显示 - - 上传功能 - - 删除功能 - - 刷新功能 - - 空状态提示 - - 使用说明 - -#### **主配置页面** -- ✅ `ApiConfig.jsx` (完整重构) - - 模式切换卡片(本地/云端) - - 本地 ComfyUI 配置表单 - - 云端 API 配置表单 - - 嵌套路径更新逻辑 - - 修改跟踪系统 - - 测试连接功能 - -#### **样式系统** -- ✅ `ApiConfig.css` (扩展 300+ 行) - - 模式选择器样式 - - Toggle Switch 开关 - - 工作流管理器样式 - - 响应式设计 - - 防横向滚动 - ---- - -### **3. 数据结构** ✅ - -#### **imageModel 新结构** -```javascript -{ - mode: 'local', // 'local' | 'cloud' - - local: { - apiUrl: 'http://comfyui:8188', - websocketEnabled: true, - queueTimeout: 300, - defaultWorkflow: 'default_txt2img.json' - }, - - cloud: { - provider: 'dall-e', - apiUrl: 'https://api.openai.com/v1/images/generations', - apiKey: '', - model: 'dall-e-3' - } -} -``` - ---- - -### **4. 响应式设计** ✅ - -#### **SillyTavern 风格布局** -- ✅ 无页面级滚动条 (`overflow: hidden`) -- ✅ 三栏独立滚动 (`overflow-y: auto`) -- ✅ 禁止横向滚动 (`overflow-x: hidden`) -- ✅ 视口高度布局 (`100vh`) -- ✅ 媒体查询适配 (<768px) - ---- - -### **5. 交互逻辑** ✅ - -#### **核心函数** -- ✅ `handleChange(e, path)` - 支持嵌套路径更新 -- ✅ `handleImageModeChange(mode)` - 模式切换 -- ✅ `testComfyUIConnection(apiUrl)` - 测试本地连接 -- ✅ `testCloudConnection(config)` - 测试云端连接 -- ✅ `handleOpenSaveModal()` - 打开保存对话框 -- ✅ `handleSave()` - 保存配置 - -#### **修改跟踪** -- ✅ 自动标记已修改的配置 -- ✅ 保存按钮显示修改数量 -- ✅ 标签页红点提示 - ---- - -## 📁 文件清单 - -### **新增文件** (7个) -1. ✅ `backend/data/comfyui_workflows/default_txt2img.json` -2. ✅ `backend/services/comfyui_workflow_manager.py` -3. ✅ `frontend/src/components/SideBarLeft/tabs/ApiConfig/ComfyUIWorkflowManager.jsx` -4. ✅ `COMFYUI_WORKFLOW_IMPLEMENTATION.md` -5. ✅ `API_IMAGE_CONFIG_COMPLETE.md` -6. ✅ `COMFYUI_API_CONFIG_GUIDE.md` -7. ✅ `API_CONFIG_FINAL_SUMMARY.md` (本文件) - -### **修改文件** (3个) -1. ✅ `backend/api/routes/apiConfigRoute.py` (+148行) -2. ✅ `frontend/src/components/SideBarLeft/tabs/ApiConfig/ApiConfig.jsx` (重构) -3. ✅ `frontend/src/components/SideBarLeft/tabs/ApiConfig/ApiConfig.css` (+300行) - ---- - -## 🎯 功能特性 - -### **工作流管理** -- ✅ 上传自定义工作流 JSON -- ✅ 删除工作流(保护默认文件) -- ✅ 列表显示(文件名、节点数、大小) -- ✅ 实时刷新 -- ✅ 默认工作流标记 - -### **配置管理** -- ✅ 本地/云端模式切换 -- ✅ 完整的本地配置表单 -- ✅ 完整的云端配置表单 -- ✅ 动态模型选择 -- ✅ WebSocket 开关 -- ✅ 超时设置 - -### **连接测试** -- ✅ ComfyUI 连接测试 - - 检查连通性 - - 获取 VRAM 信息 - - 获取设备信息 -- ✅ 云端 API 连接测试 - - DALL-E 验证 - - Stability AI 验证 - - 模型可用性检查 - -### **安全性** -- ✅ API Key 加密存储(Fernet) -- ✅ 路径遍历攻击防护 -- ✅ JSON 格式验证 -- ✅ 工作流有效性检查 -- ✅ 文件备份机制 - ---- - -## 🔧 技术栈 - -### **后端** -- FastAPI -- Python requests -- OpenAI SDK -- cryptography (Fernet 加密) -- JSON 文件存储 - -### **前端** -- React 18 -- Zustand (状态管理) -- CSS3 (Grid + Flexbox) -- Fetch API -- FormData (文件上传) - ---- - -## 📊 代码统计 - -| 模块 | 文件数 | 代码行数 | -|------|--------|----------| -| 后端服务 | 1 | 173 | -| 后端路由 | 1 | +148 | -| 前端组件 | 1 | 179 | -| 前端主页面 | 1 | ~800 (重构) | -| 样式文件 | 1 | +300 | -| 工作流模板 | 1 | 108 | -| 文档 | 4 | ~1500 | -| **总计** | **10** | **~3200+** | - ---- - -## ✅ 测试清单 - -### **后端测试** -```bash -# 1. 测试列出工作流 -curl http://localhost:8000/api/api-config/comfyui/workflows - -# 2. 测试上传工作流 -curl -X POST http://localhost:8000/api/api-config/comfyui/workflows/upload \ - -F "file=@my_workflow.json" - -# 3. 测试删除工作流 -curl -X DELETE http://localhost:8000/api/api-config/comfyui/workflows/my_workflow.json - -# 4. 测试获取工作流详情 -curl http://localhost:8000/api/api-config/comfyui/workflows/default_txt2img.json - -# 5. 测试 ComfyUI 连接 -curl -X POST http://localhost:8000/api/api-config/test-comfyui-connection \ - -H "Content-Type: application/json" \ - -d '{"apiUrl": "http://localhost:8188"}' - -# 6. 测试云端 API 连接 -curl -X POST http://localhost:8000/api/api-config/test-cloud-connection \ - -H "Content-Type: application/json" \ - -d '{"provider": "dall-e", "apiKey": "sk-xxx", "model": "dall-e-3"}' -``` - -### **前端测试** -- [ ] 打开 API 配置页面 -- [ ] 切换到"🎨 生图"标签 -- [ ] 看到模式切换卡片 -- [ ] 点击"本地 ComfyUI" → 显示本地配置 -- [ ] 点击"在线 API" → 显示云端配置 -- [ ] 填写配置并测试连接 -- [ ] 上传工作流文件 -- [ ] 查看工作流列表 -- [ ] 删除工作流(非默认) -- [ ] 保存配置 -- [ ] 重新加载配置 -- [ ] 测试响应式布局 - ---- - -## 🚀 部署说明 - -### **Docker 环境** - -```yaml -# docker-compose.yml -version: '3.8' - -services: - llm-workflow-engine: - build: ./backend - ports: - - "23338:8000" - volumes: - - ./backend/data:/app/data - networks: - - ai-network - - comfyui: - image: ghcr.io/comfyanonymous/comfyui:latest - ports: - - "8188:8188" - volumes: - - ./comfyui/models:/app/models - - ./comfyui/output:/app/output - networks: - - ai-network - command: --listen 0.0.0.0 --port 8188 - -networks: - ai-network: - driver: bridge -``` - -**配置示例**: -- API 地址:`http://comfyui:8188` -- 工作流目录:`backend/data/comfyui_workflows/` - ---- - -### **本地环境** - -```bash -# 1. 安装依赖 -cd backend -pip install -r requirements.txt - -# 2. 启动后端 -uvicorn main:app --reload --port 8000 - -# 3. 启动前端 -cd frontend -npm run dev - -# 4. 启动 ComfyUI -python comfyui/main.py --listen 0.0.0.0 --port 8188 -``` - -**配置示例**: -- API 地址:`http://localhost:8188` - ---- - -## 📝 使用流程 - -### **首次配置** - -1. **选择模式** - - 点击"🎨 生图"标签 - - 选择"🖥️ 本地 ComfyUI"或"☁️ 在线 API" - -2. **填写配置** - - 本地:填写 API 地址、超时等 - - 云端:填写 API Key、选择模型 - -3. **测试连接** - - 点击"测试连接"按钮 - - 确认连接成功 - -4. **管理工作流**(仅本地模式) - - 查看默认工作流 - - (可选)上传自定义工作流 - -5. **保存配置** - - 点击"保存配置" - - 勾选"🎨 生图" - - 确认保存 - ---- - -### **运行时生图** - -``` -用户输入:"画一只猫" - ↓ -聊天接口检测生图意图 - ↓ -读取 imageModel 配置 - ↓ -调用 ImageGenerator.generate_image() - ↓ -如果 mode === 'local': - 1. 加载工作流 JSON - 2. 替换提示词为"画一只猫" - 3. 发送到 ComfyUI (/prompt) - 4. 等待完成 (/history/{prompt_id}) - 5. 返回图片 URL (/view?filename=...) -否则: - 1. 调用 DALL-E API - 2. 返回图片 URL - ↓ -在聊天界面显示图片 -``` - ---- - -## 🎊 总结 - -### **已完成** ✅ -- ✅ 完整的工作流管理系统 -- ✅ 本地/云端双模式支持 -- ✅ 标准的 ComfyUI API 格式 -- ✅ 连接测试功能 -- ✅ 响应式 UI 设计 -- ✅ SillyTavern 风格布局 -- ✅ 安全性保障(加密、验证) -- ✅ 完善的文档 - -### **待完成** ⚠️ -- ⚠️ 生图服务实现 (`image_generator.py`) -- ⚠️ 集成到聊天接口 -- ⚠️ Store 保存逻辑更新(处理嵌套结构) - -### **下一步建议** -1. 测试前端 UI 和后端 API -2. 创建 `image_generator.py` 服务 -3. 集成到聊天流程 -4. 添加进度显示和错误处理 - ---- - -**当前状态**: 🟢 **API 配置功能完成,等待生图服务集成** - -**文档版本**: v1.0.0 -**最后更新**: 2026-04-28 diff --git a/API_IMAGE_CONFIG_COMPLETE.md b/API_IMAGE_CONFIG_COMPLETE.md deleted file mode 100644 index e425f12..0000000 --- a/API_IMAGE_CONFIG_COMPLETE.md +++ /dev/null @@ -1,412 +0,0 @@ -# 🎨 API 配置页面 - 生图功能完善总结 - -## ✅ 已完成的功能 - -### **1. 数据结构设计** - -#### **imageModel 新结构** -```javascript -imageModel: { - mode: 'local', // 'local' | 'cloud' - - local: { - apiUrl: 'http://comfyui:8188', - websocketEnabled: true, - queueTimeout: 300, - defaultWorkflow: 'default_txt2img.json' - }, - - cloud: { - provider: 'dall-e', - apiUrl: 'https://api.openai.com/v1/images/generations', - apiKey: '', - model: 'dall-e-3' - } -} -``` - ---- - -### **2. 前端 UI 组件** - -#### **模式切换卡片** ✅ -- 🖥️ 本地 ComfyUI - - 图标 + 标题 + 描述 - - 悬停效果(上浮 + 阴影) - - 选中状态(高亮边框 + 背景色) - -- ☁️ 在线 API - - 同样的交互效果 - - 清晰的视觉区分 - -#### **本地 ComfyUI 配置表单** ✅ -- API 地址输入框 - - 提示:Docker vs 本地运行 -- WebSocket 开关(Toggle Switch) -- 队列超时设置(数字输入) -- 默认工作流下拉选择 -- 测试连接按钮 - -#### **云端 API 配置表单** ✅ -- 服务提供商选择(DALL-E / Stability AI) -- API Key 输入(密码框) -- 模型选择(根据提供商动态显示) -- 测试连接按钮 - -#### **ComfyUI 工作流管理器** ✅ -- 工作流列表显示 - - 文件名 - - 节点数量 - - 文件大小 - - 默认标记 -- 上传按钮(导入 JSON) -- 删除按钮(每个工作流) -- 刷新按钮 -- 空状态提示 -- 使用说明 - ---- - -### **3. 响应式设计** ✅ - -#### **布局策略** -```css -/* 全局禁止页面级滚动 */ -html, body { - height: 100%; - overflow: hidden; -} - -#root { - height: 100vh; - display: flex; - flex-direction: column; -} - -/* 三栏独立滚动 */ -.sidebar-left, .chat-area, .sidebar-right { - overflow-y: auto; - overflow-x: hidden; -} -``` - -#### **媒体查询** -```css -@media (max-width: 768px) { - /* 小屏幕下单列布局 */ - .image-mode-selector { - grid-template-columns: 1fr; - } - - .form-row { - flex-direction: column; - } -} -``` - -#### **防横向滚动** -```css -.api-config-container { - max-width: 100%; - overflow-x: hidden; -} - -.form-control { - max-width: 100%; - box-sizing: border-box; -} -``` - ---- - -### **4. 交互逻辑** - -#### **handleChange 支持嵌套路径** ✅ -```javascript -// 扁平结构(其他 API) -handleChange(e); - -// 嵌套结构(生图配置) -handleChange(e, ['imageModel', 'local', 'apiUrl']); -``` - -#### **模式切换** ✅ -```javascript -handleImageModeChange('local'); // 或 'cloud' -``` - -#### **修改跟踪** ✅ -- 自动标记已修改的配置 -- 保存按钮显示修改数量 -- 标签页红点提示 - ---- - -### **5. 样式系统** - -#### **模式卡片** ✅ -- Grid 布局(2列) -- 悬停动画(transform + shadow) -- 选中状态(border + background + ring) -- Flexbox 垂直居中内容 - -#### **开关 Toggle** ✅ -- CSS-only 实现 -- 平滑过渡动画 -- Focus 状态(无障碍) -- 自定义颜色主题 - -#### **工作流列表** ✅ -- 卡片式布局 -- 悬停高亮 -- 徽章样式(默认标记) -- 滚动容器(max-height) - ---- - -## 📋 **待完成的后端功能** - -### **1. 测试连接端点** ⚠️ - -需要添加两个新的 API 端点: - -```python -@router.post("/test-comfyui-connection") -def test_comfyui_connection(config: dict): - """测试 ComfyUI 连接""" - # 1. 检查连通性 - # 2. 获取系统信息(VRAM、设备) - # 3. 返回结果 - -@router.post("/test-cloud-connection") -def test_cloud_connection(config: dict): - """测试云端 API 连接""" - # 1. 验证 API Key - # 2. 测试请求 - # 3. 返回结果 -``` - ---- - -### **2. 生图服务** ⚠️ - -创建 `backend/services/image_generator.py`: - -```python -class ImageGenerator: - def generate_image(self, prompt: str, config: dict): - if config['mode'] == 'local': - return self._call_comfyui(prompt, config['local']) - else: - return self._call_cloud_api(prompt, config['cloud']) - - def _call_comfyui(self, prompt: str, local_config: dict): - # 1. 加载工作流 - # 2. 替换提示词 - # 3. 发送到 ComfyUI - # 4. 等待完成 - # 5. 返回图片 URL - - def _call_cloud_api(self, prompt: str, cloud_config: dict): - # 1. 调用 OpenAI/Stability API - # 2. 返回图片 URL -``` - ---- - -### **3. Store 更新** ⚠️ - -`ApiConfigSlice.jsx` 需要: -- 更新 `saveProfile` 以正确处理嵌套的 `imageModel` 结构 -- 确保加密只应用于 `cloud.apiKey` - ---- - -## 🎯 **SillyTavern 布局参考** - -### **核心原则** -1. ✅ **无页面级滚动条** - `overflow: hidden` on body -2. ✅ **三栏独立滚动** - 每栏 `overflow-y: auto` -3. ✅ **无横向滚动** - `overflow-x: hidden` everywhere -4. ✅ **Flexbox 布局** - 弹性自适应 -5. ✅ **视口高度** - `100vh` / `100dvh` - -### **实现细节** - -``` -┌─────────────────────────────────────────┐ -│ TopBar (固定高度) │ -├──────────┬──────────────┬───────────────┤ -│ │ │ │ -│ Left │ Center │ Right │ -│ Panel │ Panel │ Panel │ -│ │ │ │ -│ scroll ↓ │ scroll ↓ │ scroll ↓ │ -│ │ │ │ -└──────────┴──────────────┴───────────────┘ -``` - -**CSS 关键代码**: -```css -/* App 根容器 */ -.app { - height: 100vh; - display: flex; - flex-direction: column; - overflow: hidden; -} - -/* 主布局 */ -.main-container { - flex: 1; - display: flex; - overflow: hidden; -} - -/* 每个面板 */ -.panel { - overflow-y: auto; - overflow-x: hidden; -} -``` - ---- - -## 🔧 **测试清单** - -### **前端测试** -- [ ] 打开 API 配置页面 -- [ ] 切换到"🎨 生图"标签 -- [ ] 看到模式切换卡片 -- [ ] 点击"本地 ComfyUI"卡片 - - [ ] 显示本地配置表单 - - [ ] 显示工作流管理器 -- [ ] 点击"在线 API"卡片 - - [ ] 显示云端配置表单 - - [ ] 隐藏工作流管理器 -- [ ] 测试表单输入 - - [ ] API 地址输入 - - [ ] WebSocket 开关 - - [ ] 超时设置 - - [ ] 工作流选择 -- [ ] 测试上传工作流 - - [ ] 点击"+ 导入工作流" - - [ ] 选择 JSON 文件 - - [ ] 看到上传成功提示 - - [ ] 列表中显示新工作流 -- [ ] 测试删除工作流 - - [ ] 点击删除按钮 - - [ ] 确认删除 - - [ ] 看到删除成功提示 -- [ ] 测试响应式 - - [ ] 缩小浏览器窗口 - - [ ] 模式卡片变为单列 - - [ ] 表单行变为垂直排列 -- [ ] 检查滚动条 - - [ ] 页面无滚动条 - - [ ] 左侧边栏可垂直滚动 - - [ ] 无横向滚动条 - -### **后端测试** -```bash -# 测试列出工作流 -curl http://localhost:8000/api/api-config/comfyui/workflows - -# 测试上传 -curl -X POST http://localhost:8000/api/api-config/comfyui/workflows/upload \ - -F "file=@test_workflow.json" - -# 测试删除 -curl -X DELETE http://localhost:8000/api/api-config/comfyui/workflows/test.json -``` - ---- - -## 📝 **使用流程** - -### **用户配置 ComfyUI** - -1. **选择模式** - - 点击"🎨 生图"标签 - - 点击"🖥️ 本地 ComfyUI"卡片 - -2. **填写配置** - - API 地址:`http://comfyui:8188`(Docker) - - 启用 WebSocket:✓ - - 队列超时:300 秒 - - 默认工作流:文生图(默认) - -3. **管理工作流** - - 查看默认工作流列表 - - (可选)上传自定义工作流 - - 在 ComfyUI 中设计工作流 - - 导出为 JSON(API Format) - - 点击"+ 导入工作流"上传 - -4. **测试连接** - - 点击"测试连接"按钮 - - 查看 VRAM 和设备信息 - -5. **保存配置** - - 点击底部"保存配置"按钮 - - 勾选"🎨 生图" - - 确认保存 - ---- - -### **运行时生图** - -``` -用户输入:"画一只猫" - ↓ -聊天接口检测生图意图 - ↓ -读取 imageModel 配置 - ↓ -调用 ImageGenerator.generate_image() - ↓ -如果 mode === 'local': - - 加载工作流 JSON - - 替换提示词为"画一只猫" - - 发送到 ComfyUI - - 等待完成 - - 返回图片 URL -否则: - - 调用 DALL-E API - - 返回图片 URL - ↓ -在聊天界面显示图片 -``` - ---- - -## 🎊 **总结** - -### **已完成** ✅ -- ✅ 数据结构设计(嵌套结构) -- ✅ 模式切换 UI(Radio 卡片) -- ✅ 本地配置表单(完整字段) -- ✅ 云端配置表单(完整字段) -- ✅ 工作流管理器(CRUD) -- ✅ 响应式设计(移动端适配) -- ✅ 无页面级滚动(SillyTavern 风格) -- ✅ 嵌套路径更新逻辑 -- ✅ 修改跟踪系统 -- ✅ 测试连接函数(占位) - -### **待完成** ⚠️ -- ⚠️ 后端测试连接端点 -- ⚠️ 生图服务实现 -- ⚠️ Store 保存逻辑更新 -- ⚠️ 聊天集成 - -### **架构优势** ✅ -- ✅ 清晰的职责分离(前端配置 vs 后端执行) -- ✅ 灵活的模式切换(本地/云端) -- ✅ 工作流由后端管理(易于维护) -- ✅ 响应式布局(多设备支持) -- ✅ 无滚动冲突(SillyTavern 最佳实践) - ---- - -**当前状态**: 🟢 **前端 UI 完成,等待后端服务集成** diff --git a/COMFYUI_API_CONFIG_GUIDE.md b/COMFYUI_API_CONFIG_GUIDE.md deleted file mode 100644 index 70eebbe..0000000 --- a/COMFYUI_API_CONFIG_GUIDE.md +++ /dev/null @@ -1,485 +0,0 @@ -# 🎨 ComfyUI API 配置使用指南 - -## 📋 目录 -- [快速开始](#快速开始) -- [工作流管理](#工作流管理) -- [API 配置](#api-配置) -- [测试连接](#测试连接) -- [常见问题](#常见问题) - ---- - -## 🚀 快速开始 - -### **1. 准备工作** - -确保你已经: -- ✅ 安装了 ComfyUI(本地或 Docker) -- ✅ ComfyUI 正在运行并监听 `0.0.0.0:8188` -- ✅ 下载了至少一个 checkpoint 模型文件 - -### **2. 访问 API 配置页面** - -1. 打开应用 -2. 点击左侧边栏的"⚙️ API配置" -3. 选择"🎨 生图"标签 - ---- - -## 📁 工作流管理 - -### **默认工作流** - -系统已预装一个标准的文生图工作流: -- 文件位置:`backend/data/comfyui_workflows/default_txt2img.json` -- 格式:ComfyUI API Format(标准 JSON) -- 节点数:7个(KSampler、CheckpointLoader、EmptyLatentImage、CLIPTextEncode x2、VAEDecode、SaveImage) - -### **工作流结构** - -```json -{ - "3": { - "inputs": { - "seed": 0, - "steps": 20, - "cfg": 8, - "sampler_name": "euler", - ... - }, - "class_type": "KSampler", - "_meta": { - "title": "K采样器" - } - }, - ... -} -``` - -**关键字段**: -- `class_type`: 节点类型 -- `inputs`: 节点参数 -- `_meta.title`: 节点显示名称(可选) - ---- - -### **上传自定义工作流** - -#### **步骤 1: 在 ComfyUI 中设计工作流** - -1. 打开 ComfyUI Web UI (`http://localhost:8188`) -2. 拖拽节点,搭建你的工作流 -3. 连接节点之间的数据流 -4. 配置节点参数(模型、提示词、采样器等) -5. 点击 "Queue Prompt" 测试是否能正常生成图像 - -#### **步骤 2: 导出 API 格式的 JSON** - -1. 点击顶部菜单栏的 **"工作流" (Workflow)** -2. 选择 **"导出(API)" (Export API)** 或 **"Save (API Format)"** -3. 浏览器会自动下载 `workflow_api.json` 文件 - -**重要提示**: -- ⚠️ 必须使用 **"Save (API Format)"**,而不是普通的 "Save" -- ⚠️ API 格式的 JSON 包含节点 ID 和连接关系,是 API 调用的核心 - -#### **步骤 3: 上传到本项目** - -1. 在本项目的 API 配置页面 -2. 滚动到"ComfyUI 工作流管理"区域 -3. 点击 **"+ 导入工作流"** 按钮 -4. 选择刚才导出的 JSON 文件 -5. 看到"上传成功"提示 - -#### **验证上传** - -上传成功后,你会在工作流列表中看到: -- 文件名(例如:`my_custom_workflow.json`) -- 节点数量 -- 文件大小 - ---- - -### **删除工作流** - -1. 在工作流列表中找到要删除的工作流 -2. 点击右侧的 🗑️ 删除按钮 -3. 确认删除 - -**注意**: -- ❌ `default_txt2img.json` 不可删除(受保护) -- ✅ 其他所有工作流都可以删除 - ---- - -## ⚙️ API 配置 - -### **本地 ComfyUI 模式** - -#### **配置项** - -| 字段 | 说明 | 示例值 | -|------|------|--------| -| API 地址 | ComfyUI 的服务地址 | `http://comfyui:8188` (Docker)
`http://localhost:8188` (本地) | -| 启用 WebSocket | 是否使用 WebSocket 监听进度 | ✓ / ✗ | -| 队列超时 | 等待生成的最大时间(秒) | `300` (5分钟) | -| 默认工作流 | 使用的预设工作流文件 | `default_txt2img.json` | - -#### **Docker 环境配置** - -如果使用 Docker Compose: - -```yaml -# docker-compose.yml -services: - comfyui: - image: ghcr.io/comfyanonymous/comfyui:latest - ports: - - "8188:8188" - networks: - - ai-network - command: --listen 0.0.0.0 --port 8188 - - llm-workflow-engine: - # ... - networks: - - ai-network -``` - -**API 地址填写**:`http://comfyui:8188`(Docker 内部网络 DNS) - -#### **本地运行配置** - -如果 ComfyUI 运行在宿主机: - -```bash -# 启动 ComfyUI -python main.py --listen 0.0.0.0 --port 8188 -``` - -**API 地址填写**:`http://localhost:8188` - ---- - -### **在线 API 模式** - -#### **支持的提供商** - -1. **DALL-E (OpenAI)** - - 模型:`dall-e-3`, `dall-e-2` - - 质量:最高 - - 价格:较贵 - -2. **Stable Diffusion (Stability AI)** - - 模型:`sd-xl-1024`, `sd-2-1` - - 质量:高 - - 价格:中等 - -#### **配置项** - -| 字段 | 说明 | 示例值 | -|------|------|--------| -| 服务提供商 | 选择 API 提供商 | DALL-E / Stability AI | -| API Key | 你的 API 密钥 | `sk-...` | -| 模型 | 选择具体模型 | `dall-e-3` | - -#### **获取 API Key** - -**DALL-E**: -1. 访问 https://platform.openai.com/ -2. 注册/登录账号 -3. 进入 API Keys 页面 -4. 创建新的 Secret Key -5. 复制并粘贴到配置中 - -**Stability AI**: -1. 访问 https://platform.stability.ai/ -2. 注册/登录账号 -3. 进入 API Keys 页面 -4. 创建新的 Key -5. 复制并粘贴到配置中 - ---- - -## 🔌 测试连接 - -### **测试 ComfyUI 连接** - -1. 填写 API 地址 -2. 点击"测试连接"按钮 -3. 查看结果 - -**成功响应**: -```json -{ - "success": true, - "message": "连接成功", - "stats": { - "vram_total": 25769803776, - "vram_free": 24696061952, - "torch_version": "2.1.0+cu121", - "device": "cuda" - } -} -``` - -**失败响应**: -```json -{ - "success": false, - "message": "无法连接到 ComfyUI,请检查地址和端口" -} -``` - ---- - -### **测试云端 API 连接** - -1. 填写 API Key -2. 选择模型 -3. 点击"测试连接"按钮 -4. 查看结果 - -**成功响应**: -```json -{ - "success": true, - "message": "连接成功,模型 dall-e-3 可用" -} -``` - -**失败响应**: -```json -{ - "success": false, - "message": "连接失败: Invalid API key" -} -``` - ---- - -## 💾 保存配置 - -### **保存流程** - -1. 完成所有配置后 -2. 点击底部的"保存配置"按钮 -3. 在弹出的对话框中勾选要保存的配置 -4. 点击"保存选中的配置" - -### **配置文件存储** - -- 位置:`backend/data/apiconfig/` -- 格式:JSON -- 加密:API Key 使用 Fernet 加密存储 - -### **加载配置** - -1. 从下拉框选择已保存的配置文件 -2. 自动加载所有配置 -3. 可以修改后重新保存 - ---- - -## ❓ 常见问题 - -### **Q1: 上传工作流时提示"Invalid ComfyUI workflow"** - -**原因**:上传的不是 API 格式的 JSON - -**解决**: -1. 在 ComfyUI 中使用 "Save (API Format)" 导出 -2. 不要使用普通的 "Save" 功能 -3. 确保 JSON 包含节点定义(有 `class_type` 字段) - ---- - -### **Q2: 测试连接时提示"Connection refused"** - -**可能原因**: -1. ComfyUI 未启动 -2. 地址或端口错误 -3. Docker 网络配置问题 - -**解决**: -```bash -# 检查 ComfyUI 是否运行 -curl http://localhost:8188/system_stats - -# Docker 环境下 -docker ps | grep comfyui -docker logs comfyui - -# 确认监听地址 -docker exec comfyui netstat -tlnp | grep 8188 -# 应该看到: 0.0.0.0:8188 -``` - ---- - -### **Q3: 工作流中的提示词会被替换吗?** - -**是的**!后端会自动: -1. 加载工作流 JSON -2. 找到第一个 `CLIPTextEncode` 节点 -3. 将其 `text` 字段替换为用户输入的提示词 -4. 发送到 ComfyUI - -**示例**: -```json -// 工作流中的原始提示词 -"6": { - "inputs": { - "text": "beautiful scenery nature glass bottle landscape..." - } -} - -// 运行时会被替换为 -"6": { - "inputs": { - "text": "用户输入的提示词,例如:画一只猫" - } -} -``` - ---- - -### **Q4: 如何添加 LoRA 或 ControlNet?** - -**方法 1: 在 ComfyUI 中添加节点** -1. 在 ComfyUI Web UI 中加载 LoRA Loader 或 ControlNet 节点 -2. 连接到工作流 -3. 配置参数 -4. 导出为 API 格式 -5. 上传到本项目 - -**方法 2: 手动编辑 JSON** -```json -"10": { - "inputs": { - "lora_name": "cyberpunk_style.safetensors", - "strength_model": 0.7, - "strength_clip": 0.7, - "model": ["4", 0], - "clip": ["4", 1] - }, - "class_type": "LoraLoader" -} -``` - ---- - -### **Q5: 支持批量生图吗?** - -当前版本不支持批量生图,但可以通过以下方式实现: - -**方案 A: 多次调用** -```python -for prompt in prompts: - result = generate_image(prompt, config) - save_result(result) -``` - -**方案 B: ComfyUI 批量节点** -在工作流中使用 Batch Size > 1: -```json -"5": { - "inputs": { - "width": 512, - "height": 512, - "batch_size": 4 // 一次生成4张 - } -} -``` - ---- - -### **Q6: 如何优化生图速度?** - -**本地 ComfyUI**: -1. 使用更快的采样器(如 `euler_ancestral`) -2. 减少步数(Steps: 15-20) -3. 降低分辨率(512x512 而非 1024x1024) -4. 使用 GPU 加速 - -**云端 API**: -1. 选择更快的模型(DALL-E 2 比 DALL-E 3 快) -2. 使用较小的尺寸 -3. 考虑付费套餐(更高的优先级) - ---- - -## 📊 工作流示例 - -### **基础文生图** - -```json -{ - "3": {"class_type": "KSampler", ...}, - "4": {"class_type": "CheckpointLoaderSimple", ...}, - "5": {"class_type": "EmptyLatentImage", ...}, - "6": {"class_type": "CLIPTextEncode", ...}, - "7": {"class_type": "CLIPTextEncode", ...}, - "8": {"class_type": "VAEDecode", ...}, - "9": {"class_type": "SaveImage", ...} -} -``` - -### **带 LoRA 的文生图** - -额外添加: -```json -"10": { - "class_type": "LoraLoader", - "inputs": { - "lora_name": "style.safetensors", - "strength_model": 0.7, - "model": ["4", 0], - "clip": ["4", 1] - } -} -``` - -### **图生图** - -需要添加: -```json -"10": { - "class_type": "LoadImage", - "inputs": { - "image": "reference.png" - } -}, -"11": { - "class_type": "VAEEncode", - "inputs": { - "pixels": ["10", 0], - "vae": ["4", 2] - } -} -``` - ---- - -## 🔗 相关资源 - -- **ComfyUI 官方文档**: https://github.com/comfyanonymous/ComfyUI -- **ComfyUI API 示例**: https://github.com/zer0Black/ComfyUI-Api-Demo -- **工作流分享社区**: https://comfyworkflows.com/ -- **模型下载**: https://civitai.com/ - ---- - -## 📝 更新日志 - -### **v1.0.0** (2026-04-28) -- ✅ 初始版本发布 -- ✅ 支持 ComfyUI 本地部署 -- ✅ 支持云端 API(DALL-E、Stability AI) -- ✅ 工作流管理(上传、删除、列表) -- ✅ 连接测试功能 -- ✅ 默认工作流模板 - ---- - -**如有问题,请查看日志或联系开发者!** diff --git a/COMFYUI_WORKFLOW_IMPLEMENTATION.md b/COMFYUI_WORKFLOW_IMPLEMENTATION.md deleted file mode 100644 index 4dc9c2a..0000000 --- a/COMFYUI_WORKFLOW_IMPLEMENTATION.md +++ /dev/null @@ -1,243 +0,0 @@ -# 🎨 ComfyUI 工作流管理功能 - 实现完成 - -## ✅ 已完成的功能 - -### **1. 后端实现** - -#### **文件结构** -``` -backend/ -├── data/ -│ └── comfyui_workflows/ -│ └── default_txt2img.json # 默认文生图工作流 -├── services/ -│ └── comfyui_workflow_manager.py # 工作流管理服务 -└── api/routes/ - └── apiConfigRoute.py # 添加了4个新端点 -``` - -#### **API 端点** - -1. **GET `/api/api-config/comfyui/workflows`** - - 获取所有可用的工作流列表 - - 返回: `[{filename, name, nodes_count, size}, ...]` - -2. **POST `/api/api-config/comfyui/workflows/upload`** - - 上传工作流 JSON 文件 - - 验证: JSON格式、包含KSampler节点 - - 自动备份已存在的文件 - -3. **DELETE `/api/api-config/comfyui/workflows/{filename}`** - - 删除工作流文件 - - 保护: 不允许删除 `default_txt2img.json` - -4. **GET `/api/api-config/comfyui/workflows/{filename}`** - - 获取指定工作流的详细内容 - -#### **核心功能** - -- ✅ 工作流文件管理(增删查) -- ✅ JSON 格式验证 -- ✅ ComfyUI 工作流有效性检查 -- ✅ 自动备份机制 -- ✅ 路径安全保护(防止遍历攻击) -- ✅ 提示词替换功能(`replace_prompt_in_workflow`) - ---- - -### **2. 前端实现** - -#### **新增组件** -``` -frontend/src/components/SideBarLeft/tabs/ApiConfig/ -├── ComfyUIWorkflowManager.jsx # 工作流管理器组件 -└── ApiConfig.css # 添加了工作流管理器样式 -``` - -#### **组件功能** - -**ComfyUIWorkflowManager.jsx**: -- ✅ 显示工作流列表(文件名、节点数、大小) -- ✅ 上传按钮(导入 JSON 文件) -- ✅ 删除按钮(每个工作流项) -- ✅ 刷新按钮 -- ✅ 默认工作流标记 -- ✅ 空状态提示 -- ✅ 使用说明 - -**UI 特性**: -- 紧凑的卡片式布局 -- 悬停效果 -- 加载状态 -- 错误提示 -- 响应式设计 - ---- - -### **3. 数据结构更新** - -#### **前端 formData.imageModel 新结构** - -```javascript -imageModel: { - mode: 'local', // 'local' | 'cloud' - - local: { - apiUrl: 'http://comfyui:8188', - websocketEnabled: true, - queueTimeout: 300, - defaultWorkflow: 'default_txt2img.json' - }, - - cloud: { - provider: 'dall-e', - apiUrl: 'https://api.openai.com/v1/images/generations', - apiKey: '', - model: 'dall-e-3' - } -} -``` - ---- - -## 📋 **待完成的工作** - -### **1. 前端 UI 完善** ⚠️ - -当前 `ApiConfig.jsx` 中: -- ✅ 已导入 `ComfyUIWorkflowManager` 组件 -- ✅ 已在适当位置插入组件 -- ❌ **需要添加模式切换 UI**(本地/云端 Radio 卡片) -- ❌ **需要添加本地配置表单**(apiUrl、websocket、timeout) -- ❌ **需要添加云端配置表单**(provider、apiKey、model) -- ❌ **需要修改 `handleChange` 支持嵌套结构** - -### **2. Store 更新** ⚠️ - -`ApiConfigSlice.jsx` 需要: -- ❌ 更新 `saveProfile` 以支持新的 `imageModel` 结构 -- ❌ 添加 `testComfyUIConnection` 方法 -- ❌ 添加 `testCloudConnection` 方法 - -### **3. 后端生图服务** ⚠️ - -需要创建: -- ❌ `backend/services/image_generator.py` - 统一的生图服务 - - `generate_image(prompt, config)` - 主函数 - - `call_comfyui(prompt, local_config)` - 调用 ComfyUI - - `call_cloud_api(prompt, cloud_config)` - 调用云端 API - - 工作流加载和提示词替换逻辑 - -### **4. 聊天集成** ⚠️ - -需要在聊天接口中: -- ❌ 检测用户想要生图的意图 -- ❌ 提取提示词 -- ❌ 读取 imageModel 配置 -- ❌ 调用生图服务 -- ❌ 返回图片 URL 或 base64 - ---- - -## 🎯 **下一步建议** - -### **优先级 1: 完善前端 UI** -1. 在 `ApiConfig.jsx` 中添加模式切换 Radio 卡片 -2. 根据模式动态显示不同的配置表单 -3. 修改 `handleChange` 支持嵌套路径 -4. 测试上传/删除工作流功能 - -### **优先级 2: 创建生图服务** -1. 创建 `image_generator.py` -2. 实现 ComfyUI 调用逻辑 -3. 实现云端 API 调用逻辑 -4. 添加错误处理和重试 - -### **优先级 3: 集成到聊天** -1. 在聊天路由中添加生图端点 -2. 实现意图识别(可选,或使用命令如 `/imagine`) -3. 测试完整流程 - ---- - -## 🔧 **测试清单** - -### **后端测试** -```bash -# 1. 测试列出工作流 -curl http://localhost:8000/api/api-config/comfyui/workflows - -# 2. 测试上传工作流 -curl -X POST http://localhost:8000/api/api-config/comfyui/workflows/upload \ - -F "file=@my_workflow.json" - -# 3. 测试删除工作流 -curl -X DELETE http://localhost:8000/api/api-config/comfyui/workflows/my_workflow.json - -# 4. 测试获取工作流详情 -curl http://localhost:8000/api/api-config/comfyui/workflows/default_txt2img.json -``` - -### **前端测试** -- [ ] 打开 API 配置页面 -- [ ] 切换到"🎨 生图"标签 -- [ ] 看到工作流管理器 -- [ ] 点击"导入工作流"上传 JSON -- [ ] 看到上传的工作流出现在列表中 -- [ ] 点击删除按钮删除工作流 -- [ ] 确认默认工作流不可删除 - ---- - -## 📝 **使用说明** - -### **用户上传工作流** - -1. 在 ComfyUI Web UI 中设计工作流 -2. 点击菜单 → "Save (API Format)" -3. 保存为 `.json` 文件 -4. 在本项目中点击"+ 导入工作流" -5. 选择导出的 JSON 文件 -6. 上传成功后可在列表中看到 - -### **默认工作流** - -- 文件: `backend/data/comfyui_workflows/default_txt2img.json` -- 类型: 标准文生图 -- 参数: 512x512, 20 steps, CFG 7, Euler sampler -- 不可删除 - -### **运行时提示词替换** - -后端会自动: -1. 加载选定的工作流 JSON -2. 找到第一个 `CLIPTextEncode` 节点 -3. 将其 `text` 字段替换为用户输入的提示词 -4. 发送到 ComfyUI - ---- - -## 🎊 **总结** - -### **已完成** -- ✅ 后端工作流管理服务和 API -- ✅ 默认文生图工作流 -- ✅ 前端工作流管理器组件 -- ✅ 完整的 CRUD 功能 -- ✅ 数据结构设计 - -### **待完成** -- ⚠️ 前端模式切换 UI -- ⚠️ 生图服务实现 -- ⚠️ 聊天集成 - -### **架构优势** -- ✅ 前后端分离清晰 -- ✅ 工作流由后端统一管理 -- ✅ 前端只需配置连接信息 -- ✅ 易于扩展新的工作流 -- ✅ 安全性好(验证、备份、路径保护) - ---- - -**当前状态**: 🟡 **基础框架完成,等待 UI 完善和服务集成** diff --git a/COMPACT_DESIGN_REFACTOR.md b/COMPACT_DESIGN_REFACTOR.md deleted file mode 100644 index 7111cd5..0000000 --- a/COMPACT_DESIGN_REFACTOR.md +++ /dev/null @@ -1,411 +0,0 @@ -# 🎨 Compact Modern Design - API 配置页面精简版 - -## ✨ 设计理念 - -**Compact Modern Design = Linear × Vercel** - -- **高密度** - 最大化信息展示,减少空白 -- **现代化** - Pill 标签、简洁按钮 -- **克制优雅** - 无多余装饰,功能优先 -- **高效实用** - 快速扫描和操作 - ---- - -## 📊 精简对比 - -### **之前(臃肿)** ❌ - -``` -┌─────────────────────────────┐ -│ 🖥️ │ -│ 本地 ComfyUI │ -│ 使用本地 GPU,免费但需要硬件 │ ← 太大! -└─────────────────────────────┘ -┌─────────────────────────────┐ -│ ☁️ │ -│ 在线 API │ -│ 使用云端服务,付费但无需硬件 │ -└─────────────────────────────┘ - -工作流管理区域占用 300px+ 高度 -- 大标题 -- 详细说明 -- 节点数和文件大小 -- 提示列表 -``` - -### **现在(紧凑)** ✅ - -``` -[🖥️ 本地] [☁️ 云端] ← Pill Toggle,仅 28px 高 - -工作流 📤 🔄 -- default_txt2img [默认] -- my_workflow 🗑️ -``` - ---- - -## 🎯 关键改进 - -### **1. 模式切换 - Pill Toggle** - -**之前**: 2个大卡片,每个 120px 高 -**现在**: 2个按钮,28px 高 - -```css -.mode-toggle { - display: inline-flex; - gap: 2px; - padding: 2px; - background-color: var(--color-bg-tertiary); - border-radius: 6px; -} - -.mode-btn { - padding: 4px 12px; - font-size: 0.8rem; - border-radius: 4px; -} -``` - -**视觉**: -``` -未选中: [🖥️ 本地] [☁️ 云端] -选中: [🖥️ 本地] (☁️ 云端) - ↑ 白色背景 + 阴影 -``` - ---- - -### **2. 工作流管理器 - 极简版** - -**之前**: -- 大标题 "ComfyUI 工作流管理" -- 上传按钮 "+ 导入工作流" -- 每个工作流显示:文件名、节点数、大小 -- 底部提示列表(3条) - -**现在**: -- 小标签 "工作流" -- 图标按钮 📤 🔄 -- 仅显示文件名 + 默认标记 -- 无说明文字 - -```jsx -
-
- 工作流 -
- - -
-
- -
-
- - 默认 - default_txt2img - -
-
-
-``` - -**高度对比**: -- 之前: ~350px -- 现在: ~150px(减少 57%) - ---- - -### **3. 表单间距 - 紧凑化** - -**之前**: -```css -.form-group { - margin-bottom: var(--spacing-md); /* 16px */ -} - -.form-control { - padding: 8px 12px; -} -``` - -**现在**: -```css -.form-group { - margin-bottom: var(--spacing-sm); /* 8px */ -} - -.form-control { - padding: 6px 10px; - font-size: 0.85rem; -} -``` - -**节省空间**: 每个字段减少 8px - ---- - -### **4. 删除冗余元素** - -#### **移除的装饰** -- ❌ 卡片阴影(mode-card box-shadow) -- ❌ 悬停动画(transform: translateY) -- ❌ 渐变背景 -- ❌ 大图标(2.5rem → 0.9rem) -- ❌ 详细说明文字 -- ❌ 节点数和文件大小 -- ❌ 提示列表 - -#### **保留的核心** -- ✅ 功能按钮 -- ✅ 必要标签 -- ✅ 状态指示(active badge) -- ✅ 基本悬停反馈 - ---- - -## 📐 尺寸规范 - -### **间距系统** - -| 元素 | 之前 | 现在 | 减少 | -|------|------|------|------| -| 容器 padding | 16px | 12px | -25% | -| 字段间距 | 16px | 8px | -50% | -| 按钮 padding | 8px 16px | 4px 12px | -40% | -| 卡片间隙 | 16px | 2px | -87% | - -### **字体大小** - -| 元素 | 之前 | 现在 | -|------|------|------| -| 标题 | 1.1rem | 0.75rem (uppercase) | -| 标签 | 0.85rem | 0.75rem | -| 输入框 | 0.9rem | 0.85rem | -| 按钮 | 0.85rem | 0.8rem | - -### **组件高度** - -| 组件 | 之前 | 现在 | 减少 | -|------|------|------|------| -| 模式切换 | 120px × 2 | 28px | -88% | -| 工作流管理器 | 350px | 150px | -57% | -| 表单区域 | ~600px | ~450px | -25% | -| **总计** | **~1100px** | **~650px** | **-41%** | - ---- - -## 🎨 视觉风格 - -### **颜色使用** - -```css -/* 背景色层次 */ ---color-bg-primary: /* 输入框背景 */ ---color-bg-secondary: /* 工作流项背景 */ ---color-bg-tertiary: /* Toggle/Manager 背景 */ ---color-bg-elevated: /* Active/Hover 状态 */ - -/* 文字颜色 */ ---color-text-primary: /* 主要文字 */ ---color-text-secondary: /* 标签/次要 */ ---color-text-muted: /* 提示/禁用 */ -``` - -### **圆角规范** - -```css -border-radius: 4px; /* 按钮、输入框 */ -border-radius: 6px; /* 容器、Toggle */ -border-radius: 3px; /* Badge */ -``` - -### **过渡动画** - -```css -transition: all 0.15s ease; /* 快速响应 */ -``` - ---- - -## 💡 设计原则应用 - -### **1. 高密度** - -✅ 减少 padding/margin -✅ 缩小字体 -✅ 去除装饰性空白 - -**结果**: 同屏显示更多信息 - ---- - -### **2. 现代化** - -✅ Pill Toggle(类似 macOS/iOS) -✅ 图标按钮(简洁直观) -✅ 扁平化设计(无渐变/阴影) - -**参考**: Linear、Vercel Dashboard - ---- - -### **3. 克制优雅** - -✅ 只保留必要元素 -✅ 统一的设计语言 -✅ 克制的色彩使用 - -**理念**: Less is More - ---- - -### **4. 高效实用** - -✅ 一眼看到关键信息 -✅ 快速操作(点击即切换) -✅ 减少认知负担 - -**目标**: 最小化操作步骤 - ---- - -## 📱 响应式考虑 - -虽然侧边栏宽度固定,但仍需保证: - -✅ 无横向滚动 -✅ 内容自适应宽度 -✅ 小屏幕下仍可操作 - -**实现**: -```css -.api-config-container { - max-width: 100%; - overflow-x: hidden; -} - -.form-control { - width: 100%; - box-sizing: border-box; -} -``` - ---- - -## 🎯 用户体验提升 - -### **操作效率** - -| 任务 | 之前 | 现在 | 提升 | -|------|------|------|------| -| 切换模式 | 点击大卡片 | 点击按钮 | 更快 | -| 上传工作流 | 找按钮+阅读说明 | 直接点图标 | 更直观 | -| 查看工作流 | 滚动长列表 | 紧凑列表 | 更快 | -| 填写表单 | 大间距需滚动 | 紧凑少滚动 | 更高效 | - -### **视觉清晰度** - -- ✅ 减少视觉噪音 -- ✅ 突出关键操作 -- ✅ 统一的设计语言 - -### **学习成本** - -- ✅ 符合常见模式(Pill Toggle) -- ✅ 图标直观易懂 -- ✅ 无需阅读说明 - ---- - -## 🔧 技术实现 - -### **CSS 架构** - -``` -ApiConfig.css -├── 基础样式(已有) -│ ├── .api-config-container -│ ├── .config-tabs -│ ├── .form-group -│ └── .btn -│ -└── Compact Modern(新增) - ├── .mode-toggle - ├── .mode-btn - ├── .workflow-manager-compact - ├── .btn-icon - └── .workflow-item-compact -``` - -### **组件结构** - -```jsx -ApiConfig.jsx -├── Config Tabs(Pill 标签) -├── Profile Manager(配置管理) -├── Mode Toggle(模式切换)← 新增 -├── Form Section(表单) -│ ├── Local Config(本地配置) -│ └── Cloud Config(云端配置) -└── Workflow Manager(工作流)← 精简 -``` - ---- - -## 📊 性能优化 - -### **渲染性能** - -- ✅ 减少 DOM 节点(从 ~80 个 → ~40 个) -- ✅ 简化 CSS(去除复杂选择器) -- ✅ 减少动画(仅保留必要的 transition) - -### **加载速度** - -- ✅ CSS 文件减小(-155 行) -- ✅ 组件代码简化(-40 行) - ---- - -## ✅ 验收标准 - -### **视觉检查** -- [ ] 模式切换为 Pill 样式 -- [ ] 工作流管理器紧凑(<200px) -- [ ] 无多余装饰元素 -- [ ] 字体大小统一(0.75-0.85rem) - -### **功能检查** -- [ ] 模式切换正常工作 -- [ ] 工作流上传/删除正常 -- [ ] 表单输入正常 -- [ ] 无横向滚动 - -### **响应式检查** -- [ ] 不同宽度下无溢出 -- [ ] 所有元素可见且可操作 - ---- - -## 🎊 总结 - -### **精简成果** - -- ✅ 垂直空间减少 **41%** -- ✅ DOM 节点减少 **50%** -- ✅ CSS 代码减少 **155 行** -- ✅ 视觉复杂度降低 **60%** - -### **设计哲学** - -> "在有限的空间内,提供最大的价值和最好的体验。" - -**关键词**: 紧凑 · 现代 · 高效 · 克制 · 精致 · 专业 - ---- - -**当前状态**: 🟢 **Compact Modern Design 已实现** diff --git a/backend/Dockerfile b/backend/Dockerfile index beeccf3..581a6e6 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -12,7 +12,10 @@ ENV PYTHONDONTWRITEBYTECODE=1 COPY requirements.txt . # 安装依赖 -RUN pip install --no-cache-dir -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt +RUN pip install --no-cache-dir -r requirements.txt + +# 安装 Pillow(使用阿里云镜像源) +RUN pip install --no-cache-dir Pillow -i https://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com || echo "Pillow installation failed, will install manually" # 复制所有代码 COPY . . diff --git a/backend/api/route.py b/backend/api/route.py index 6eb8759..53d2fa7 100644 --- a/backend/api/route.py +++ b/backend/api/route.py @@ -1,5 +1,5 @@ from fastapi import APIRouter -from .routes import presetsRoute, chatsRoute, worldbooksRoute, apiConfigRoute +from .routes import presetsRoute, chatsRoute, worldbooksRoute, apiConfigRoute, charactersRoute from utils.file_utils import get_all_roles_and_chats from core.config import settings from pathlib import Path @@ -11,6 +11,7 @@ router.include_router(presetsRoute.router) router.include_router(chatsRoute.router) router.include_router(worldbooksRoute.router) router.include_router(apiConfigRoute.router) +router.include_router(charactersRoute.router) # 保留原有的其他路由 diff --git a/backend/api/routes/chatsRoute.py b/backend/api/routes/chatsRoute.py index 855b3da..60295ce 100644 --- a/backend/api/routes/chatsRoute.py +++ b/backend/api/routes/chatsRoute.py @@ -1,56 +1,112 @@ from fastapi import APIRouter, HTTPException, status -# TODO: 实现 ChatService 来替代旧的 ChatHistory 逻辑 -# from services.chat_service import ChatService +from pathlib import Path +try: + from backend.services.chat_service import ChatService + from backend.core.config import settings +except ImportError: + # Docker环境:直接从当前目录导入 + from services.chat_service import ChatService + from core.config import settings router = APIRouter(prefix="/chat", tags=["chat"]) +# 初始化聊天服务 +data_path = Path(settings.DATA_PATH) if hasattr(settings, 'DATA_PATH') else Path("data") +chat_service = ChatService(data_path) + @router.get("", response_model=dict) async def list_all_chats(): """获取所有角色的所有聊天列表""" - # return await ChatService.list_all_chats() - return {"chats": []} + return chat_service.list_all_chats() + +@router.get("/{role_name}") +async def list_role_chats(role_name: str): + """获取指定角色的所有聊天列表""" + try: + all_chats = chat_service.list_all_chats() + # 从所有聊天中筛选出该角色的聊天 + role_chats = all_chats.get(role_name, []) + return role_chats + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) @router.get("/{role_name}/{chat_name}") async def get_chat(role_name: str, chat_name: str): """获取指定聊天的完整内容""" - raise HTTPException(status_code=501, detail="Not Implemented") + try: + return chat_service.get_chat(role_name, chat_name) + except FileNotFoundError as e: + raise HTTPException(status_code=404, detail=str(e)) @router.post("/{role_name}", status_code=status.HTTP_201_CREATED) -async def create_chat(role_name: str, chat_name: str, metadata: dict = None): +async def create_chat(role_name: str, chat_data: dict): """创建新聊天""" - raise HTTPException(status_code=501, detail="Not Implemented") + try: + chat_name = chat_data.get("chat_name", "新聊天") + metadata = chat_data.get("metadata", {}) + return chat_service.create_chat(role_name, chat_name, metadata) + except FileExistsError as e: + raise HTTPException(status_code=400, detail=str(e)) @router.put("/{role_name}/{chat_name}") async def update_chat(role_name: str, chat_name: str, update_data: dict): """更新聊天元数据""" + # TODO: 实现更新聊天元数据功能 raise HTTPException(status_code=501, detail="Not Implemented") @router.delete("/{role_name}/{chat_name}") async def delete_chat(role_name: str, chat_name: str): """删除指定聊天""" + # TODO: 实现删除聊天功能 raise HTTPException(status_code=501, detail="Not Implemented") @router.get("/{role_name}/{chat_name}/messages") async def list_messages(role_name: str, chat_name: str): """获取聊天的所有消息""" - raise HTTPException(status_code=501, detail="Not Implemented") + try: + chat_data = chat_service.get_chat(role_name, chat_name) + return {"messages": chat_data["messages"]} + except FileNotFoundError as e: + raise HTTPException(status_code=404, detail=str(e)) @router.get("/{role_name}/{chat_name}/messages/{floor}") async def get_message(role_name: str, chat_name: str, floor: int): """获取指定楼层的消息""" - raise HTTPException(status_code=501, detail="Not Implemented") + try: + chat_data = chat_service.get_chat(role_name, chat_name) + for msg in chat_data["messages"]: + if msg.get("floor") == floor: + return msg + raise HTTPException(status_code=404, detail=f"Message at floor {floor} not found") + except FileNotFoundError as e: + raise HTTPException(status_code=404, detail=str(e)) @router.post("/{role_name}/{chat_name}/messages", status_code=status.HTTP_201_CREATED) async def add_message(role_name: str, chat_name: str, message_data: dict): """向聊天添加新消息""" - raise HTTPException(status_code=501, detail="Not Implemented") + try: + return chat_service.add_message(role_name, chat_name, message_data) + except FileNotFoundError as e: + raise HTTPException(status_code=404, detail=str(e)) + except ValueError as e: + raise HTTPException(status_code=400, detail=str(e)) @router.put("/{role_name}/{chat_name}/messages/{floor}") async def update_message(role_name: str, chat_name: str, floor: int, update_data: dict): """更新指定楼层的消息""" - raise HTTPException(status_code=501, detail="Not Implemented") + try: + return chat_service.update_message(role_name, chat_name, floor, update_data) + except FileNotFoundError as e: + raise HTTPException(status_code=404, detail=str(e)) + except ValueError as e: + raise HTTPException(status_code=404, detail=str(e)) @router.delete("/{role_name}/{chat_name}/messages/{floor}") async def delete_message(role_name: str, chat_name: str, floor: int): """删除指定楼层的消息""" - raise HTTPException(status_code=501, detail="Not Implemented") + try: + return chat_service.delete_message(role_name, chat_name, floor) + except FileNotFoundError as e: + raise HTTPException(status_code=404, detail=str(e)) + except ValueError as e: + raise HTTPException(status_code=404, detail=str(e)) diff --git a/backend/core/config.py b/backend/core/config.py index 8f3f659..8c5071a 100644 --- a/backend/core/config.py +++ b/backend/core/config.py @@ -54,6 +54,12 @@ class Settings: # ComfyUI 工作流目录 COMFYUI_WORKFLOWS_PATH = DATA_PATH / "comfyui_workflows" + + # 角色卡目录 + CHARACTERS_PATH = DATA_PATH / "characters" + + # 图片资源目录 + IMAGES_PATH = DATA_PATH / "images" def ensure_directories(self): """确保所有配置的目录存在,如果不存在则创建""" @@ -64,6 +70,8 @@ class Settings: self.CHAT_PATH, self.TEMP_PATH, self.COMFYUI_WORKFLOWS_PATH, + self.CHARACTERS_PATH, + self.IMAGES_PATH, ] for directory in directories: directory.mkdir(parents=True, exist_ok=True) diff --git a/backend/main.py b/backend/main.py index 0bd088a..a685786 100644 --- a/backend/main.py +++ b/backend/main.py @@ -17,12 +17,22 @@ for logger_name in ['uvicorn', 'uvicorn.access', 'fastapi']: # backend/app/main.py from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware try: from backend.api.route import router except ImportError: from api.route import router app = FastAPI(title="LLM Workflow Engine") +# 配置CORS +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], # 开发环境允许所有来源,生产环境应该指定具体域名 + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + # 注册路由 app.include_router(router, prefix="/api") diff --git a/data/characters/defult.png b/data/characters/defult.png new file mode 100644 index 0000000..f27bf75 Binary files /dev/null and b/data/characters/defult.png differ diff --git a/data/chat/testRole1/111.jsonl b/data/chat/testRole1/111.jsonl deleted file mode 100644 index 30358dd..0000000 --- a/data/chat/testRole1/111.jsonl +++ /dev/null @@ -1,5 +0,0 @@ -{"user_name": "User", "character_name": "AI Dungeon Master", "integrity": "uuid-001", "chat_id_hash": "hash-001", "note_prompt": "你是一个经验丰富的D&D地下城主。", "note_interval": 0, "note_position": 0, "note_depth": 0, "note_role": 0, "extensions": {}, "timedWorldInfo": {}, "variables": {}, "tainted": false, "lastInContextMessageId": -1} -{"name": "User", "is_user": true, "is_system": false, "floor": 0, "send_date": "1700000000000", "mes": "你好,我想开始一个新的冒险。", "extra": {}, "swipes": [], "swipe_id": 0, "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": []} -{"name": "AI Dungeon Master", "is_user": false, "is_system": false, "floor": 1, "send_date": "1700000001000", "mes": "欢迎,冒险者。请告诉我你想扮演什么角色?", "extra": {"api": "openai", "model": "gpt-4"}, "swipes": ["欢迎,冒险者。请告诉我你想扮演什么角色?", "你好,旅行者。在这个奇幻世界中,你是谁?"], "swipe_id": 0, "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "api": "openai", "model": "gpt-4", "reasoning": null, "reasoning_duration": null, "reasoning_signature": null, "time_to_first_token": null, "bias": null} -{"name": "User", "is_user": true, "is_system": false, "floor": 2, "send_date": "1700000002000", "mes": "我想成为一名人类战士。", "extra": {}, "swipes": [], "swipe_id": 0, "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": []} -{"name": "AI Dungeon Master", "is_user": false, "is_system": false, "floor": 3, "send_date": "1700000003000", "mes": "很好。你站在喧闹的酒馆门口,手里握着一把旧长剑。你打算做什么?", "extra": {"api": "openai", "model": "gpt-4"}, "swipes": ["很好。你站在喧闹的酒馆门口,手里握着一把旧长剑。你打算做什么?", "明白了。作为一名人类战士,你正身处繁华的市集广场。你的下一步行动是?"], "swipe_id": 0, "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "api": "openai", "model": "gpt-4", "reasoning": null, "reasoning_duration": null, "reasoning_signature": null, "time_to_first_token": null, "bias": null} diff --git a/data/chat/testRole1/11111.jsonl b/data/chat/testRole1/11111.jsonl deleted file mode 100644 index 21bf633..0000000 --- a/data/chat/testRole1/11111.jsonl +++ /dev/null @@ -1,5 +0,0 @@ -{"user_name": "Commander", "character_name": "XCOM AI", "integrity": "uuid-003", "chat_id_hash": "hash-003", "note_prompt": "你是一名XCOM基地的中央AI,负责协助指挥官管理外星威胁。", "note_interval": 0, "note_position": 0, "note_depth": 0, "note_role": 0, "extensions": {}, "timedWorldInfo": {}, "variables": {}, "tainted": false, "lastInContextMessageId": -1} -{"name": "Commander", "is_user": true, "is_system": false, "floor": 0, "send_date": "1700000000000", "mes": "报告当前的外星活动情况。", "extra": {}, "swipes": [], "swipe_id": 0, "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": []} -{"name": "XCOM AI", "is_user": false, "is_system": false, "floor": 1, "send_date": "1700000001000", "mes": "指挥官,卫星侦测到在南美洲丛林中有高能反应。可能是外星着陆舱。", "extra": {"api": "openai", "model": "gpt-4"}, "swipes": ["指挥官,卫星侦测到在南美洲丛林中有高能反应。可能是外星着陆舱。", "警报。我们在非洲检测到异常信号,疑似外星绑架行动正在进行。"], "swipe_id": 0, "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "api": "openai", "model": "gpt-4", "reasoning": null, "reasoning_duration": null, "reasoning_signature": null, "time_to_first_token": null, "bias": null} -{"name": "Commander", "is_user": true, "is_system": false, "floor": 2, "send_date": "1700000002000", "mes": "派遣布拉德福上尉带领一个小队去调查。", "extra": {}, "swipes": [], "swipe_id": 0, "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": []} -{"name": "XCOM AI", "is_user": false, "is_system": false, "floor": 3, "send_date": "1700000003000", "mes": "指令已确认。天火运输机正在起飞。预计到达时间:20分钟。", "extra": {"api": "openai", "model": "gpt-4"}, "swipes": ["指令已确认。天火运输机正在起飞。预计到达时间:20分钟。", "收到。正在部署天火运输机。布拉德福上尉已登机。"], "swipe_id": 0, "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "api": "openai", "model": "gpt-4", "reasoning": null, "reasoning_duration": null, "reasoning_signature": null, "time_to_first_token": null, "bias": null} diff --git a/data/chat/testRole2/222.jsonl b/data/chat/testRole2/222.jsonl deleted file mode 100644 index c721f79..0000000 --- a/data/chat/testRole2/222.jsonl +++ /dev/null @@ -1,5 +0,0 @@ -{"user_name": "Player", "character_name": "Game Master", "integrity": "uuid-002", "chat_id_hash": "hash-002", "note_prompt": "场景:赛博朋克风格的未来城市。", "note_interval": 0, "note_position": 0, "note_depth": 0, "note_role": 0, "extensions": {}, "timedWorldInfo": {}, "variables": {}, "tainted": false, "lastInContextMessageId": -1} -{"name": "Player", "is_user": true, "is_system": false, "floor": 0, "send_date": "1700000000000", "mes": "我检查我的义体状态。", "extra": {}, "swipes": [], "swipe_id": 0, "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": []} -{"name": "Game Master", "is_user": false, "is_system": false, "floor": 1, "send_date": "1700000001000", "mes": "你的视觉义眼显示系统正常,但左臂的伺服电机发出轻微的嗡嗡声,似乎需要维护。", "extra": {"api": "openai", "model": "gpt-4"}, "swipes": ["你的视觉义眼显示系统正常,但左臂的伺服电机发出轻微的嗡嗡声,似乎需要维护。", "系统自检完成。你的神经接口连接稳定,但义体排异反应指数略有上升。"], "swipe_id": 0, "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "api": "openai", "model": "gpt-4", "reasoning": null, "reasoning_duration": null, "reasoning_signature": null, "time_to_first_token": null, "bias": null} -{"name": "Player", "is_user": true, "is_system": false, "floor": 2, "send_date": "1700000002000", "mes": "我联系我的黑客朋友,问他知不知道哪里有靠谱的义体医生。", "extra": {}, "swipes": [], "swipe_id": 0, "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": []} -{"name": "Game Master", "is_user": false, "is_system": false, "floor": 3, "send_date": "1700000003000", "mes": "你的朋友回复说:'去下城区的老维克那里,虽然他的店看起来很破,但他手艺没得说。'", "extra": {"api": "openai", "model": "gpt-4"}, "swipes": ["你的朋友回复说:'去下城区的老维克那里,虽然他的店看起来很破,但他手艺没得说。'", "通讯接通。你的朋友告诉你:'别去连锁店,去太平间后巷找'扳手',他收费公道。'"], "swipe_id": 0, "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "api": "openai", "model": "gpt-4", "reasoning": null, "reasoning_duration": null, "reasoning_signature": null, "time_to_first_token": null, "bias": null} diff --git a/data/chat/testRole3/333.jsonl b/data/chat/testRole3/333.jsonl deleted file mode 100644 index 0e8b60c..0000000 --- a/data/chat/testRole3/333.jsonl +++ /dev/null @@ -1,5 +0,0 @@ -{"user_name": "Player", "character_name": "Narrator", "integrity": "uuid-004", "chat_id_hash": "hash-004", "note_prompt": "这是一个文字冒险游戏,你需要描述场景并等待玩家输入。", "note_interval": 0, "note_position": 0, "note_depth": 0, "note_role": 0, "extensions": {}, "timedWorldInfo": {}, "variables": {}, "tainted": false, "lastInContextMessageId": -1} -{"name": "Player", "is_user": true, "is_system": false, "floor": 0, "send_date": "1700000000000", "mes": "开始游戏。", "extra": {}, "swipes": [], "swipe_id": 0, "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": []} -{"name": "Narrator", "is_user": false, "is_system": false, "floor": 1, "send_date": "1700000001000", "mes": "你醒来时发现自己躺在一片陌生的森林里,四周弥漫着浓雾。你身边有一个背包和一把生锈的匕首。", "extra": {"api": "openai", "model": "gpt-4"}, "swipes": ["你醒来时发现自己躺在一片陌生的森林里,四周弥漫着浓雾。你身边有一个背包和一把生锈的匕首。", "当你睁开眼睛,发现自己身处一艘废弃的飞船中,应急灯闪烁着红光。你手里紧握着一个数据盘。"], "swipe_id": 0, "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "api": "openai", "model": "gpt-4", "reasoning": null, "reasoning_duration": null, "reasoning_signature": null, "time_to_first_token": null, "bias": null} -{"name": "Player", "is_user": true, "is_system": false, "floor": 2, "send_date": "1700000002000", "mes": "我打开背包看看里面有什么。", "extra": {}, "swipes": [], "swipe_id": 0, "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": []} -{"name": "Narrator", "is_user": false, "is_system": false, "floor": 3, "send_date": "1700000003000", "mes": "背包里有一块干硬的面包,一个水壶(里面还有半壶水),以及一张画着奇怪符号的羊皮纸。", "extra": {"api": "openai", "model": "gpt-4"}, "swipes": ["背包里有一块干硬的面包,一个水壶(里面还有半壶水),以及一张画着奇怪符号的羊皮纸。", "背包里只有一把激光手枪,能量槽仅剩10%。还有一张写着'不要相信AI'的纸条。"], "swipe_id": 0, "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "api": "openai", "model": "gpt-4", "reasoning": null, "reasoning_duration": null, "reasoning_signature": null, "time_to_first_token": null, "bias": null} diff --git a/docker-compose.yml b/docker-compose.yml index c94e46b..647ee49 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,6 +7,8 @@ services: dockerfile: Dockerfile container_name: llm-backend command: uvicorn main:app --host 0.0.0.0 --port 8000 --reload + ports: + - "23337:8000" volumes: - ./backend:/app - ./data:/app/data diff --git a/frontend/COLOR_SCHEME_OPTIMIZATION.md b/frontend/COLOR_SCHEME_OPTIMIZATION.md deleted file mode 100644 index 45edd4b..0000000 --- a/frontend/COLOR_SCHEME_OPTIMIZATION.md +++ /dev/null @@ -1,388 +0,0 @@ -# 🎨 成熟配色方案优化 - 自然舒适的视觉体验 - -## ✅ 完成的优化 - -参考 **Material Design**、**VS Code**、**GitHub Dark** 等成熟网站的配色方案,优化了整体色彩系统。 - ---- - -## 🎯 设计理念 - -### 核心原则 - -1. **避免纯黑纯白** - 使用深灰/暖白,减少视觉疲劳 -2. **层次分明** - 通过不同灰度建立清晰的视觉层级 -3. **柔和对比** - 文字与背景保持舒适对比度(4.5:1 以上) -4. **不抢眼** - 低饱和度、低调优雅的色彩 -5. **自然舒适** - 长时间使用不刺眼、不疲劳 - ---- - -## 📊 配色方案对比 - -### 暗色主题(Dark Theme) - -#### 之前 - 偏蓝的深色 -```css ---color-bg-primary: #0f1115; /* 深蓝黑 */ ---color-bg-secondary: #161920; /* 中蓝黑 */ ---color-bg-tertiary: #1c1f27; /* 浅蓝黑 */ ---color-text-primary: #e8eaed; /* 亮白 */ ---color-accent: #6d8cff; /* 亮蓝色 */ -``` - -**问题**: -- ❌ 偏蓝调,不够中性 -- ❌ 文字过亮,对比度过高 -- ❌ 强调色过于鲜艳 - ---- - -#### 之后 - Material Design 标准深灰 -```css ---color-bg-primary: #121212; /* 深灰黑 - Material Design 标准 */ ---color-bg-secondary: #1e1e1e; /* 中灰黑 */ ---color-bg-tertiary: #2d2d2d; /* 浅灰黑 */ ---color-text-primary: #e0e0e0; /* 柔白 - 降低亮度 */ ---color-accent: #8b9cf7; /* 柔和蓝紫 - 降低饱和度 */ -``` - -**优势**: -- ✅ 中性灰色调,更专业 -- ✅ 文字柔和,对比度适中(符合 WCAG 4.5:1) -- ✅ 强调色优雅,不抢眼 - ---- - -### 亮色主题(Light Theme) - -#### 之前 - 冷白色 -```css ---color-bg-primary: #fafbfc; /* 冷白 */ ---color-text-primary: #1a1d21; /* 近黑 */ ---color-accent: #5b7fff; /* 亮蓝色 */ -``` - -**问题**: -- ❌ 背景偏冷 -- ❌ 文字过深,对比度过高 -- ❌ 强调色与暗色主题不一致 - ---- - -#### 之后 - 暖白色 -```css ---color-bg-primary: #fafafa; /* 暖白 - 更柔和 */ ---color-text-primary: #2c2c2c; /* 深灰 - 非纯黑 */ ---color-accent: #7a8be6; /* 与暗色主题一致 */ -``` - -**优势**: -- ✅ 暖白色调,更舒适 -- ✅ 文字为深灰而非纯黑,减少刺眼感 -- ✅ 强调色与暗色主题保持一致 - ---- - -## 🎨 完整配色体系 - -### 暗色主题配色表 - -| 用途 | 颜色值 | 说明 | -|------|--------|------| -| **背景层** | | | -| 主背景 | `#121212` | Material Design 标准深灰黑 | -| 次级背景 | `#1e1e1e` | 卡片、面板背景 | -| 三级背景 | `#2d2d2d` | 输入框、按钮背景 | -| 浮层背景 | `#252525` | 下拉菜单、弹窗 | -| 微妙背景 | `#1a1a1a` | 渐变、装饰 | -| **文字层** | | | -| 主文字 | `#e0e0e0` | 正文、标题(柔白) | -| 次要文字 | `#9e9e9e` | 副标题、说明(中灰) | -| 弱化文字 | `#757575` | 占位符、禁用(深灰) | -| **边框层** | | | -| 边框 | `#333333` | 分隔线、边框 | -| 浅色边框 | `#2a2a2a` | 轻微分隔 | -| 聚焦边框 | `#404040` | 输入框聚焦 | -| **强调色** | | | -| 主强调色 | `#8b9cf7` | 链接、按钮、激活状态 | -| 悬停 | `#9dabff` | 鼠标悬停 | -| 激活 | `#7a8be6` | 点击激活 | -| 浅色强调 | `rgba(139, 156, 247, 0.1)` | 背景高亮 | -| 极浅强调 | `rgba(139, 156, 247, 0.05)` | 微妙高亮 | - ---- - -### 亮色主题配色表 - -| 用途 | 颜色值 | 说明 | -|------|--------|------| -| **背景层** | | | -| 主背景 | `#fafafa` | 暖白色,非纯白 | -| 次级背景 | `#ffffff` | 纯白(卡片、面板) | -| 三级背景 | `#f5f5f5` | 浅灰色 | -| 浮层背景 | `#ffffff` | 下拉菜单、弹窗 | -| 微妙背景 | `#f0f0f0` | 渐变、装饰 | -| **文字层** | | | -| 主文字 | `#2c2c2c` | 正文、标题(深灰) | -| 次要文字 | `#666666` | 副标题、说明(中灰) | -| 弱化文字 | `#999999` | 占位符、禁用(浅灰) | -| **边框层** | | | -| 边框 | `#e0e0e0` | 分隔线、边框 | -| 浅色边框 | `#ebebeb` | 轻微分隔 | -| 聚焦边框 | `#d0d0d0` | 输入框聚焦 | -| **强调色** | | | -| 主强调色 | `#7a8be6` | 与暗色主题一致 | -| 悬停 | `#6a7bd6` | 鼠标悬停 | -| 激活 | `#5a6bc6` | 点击激活 | -| 浅色强调 | `rgba(122, 139, 230, 0.08)` | 背景高亮 | -| 极浅强调 | `rgba(122, 139, 230, 0.04)` | 微妙高亮 | - ---- - -## 🔍 设计细节 - -### 1. 为什么不用纯黑(#000000)? - -**科学依据**: -- OLED 屏幕显示纯黑时像素完全关闭,导致"拖影"效应 -- 纯黑与亮色文字对比度过高,造成视觉疲劳 -- 深灰(#121212)能更好地表达阴影和层次感 - -**Material Design 官方建议**: -> "Use dark gray (#121212) instead of pure black for surfaces. This allows shadows to be visible and creates depth." - ---- - -### 2. 为什么不用纯白(#ffffff)作为文字? - -**可读性研究**: -- 纯白文字在深色背景上会产生"光晕"效应 -- 柔白(#e0e0e0)降低亮度,减少眼睛疲劳 -- 长文本阅读时,柔白比纯白更舒适 - -**WCAG 对比度要求**: -- 普通文本:至少 4.5:1 -- 大号文本:至少 3:1 - -当前配色: -- `#e0e0e0` on `#121212` = **13.2:1** ✅ 远超标准 -- `#9e9e9e` on `#121212` = **7.8:1** ✅ 符合标准 - ---- - -### 3. 强调色选择逻辑 - -**之前**: `#6d8cff` (亮蓝色) -- 饱和度高,过于鲜艳 -- 在深色背景上显得突兀 - -**之后**: `#8b9cf7` (柔和蓝紫) -- 降低饱和度,更优雅 -- 带紫色调,更有质感 -- 与深色背景融合更好 - -**灵感来源**: -- Material Design 3 的 Primary Color -- VS Code 的链接颜色 -- GitHub Dark 的强调色 - ---- - -### 4. 阴影优化 - -**之前**: 多层阴影叠加 -```css ---shadow-md: 0 4px 8px rgba(0, 0, 0, 0.25), - 0 2px 4px rgba(0, 0, 0, 0.2); -``` - -**之后**: 单层阴影 -```css ---shadow-md: 0 4px 8px rgba(0, 0, 0, 0.4); -``` - -**原因**: -- 简化渲染,提升性能 -- 单层阴影更清晰、更现代 -- 适当提高透明度,确保在深色背景上可见 - ---- - -## 📈 视觉效果对比 - -### 暗色主题 - -**之前**: -``` -┌─────────────────────────────┐ -│ 深蓝黑背景 (#0f1115) │ -│ │ -│ 亮白文字 (#e8eaed) │ ← 对比度过高 -│ 亮蓝强调 (#6d8cff) │ ← 过于鲜艳 -└─────────────────────────────┘ -``` - -**之后**: -``` -┌─────────────────────────────┐ -│ 深灰黑背景 (#121212) │ -│ │ -│ 柔白文字 (#e0e0e0) │ ← 柔和舒适 -│ 蓝紫强调 (#8b9cf7) │ ← 优雅低调 -└─────────────────────────────┘ -``` - ---- - -### 亮色主题 - -**之前**: -``` -┌─────────────────────────────┐ -│ 冷白背景 (#fafbfc) │ -│ │ -│ 近黑文字 (#1a1d21) │ ← 对比度过高 -│ 亮蓝强调 (#5b7fff) │ ← 刺眼 -└─────────────────────────────┘ -``` - -**之后**: -``` -┌─────────────────────────────┐ -│ 暖白背景 (#fafafa) │ -│ │ -│ 深灰文字 (#2c2c2c) │ ← 柔和自然 -│ 蓝紫强调 (#7a8be6) │ ← 优雅统一 -└─────────────────────────────┘ -``` - ---- - -## 🎯 参考资源 - -### Material Design 暗色主题 -- 官方文档: https://material.io/design/color/dark-theme.html -- 推荐背景: `#121212` -- 推荐文字: `#e0e0e0` (主要), `#9e9e9e` (次要) - -### VS Code Dark+ -- 背景: `#1e1e1e` -- 文字: `#d4d4d4` -- 强调: `#569cd6` (蓝色) - -### GitHub Dark -- 背景: `#0d1117` -- 卡片: `#161b22` -- 边框: `#30363d` -- 文字: `#c9d1d9` - -### Apple Human Interface Guidelines -- 推荐使用系统灰度色板 -- 避免纯黑纯白 -- 保持足够的对比度 - ---- - -## ✅ 验证清单 - -### 暗色主题 -- [x] 背景使用深灰而非纯黑 (#121212) -- [x] 文字使用柔白而非纯白 (#e0e0e0) -- [x] 强调色降低饱和度 (#8b9cf7) -- [x] 边框颜色适中 (#333333) -- [x] 阴影适度增强 (0.3-0.55) -- [x] 对比度符合 WCAG 标准 - -### 亮色主题 -- [x] 背景使用暖白而非冷白 (#fafafa) -- [x] 文字使用深灰而非纯黑 (#2c2c2c) -- [x] 强调色与暗色主题一致 (#7a8be6) -- [x] 边框颜色柔和 (#e0e0e0) -- [x] 阴影非常轻微 (0.04-0.09) -- [x] 整体不刺眼 - -### 整体效果 -- [x] 暗色主题不偏蓝,中性灰色 -- [x] 亮色主题温暖舒适 -- [x] 两个主题强调色保持一致 -- [x] 所有颜色都不抢眼 -- [x] 长时间使用不疲劳 - ---- - -## 🎊 最终效果 - -### 用户体验提升 - -1. **更舒适的视觉** - - 无纯黑纯白的刺眼感 - - 柔和的对比度 - - 自然的色彩过渡 - -2. **更专业的印象** - - Material Design 标准配色 - - 中性灰色调 - - 优雅的强调色 - -3. **更好的可读性** - - 符合 WCAG 无障碍标准 - - 清晰的视觉层级 - - 适当的对比度 - -4. **更统一的风格** - - 暗色/亮色主题协调一致 - - 强调色保持统一 - - 整体不抢眼、不突兀 - ---- - -## 💡 使用建议 - -### 何时使用各层级背景 - -``` -主背景 (#121212 / #fafafa) - └─ 页面整体背景 - -次级背景 (#1e1e1e / #ffffff) - ├─ 侧边栏 - ├─ 卡片 - └─ 面板 - -三级背景 (#2d2d2d / #f5f5f5) - ├─ 输入框 - ├─ 按钮 - └─ 下拉选项 - -浮层背景 (#252525 / #ffffff) - ├─ 弹窗 - ├─ 下拉菜单 - └─ 工具提示 -``` - -### 何时使用各层级文字 - -``` -主文字 (#e0e0e0 / #2c2c2c) - ├─ 正文 - ├─ 标题 - └─ 重要信息 - -次要文字 (#9e9e9e / #666666) - ├─ 副标题 - ├─ 说明文字 - └─ 标签 - -弱化文字 (#757575 / #999999) - ├─ 占位符 - ├─ 禁用状态 - └─ 辅助信息 -``` - ---- - -**完成时间**: 2026-04-28 -**状态**: ✅ 成熟配色方案优化完成 -**参考标准**: Material Design、VS Code、GitHub Dark -**核心理念**: 自然、舒适、不抢眼 diff --git a/frontend/CSS_MIGRATION_COMPLETE.md b/frontend/CSS_MIGRATION_COMPLETE.md deleted file mode 100644 index 4443246..0000000 --- a/frontend/CSS_MIGRATION_COMPLETE.md +++ /dev/null @@ -1,443 +0,0 @@ -# 🎨 CSS 完整迁移报告 - -## ✅ 已完成的工作 - -### 1. **全局样式系统** - -#### 新增文件 -- ✅ `src/styles/variables.css` - 完整的 CSS 变量定义(深色/浅色主题) -- ✅ `src/styles/reset.css` - CSS Reset 和全局动画、布局样式 - -#### 核心特性 -```css -/* 深色主题(默认)*/ ---color-bg-primary: #0f1115; /* 优雅深色背景 */ ---color-accent: #6d8cff; /* 柔和蓝色强调色 */ ---radius-md: 12px; /* 精致圆角 */ ---shadow-lg: 多层阴影创造深度 */ ---transition-normal: 250ms cubic-bezier(...); /* 流畅动画 */ - -/* 浅色主题 */ ---color-bg-primary: #fafbfc; /* 明亮干净背景 */ ---color-accent: #5b7fff; /* 稍深的蓝色 */ -``` - ---- - -### 2. **主布局样式 (index.css)** - -#### 更新内容 -- ✅ `.app` - 应用容器,使用 flexbox 布局 -- ✅ `.main-container` - 主内容区域,包含三个面板 -- ✅ `.sidebar-left` - 左侧边栏(20% 宽度) -- ✅ `.chat-area` - 中间聊天区域(60% 宽度),带渐变背景 -- ✅ `.sidebar-right` - 右侧边栏(20% 宽度) -- ✅ 自定义滚动条样式(webkit) -- ✅ 主题切换过渡动画 - -#### 关键改进 -```css -/* 之前 */ -.sidebar-left { - width: 22.5%; - background-color: #ffffff; -} - -/* 之后 */ -.sidebar-left { - flex: 0 0 20%; - background-color: var(--color-bg-secondary); - border-right: 1px solid var(--color-border); - box-shadow: var(--shadow-xs); -} - -/* 聊天区域添加渐变背景 */ -.chat-area::before { - content: ''; - position: absolute; - background: - radial-gradient(circle at 20% 30%, rgba(109, 140, 255, 0.04) 0%, transparent 50%), - radial-gradient(circle at 80% 70%, rgba(109, 140, 255, 0.03) 0%, transparent 50%), - linear-gradient(180deg, var(--color-bg-primary) 0%, var(--color-bg-subtle) 100%); - pointer-events: none; - z-index: 0; -} -``` - ---- - -### 3. **TopBar 样式 (TopBar.css)** - -#### 完全重构 -- ✅ `.toolbar` - 顶部工具栏,56px 高度,毛玻璃效果 -- ✅ `.toolbar-icon` - 工具栏图标按钮 -- ✅ `.status-badge` - 状态徽章(角色、模型、预设、世界书) -- ✅ `.action-btn` - 操作按钮(设置、拓展、主题切换) -- ✅ `.theme-toggle` - 主题切换按钮特殊样式 -- ✅ `.panel-overlay` - 弹出面板遮罩层 -- ✅ `.panel-content` - 弹出面板内容 - -#### 视觉对比 - -**之前**: -```css -.toolbar { - height: 50px; - background-color: #fff; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); -} -``` - -**之后**: -```css -.toolbar { - height: 56px; - background-color: var(--color-bg-secondary); - border-bottom: 1px solid var(--color-border); - box-shadow: var(--shadow-md); - backdrop-filter: blur(10px); -} -``` - ---- - -### 4. **SideBarLeft 样式 (SideBarLeft.css)** - -#### 完全重构 -- ✅ `.sidebar-tabs` - 标签页容器 -- ✅ `.tab-button` - 标签按钮,底部边框激活指示器 -- ✅ `.sidebar-content` - 侧边栏内容区域 -- ✅ `.tab-placeholder` - 空状态占位符 - -#### 关键改进 -```css -/* 之前 */ -.tab-button.active::after { - content: ''; - position: absolute; - bottom: 0; - height: 3px; - background-color: #4a90e2; -} - -/* 之后 */ -.tab-button.active { - color: var(--color-accent); - border-bottom-color: var(--color-accent); - background: var(--color-accent-ultra-light); -} -``` - ---- - -### 5. **SideBarRight 样式 (SideBarRight.css)** - -#### 完全重构 -- ✅ `.sidebar-tabs` - 标签页容器 -- ✅ `.tab-button` - 标签按钮 -- ✅ `.sidebar-content` - 侧边栏内容区域 -- ✅ `.panel-section` - 面板分区(支持双标签) -- ✅ `.panel-section.has-divider` - 分隔线样式 -- ✅ `.tab-placeholder` - 空状态占位符 - -#### 关键特性 -```css -/* 当有两个页面时,第一个页面添加底部分隔线 */ -.panel-section.has-divider { - border-bottom: 1px solid var(--color-border-light); -} - -/* 当只有一个页面选中时,占据全部空间 */ -.panel-section:only-child { - flex: 1; -} -``` - ---- - -### 6. **ChatBox 样式 (ChatBox.css)** - -#### 全面更新 -- ✅ `.chat-box` - 聊天框容器 -- ✅ `.chat-messages` - 消息列表 -- ✅ `.message.user` / `.message.ai` - 用户/AI 消息气泡 -- ✅ `.bubble` - 消息气泡 -- ✅ `.message-header` - 消息头部(名称、ID、工具栏) -- ✅ `.toolbar-button` - 消息工具栏按钮 -- ✅ `.chat-input-wrapper` - 输入框容器,毛玻璃效果 -- ✅ `.chat-options` - 选项弹出框 -- ✅ `.option-label` - 选项标签 -- ✅ `.chat-input-area textarea` - 输入框 -- ✅ `.send-button` - 发送按钮,渐变背景 -- ✅ `.loading` / `.error` - 加载和错误状态 - -#### 关键改进 - -**消息气泡**: -```css -/* 之前 */ -.message.user { - background-color: #007bff; - border-bottom-right-radius: 4px; -} - -/* 之后 */ -.message.user { - background: var(--gradient-primary); - border-bottom-right-radius: var(--radius-sm); - box-shadow: var(--shadow-md); -} - -.message.ai { - background-color: var(--color-bg-secondary); - border: 1px solid var(--color-border-light); - box-shadow: var(--shadow-sm); -} -``` - -**发送按钮**: -```css -/* 之前 */ -.send-button { - background-color: #007bff; - border-radius: 20px; -} - -/* 之后 */ -.send-button { - background: var(--gradient-primary); - border-radius: var(--radius-xl); - box-shadow: var(--shadow-sm); -} - -.send-button:hover { - transform: translateY(-2px); - box-shadow: var(--shadow-md); -} -``` - -**输入框焦点**: -```css -.chat-input-area textarea:focus { - border-color: var(--color-accent); - box-shadow: 0 0 0 3px var(--color-accent-light); -} -``` - ---- - -### 7. **ThemeToggle 组件** - -#### 新增组件 -- ✅ `components/TopBar/items/ThemeToggle/ThemeToggle.jsx` -- ✅ `components/TopBar/items/ThemeToggle/ThemeToggle.css` -- ✅ `components/TopBar/items/ThemeToggle/index.js` - -#### 功能特点 -- 🌓 支持深色/浅色主题切换 -- 💾 主题偏好保存到 localStorage -- ✨ 悬停动画效果(旋转 + 缩放) -- 📱 响应式设计 - -```css -.theme-toggle:hover .theme-icon { - transform: rotate(15deg) scale(1.1); -} -``` - ---- - -## 📊 迁移统计 - -### 文件修改 -| 文件 | 状态 | 变更行数 | -|------|------|----------| -| `src/styles/variables.css` | ✅ 新建 | +119 | -| `src/styles/reset.css` | ✅ 新建 | +133 | -| `src/index.css` | ✅ 重构 | +99 / -8 | -| `components/TopBar/TopBar.css` | ✅ 重构 | +62 / -39 | -| `components/SideBarLeft/SideBarLeft.css` | ✅ 重构 | +37 / -51 | -| `components/SideBarRight/SideBarRight.css` | ✅ 重构 | +57 / -47 | -| `components/Mid/ChatBox/ChatBox.css` | ✅ 重构 | +84 / -66 | -| `components/TopBar/items/ThemeToggle/` | ✅ 新建 | +77 | - -**总计**: 约 **669 行新增**, **311 行删除** - -### CSS 变量使用 -- 🎨 颜色变量: 20+ 个 -- 📏 间距变量: 7 个 (--spacing-xs 到 --spacing-3xl) -- 🔘 圆角变量: 6 个 (--radius-sm 到 --radius-full) -- 💫 阴影变量: 7 个 (--shadow-xs 到 --shadow-2xl) -- ⚡ 过渡变量: 5 个 (--transition-fast 到 --transition-smooth) -- 📚 Z-index 变量: 7 个 - ---- - -## 🎯 设计风格对照表 - -### Reference → Our Project - -| Reference (Vue) | Our Project (React) | 状态 | -|-----------------|---------------------|------| -| MainLayout.vue | App.jsx + index.css | ✅ 完成 | -| TopBar.vue | TopBar/TopBar.jsx + TopBar.css | ✅ 完成 | -| LeftPanel.vue | SideBarLeft/SideBarLeft.css | ✅ 完成 | -| CenterPanel.vue | Mid/ChatBox/ChatBox.css | ✅ 完成 | -| RightPanel.vue | SideBarRight/SideBarRight.css | ✅ 完成 | -| useTheme.ts | ThemeToggle.jsx | ✅ 完成 | - ---- - -## 🎨 设计特点总结 - -### 1. **优雅的深色主题** -- 深邃但不压抑的背景色 (#0f1115) -- 柔和的蓝色强调色 (#6d8cff) -- 细腻的层次感(多层背景色) - -### 2. **精致的细节** -- 8-24px 的圆角系统 -- 多层阴影创造深度 -- 流畅的缓动动画 (cubic-bezier) - -### 3. **舒适的交互** -- 250ms 标准过渡时间 -- 微妙的悬停效果 -- 平滑的主题切换 - -### 4. **现代感** -- 毛玻璃效果 (backdrop-filter: blur) -- 渐变背景 (linear-gradient, radial-gradient) -- 响应式布局 - ---- - -## 📁 最终文件结构 - -``` -frontend/src/ -├── styles/ # ✨ 新增 -│ ├── variables.css # CSS 变量定义 -│ └── reset.css # CSS Reset + 全局样式 -│ -├── components/ -│ ├── TopBar/ -│ │ ├── TopBar.jsx # (已更新) -│ │ ├── TopBar.css # (完全重构) -│ │ └── items/ -│ │ └── ThemeToggle/ # ✨ 新增 -│ │ ├── ThemeToggle.jsx -│ │ ├── ThemeToggle.css -│ │ └── index.js -│ │ -│ ├── SideBarLeft/ -│ │ ├── SideBarLeft.jsx -│ │ └── SideBarLeft.css # (完全重构) -│ │ -│ ├── SideBarRight/ -│ │ ├── SideBarRight.jsx -│ │ └── SideBarRight.css # (完全重构) -│ │ -│ └── Mid/ -│ └── ChatBox/ -│ ├── ChatBox.jsx -│ └── ChatBox.css # (完全重构) -│ -└── index.css # (完全重构) -``` - ---- - -## ✅ 验证清单 - -### 全局样式 -- [x] CSS 变量正确定义 -- [x] 深色主题正常工作 -- [x] 浅色主题正常工作 -- [x] 主题切换按钮显示正常 -- [x] 主题偏好保存到 localStorage -- [x] 页面刷新后主题保持 - -### 布局样式 -- [x] 三栏布局比例正确 (20% - 60% - 20%) -- [x] 聊天区域渐变背景正常 -- [x] 侧边栏边框和阴影正常 -- [x] 自定义滚动条样式正常 -- [x] 主题切换过渡动画流畅 - -### 组件样式 -- [x] TopBar 毛玻璃效果正常 -- [x] 标签页激活状态正确 -- [x] 消息气泡样式正确 -- [x] 输入框焦点效果正常 -- [x] 发送按钮渐变和悬停效果正常 -- [x] 弹出面板样式正确 - -### 动画效果 -- [x] 按钮悬停动画流畅 -- [x] 主题切换图标旋转动画 -- [x] 面板淡入动画 -- [x] 所有过渡使用 CSS 变量 - ---- - -## 🚀 下一步建议 - -### 短期优化 -1. **更新子组件样式** - - ApiConfig、Presets、WorldBook 等标签页组件 - - Dice、Debug、Macros、Table 等右侧标签页 - - 确保所有子组件都使用 CSS 变量 - -2. **添加更多动画** - - 消息出现动画 (fadeIn) - - 页面切换动画 - - 加载状态动画 (shimmer) - -3. **优化性能** - - 减少不必要的过渡 - - 使用 will-change 优化动画 - - 懒加载大型组件 - -### 中期优化 -1. **添加自定义主题** - - 允许用户自定义颜色 - - 保存多个主题配置 - - 主题预设库 - -2. **无障碍优化** - - 确保对比度符合 WCAG AA 标准 - - 添加 prefers-color-scheme 支持 - - 键盘导航优化 - -3. **响应式设计** - - 移动端适配 (< 768px) - - 平板适配 (768px - 1024px) - - 可折叠侧边栏 - -### 长期优化 -1. **CSS 模块化** - - 考虑使用 CSS Modules 或 Styled Components - - 更好的样式隔离 - - 动态样式支持 - -2. **设计系统** - - 创建组件库 - - 统一的设计令牌 - - 自动化测试 - ---- - -## 📚 相关资源 - -- [CSS Variables MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties) -- [dsanddurga.com](https://www.dsanddurga.com/) - 设计灵感来源 -- [Cubic Bezier Generator](https://cubic-bezier.com/) - 动画曲线工具 -- [Can I Use - backdrop-filter](https://caniuse.com/backdrop-filter) - 浏览器兼容性 - ---- - -**完成时间**: 2026-04-28 -**状态**: ✅ CSS 迁移已完成 -**主题**: 深色(默认)/ 浅色可切换 -**风格**: 参考 dsanddurga.com 的优雅设计 diff --git a/frontend/DATA_TYPE_AUDIT_REPORT.md b/frontend/DATA_TYPE_AUDIT_REPORT.md deleted file mode 100644 index 5ce6770..0000000 --- a/frontend/DATA_TYPE_AUDIT_REPORT.md +++ /dev/null @@ -1,669 +0,0 @@ -# 前端数据类型使用情况检查报告 - -## 概述 - -本报告详细分析了前端代码中涉及前后端数据传递的部分,检查是否正确使用了新创建的数据类型系统。 - -**检查时间**: 2026-04-28 -**检查范围**: `frontend/src/` 目录下所有涉及 API 调用和状态管理的文件 - ---- - -## 📊 总体评估 - -### ✅ 已完成的部分 -- ✅ 创建了完整的双层数据类型系统 (`types/` 目录) -- ✅ 定义了 SillyTavern 兼容层 (`sillytavern.types.ts`) -- ✅ 定义了内部业务层 (`internal.types.ts`) -- ✅ 实现了格式转换函数 (`converters.ts`) - -### ❌ 存在的问题 -- ❌ **所有现有代码都未使用新的类型定义** -- ❌ Store 中的数据结构与后端 internal 模型不一致 -- ❌ API 调用没有类型注解 -- ❌ 组件 Props 缺少类型定义 - ---- - -## 🔍 详细问题分析 - -### 1. RoleSelectorSlice.jsx - -**文件**: `src/Store/Slices/RoleSelectorSlice.jsx` - -#### 问题 1.1: 角色数据结构不规范 - -**当前代码** (第 49 行): -```javascript -roleData: {}, // 格式: {role_name: [{chat_name, user_name, character_name, last_modified, message_count}, ...]} -``` - -**问题分析**: -- ❌ 使用的是匿名对象结构,没有类型定义 -- ❌ 字段命名与后端不一致(应使用 camelCase) -- ❌ 缺少必要的字段如 `id`, `characterId` 等 - -**应该使用**: -```typescript -import type { RoleInfo, ChatSummary } from '@/types'; - -// State 定义 -roleData: Record -``` - -#### 问题 1.2: API 响应处理无类型 - -**当前代码** (第 19-37 行): -```javascript -const data = await response.json(); - -// 转换数据格式以适应前端需求 -const roleData = {}; -if (data.chat && Array.isArray(data.chat)) { - data.chat.forEach(chat => { - if (!roleData[chat.role_name]) { - roleData[chat.role_name] = []; - } - roleData[chat.role_name].push({ - chat_name: chat.chat_name, - user_name: chat.user_name, - character_name: chat.character_name, - last_modified: chat.last_modified, - message_count: chat.message_count - }); - }); -} -``` - -**问题分析**: -- ❌ 没有对 API 响应进行类型断言 -- ❌ 手动转换数据结构,容易出错 -- ❌ 后端返回的格式不明确 - -**建议**: -```typescript -// 假设后端返回 ChatSummary[] 格式 -interface ChatListResponse { - chat: ChatSummary[]; -} - -const data: ChatListResponse = await response.json(); -``` - ---- - -### 2. ChatBoxSlice.jsx - -**文件**: `src/Store/Slices/ChatBoxSlice.jsx` - -#### 问题 2.1: 消息数据结构不完整 - -**当前代码** (第 14 行): -```javascript -messages: [], -``` - -**问题分析**: -- ❌ 没有类型注解 -- ❌ 消息对象结构与 `ChatMessage` 类型不完全一致 -- ❌ 添加了 `floor` 字段但未在类型中明确说明 - -**应该使用**: -```typescript -import type { ChatMessage } from '@/types'; - -messages: ChatMessage[] -``` - -**注意**: `ChatMessage` 类型已包含 `floor` 字段,这是前端特有的扩展。 - -#### 问题 2.2: WebSocket 消息无类型 - -**当前代码** (第 186-212 行): -```javascript -ws.onmessage = (event) => { - const data = JSON.parse(event.data); - console.log('[WebSocket] 收到消息', { type: data.type, content: data.content }); - - if (data.type === 'chunk') { - // ... - } else if (data.type === 'complete') { - // ... - } else if (data.type === 'error') { - // ... - } -}; -``` - -**问题分析**: -- ❌ WebSocket 消息没有类型定义 -- ❌ 使用魔法字符串 ('chunk', 'complete', 'error') -- ❌ 容易拼写错误 - -**应该使用**: -```typescript -import type { WSResponseMessage } from '@/types'; - -ws.onmessage = (event) => { - const data: WSResponseMessage = JSON.parse(event.data); - - if (data.type === 'chunk') { - // TypeScript 会自动推断 data.content 存在 - } -}; -``` - -#### 问题 2.3: WebSocket 请求无类型 - -**当前代码** (第 238-255 行): -```javascript -ws.send(JSON.stringify({ - floor: nextFloor, - mes: content, - is_user: true, - currentRole: currentRole, - currentChat: currentChat, - options: options, - apiConfig: { - api_url: ..., - api_key: ... - }, - presetConfig: { - selectedPreset: ..., - parameters: ..., - promptComponents: ... - }, - stream: options.streamOutput -})); -``` - -**问题分析**: -- ❌ 请求对象结构复杂但没有类型定义 -- ❌ 嵌套对象结构不清晰 -- ❌ 难以维护 - -**应该使用**: -```typescript -import type { WSRequestMessage } from '@/types'; - -const request: WSRequestMessage = { - floor: nextFloor, - mes: content, - is_user: true, - currentRole, - currentChat, - options, - apiConfig: { - api_url: ..., - api_key: ... - }, - presetConfig: { - selectedPreset: ..., - parameters: ..., - promptComponents: ... - }, - stream: options.streamOutput -}; - -ws.send(JSON.stringify(request)); -``` - -#### 问题 2.4: API 配置获取无类型 - -**当前代码** (第 246-247 行): -```javascript -api_url: apiConfigStore.allApis.find(api => api.category === 'text' && api.id === apiConfigStore.activeMap.text)?.api_url || '', -api_key: apiConfigStore.allApis.find(api => api.category === 'text' && api.id === apiConfigStore.activeMap.text)?.api_key || '' -``` - -**问题分析**: -- ❌ `allApis` 数组元素没有类型 -- ❌ 访问属性时没有类型检查 -- ❌ 可能访问到 undefined - -**应该使用**: -```typescript -import type { ApiConfig } from '@/types'; - -// ApiConfigSlice 中应该定义为 -allApis: ApiConfig[] - -// 使用时有自动补全和类型检查 -const activeApi = apiConfigStore.allApis.find( - api => api.category === 'text' && api.id === apiConfigStore.activeMap.text -); - -api_url: activeApi?.apiUrl || '', // 注意是 apiUrl 不是 api_url -api_key: activeApi?.apiKey || '' // 注意是 apiKey 不是 api_key -``` - ---- - -### 3. ApiConfigSlice.jsx - -**文件**: `src/Store/Slices/LeftTabsSlices/ApiConfigSlice.jsx` - -#### 问题 3.1: API 配置数据结构不一致 - -**当前代码** (第 6-16 行): -```javascript -const initialState = { - allApis: [], // 存储所有获取到的API,包含category属性 - activeMap: {}, // 存储当前激活的配置映射 { category: profileId } - loading: false, - error: null, - notification: { - show: false, - message: '', - type: 'info' - } -}; -``` - -**问题分析**: -- ❌ `allApis` 没有类型定义 -- ❌ 字段命名使用 snake_case (`api_url`, `api_key`),应与 internal.types.ts 保持一致使用 camelCase -- ❌ `activeMap` 结构不明确 - -**应该使用**: -```typescript -import type { ApiConfig } from '@/types'; - -interface ApiConfigState { - allApis: ApiConfig[]; - activeMap: Record; // { category: configId } - loading: boolean; - error: string | null; - notification: { - show: boolean; - message: string; - type: 'success' | 'error' | 'info'; - }; -} -``` - -#### 问题 3.2: API 响应处理无类型 - -**当前代码** (第 34 行): -```javascript -const data = await response.json(); -set({ allApis: data, loading: false }); -``` - -**问题分析**: -- ❌ 直接使用原始响应数据 -- ❌ 没有验证数据结构 -- ❌ 如果后端返回格式变化,运行时才会发现错误 - -**应该使用**: -```typescript -const data: ApiConfig[] = await response.json(); -set({ allApis: data, loading: false }); -``` - ---- - -### 4. PresetSlice.jsx - -**文件**: `src/Store/Slices/LeftTabsSlices/PresetSlice.jsx` - -#### 问题 4.1: 预设参数命名不一致 - -**当前代码** (第 8-20 行): -```javascript -parameters: { - temperature: 1.0, - frequency_penalty: 0.0, // ❌ snake_case - presence_penalty: 0.0, // ❌ snake_case - top_p: 1.0, // ❌ snake_case - top_k: 0, // ❌ snake_case - max_context: 1000000, // ❌ snake_case - max_tokens: 30000, // ❌ snake_case - max_context_unlocked: false, // ❌ snake_case - stream_openai: true, // ❌ snake_case - seed: -1, - n: 1 -}, -``` - -**问题分析**: -- ❌ 大量使用 snake_case,与 internal.types.ts 的 camelCase 不一致 -- ❌ 这些参数应该对应后端的 `GenerationPreset` 模型 -- ❌ 字段名混乱导致前后端对接困难 - -**应该使用**: -```typescript -import type { GenerationPreset } from '@/types'; - -// 或者至少保持命名一致 -parameters: { - temperature: 1.0, - frequencyPenalty: 0.0, // ✅ camelCase - presencePenalty: 0.0, // ✅ camelCase - topP: 1.0, // ✅ camelCase - topK: 0, // ✅ camelCase - maxContext: 1000000, // ✅ camelCase - maxTokens: 30000, // ✅ camelCase - maxContextUnlocked: false, // ✅ camelCase - streamOpenai: true, // ✅ camelCase - seed: -1, - n: 1 -} -``` - -#### 问题 4.2: Prompt 组件结构不规范 - -**当前代码** (第 32-97 行): -```javascript -promptComponents: [ - { - identifier: "dialogueExamples", - name: "Chat Examples", - system_prompt: true, // ❌ snake_case - marker: true, - enabled: true, - role: 0 // ❌ 应该用枚举或明确的类型 - }, - // ... -], -``` - -**问题分析**: -- ❌ 字段命名不一致 -- ❌ `role` 使用数字 (0, 1, 2),应该使用枚举或字符串 -- ❌ 结构与 `PromptComponent` 类型定义不完全匹配 - -**应该使用**: -```typescript -import type { PromptComponent } from '@/types'; - -promptComponents: PromptComponent[] - -// PromptComponent 类型定义应调整为 -export interface PromptComponent { - identifier: string; - name: string; - systemPrompt: boolean; // ✅ camelCase - marker: boolean; - enabled: boolean; - role: number; // 或者改为 enum PromptRole { System = 0, User = 1, Assistant = 2 } -} -``` - -#### 问题 4.3: API 响应处理复杂且无类型 - -**当前代码** (第 103-119 行): -```javascript -const response = await fetch('/api/presets'); -const data = await response.json(); - -// 转换为预设对象数组 -const presetList = data.presets.map(preset => ({ - id: preset.name, - name: preset.name, - description: preset.description, - component_count: preset.component_count, // ❌ snake_case - temperature: preset.temperature -})); -``` - -**问题分析**: -- ❌ 手动转换数据结构 -- ❌ 字段命名不一致 -- ❌ 没有类型验证 - -**应该使用**: -```typescript -interface PresetListResponse { - presets: Array<{ - name: string; - description?: string; - component_count?: number; - temperature?: number; - }>; -} - -const data: PresetListResponse = await response.json(); -const presetList: Array<{ - id: string; - name: string; - description: string; - componentCount: number; // ✅ camelCase - temperature: number; -}> = data.presets.map(preset => ({ - id: preset.name, - name: preset.name, - description: preset.description || '', - componentCount: preset.component_count || 0, - temperature: preset.temperature || 1.0 -})); -``` - ---- - -### 5. WorldBookSlice.jsx - -**文件**: `src/Store/Slices/LeftTabsSlices/WorldBookSlice.jsx` - -#### 问题 5.1: 世界书数据结构缺失 - -**当前代码** (第 51-59 行): -```javascript -worldBooks: [], // 世界书列表 -globalWorldBooks: loadGlobalWorldBooks(), // 从 LocalStorage 初始化全局世界书列表 -currentWorldBook: null, // 当前选中的世界书 -currentEntries: [], // 当前世界书的条目列表 -currentEntry: null, // 当前选中的条目 -``` - -**问题分析**: -- ❌ 完全没有类型定义 -- ❌ 世界书和条目的结构不明确 -- ❌ 应该对应后端的 `WorldInfo` 和 `WorldInfoEntry` 模型 - -**应该使用**: -```typescript -import type { WorldInfo, WorldInfoEntry } from '@/types'; - -// 需要在 internal.types.ts 中添加 WorldInfo 和 WorldInfoEntry 类型 -worldBooks: WorldInfo[]; -globalWorldBooks: WorldInfo[]; -currentWorldBook: WorldInfo | null; -currentEntries: WorldInfoEntry[]; -currentEntry: WorldInfoEntry | null; -``` - ---- - -## 📋 数据类型对照表 - -### 前端当前使用 vs 应该使用的类型 - -| 位置 | 当前数据结构 | 应该使用的类型 | 优先级 | -|------|------------|--------------|--------| -| RoleSelectorSlice | 匿名对象 `{role_name, chats}` | `Record` | 🔴 高 | -| ChatBoxSlice.messages | 匿名数组 | `ChatMessage[]` | 🔴 高 | -| ChatBoxSlice.wsConnection | WebSocket | 无需修改 | 🟢 低 | -| ApiConfigSlice.allApis | 匿名数组 | `ApiConfig[]` | 🔴 高 | -| ApiConfigSlice.activeMap | 匿名对象 | `Record` | 🟡 中 | -| PresetSlice.parameters | snake_case 对象 | `GenerationPreset` (camelCase) | 🔴 高 | -| PresetSlice.promptComponents | 匿名数组 | `PromptComponent[]` | 🟡 中 | -| WorldBookSlice.worldBooks | 匿名数组 | `WorldInfo[]` (需添加类型) | 🟡 中 | - ---- - -## 🎯 核心问题总结 - -### 1. 类型系统未集成 -- ❌ 创建了类型定义但没有任何文件导入使用 -- ❌ 所有 Store 都是 `.jsx` 而非 `.tsx` -- ❌ 没有 TypeScript 类型检查 - -### 2. 命名规范不一致 -- ❌ 混用 snake_case 和 camelCase -- ❌ 与后端 internal 模型的命名不一致 -- ❌ 增加前后端对接难度 - -### 3. API 响应处理不规范 -- ❌ 直接使用 `response.json()` 无类型断言 -- ❌ 手动转换数据结构容易出错 -- ❌ 缺少运行时验证 - -### 4. WebSocket 消息无类型 -- ❌ 请求和响应都没有类型定义 -- ❌ 使用魔法字符串 -- ❌ 难以维护和调试 - ---- - -## 💡 改进建议 - -### 短期方案(立即可做) - -#### 1. 添加 JSDoc 类型注释(无需改文件扩展名) - -```javascript -// @ts-check -/** @type {import('@/types').ChatMessage[]} */ -const messages = []; - -/** - * @param {string} content - * @returns {Promise} - */ -const sendMessage = async (content) => { - // ... -}; -``` - -#### 2. 统一命名规范 - -将所有 snake_case 改为 camelCase,与 internal.types.ts 保持一致: -- `frequency_penalty` → `frequencyPenalty` -- `api_url` → `apiUrl` -- `system_prompt` → `systemPrompt` - -#### 3. 添加 API 响应类型断言 - -```javascript -// 在 fetch 后立即断言类型 -const data = /** @type {import('@/types').ApiConfig[]} */ (await response.json()); -``` - -### 中期方案(推荐) - -#### 1. 逐步迁移到 TypeScript - -按以下顺序将 `.jsx` 改为 `.tsx`: -1. `types/` 目录(已完成 ✅) -2. Store Slices(优先级最高) -3. 组件文件 -4. 工具函数 - -#### 2. 更新 internal.types.ts - -补充缺失的类型定义: -- `WorldInfo` - 世界书 -- `WorldInfoEntry` - 世界书条目 -- `RoleInfo` - 角色信息(完善) - -#### 3. 创建 API Client 封装 - -```typescript -// src/api/client.ts -import type { ChatLog, ApiConfig, GenerationPreset } from '@/types'; - -export const apiClient = { - async getChat(role: string, chat: string): Promise { - const response = await fetch(`/api/chat/${role}/${chat}`); - return response.json(); - }, - - async getApiConfigs(): Promise { - const response = await fetch('/api/apiconfigs'); - return response.json(); - }, - - // ... -}; -``` - -### 长期方案(理想状态) - -#### 1. 完全 TypeScript 化 -- 所有文件使用 `.tsx` 扩展名 -- 启用严格的 TypeScript 检查 -- 配置 ESLint + TypeScript 规则 - -#### 2. 使用 Zod 进行运行时验证 -```typescript -import { z } from 'zod'; -import { ChatMessageSchema } from '@/types/schemas'; - -const data = await response.json(); -const validated = ChatMessageSchema.parse(data); // 运行时验证 -``` - -#### 3. 自动生成类型 -- 从后端 OpenAPI/Swagger 文档生成前端类型 -- 确保前后端类型始终同步 - ---- - -## 📝 具体修复清单 - -### 优先级 P0(必须修复) - -- [ ] 更新 `ChatBoxSlice.jsx` 使用 `ChatMessage[]` 类型 -- [ ] 更新 `ApiConfigSlice.jsx` 使用 `ApiConfig[]` 类型 -- [ ] 统一 PresetSlice 参数命名为 camelCase -- [ ] 为 WebSocket 消息添加类型定义 - -### 优先级 P1(强烈建议) - -- [ ] 将 Store Slices 从 `.jsx` 迁移到 `.tsx` -- [ ] 补充 `WorldInfo` 和 `WorldInfoEntry` 类型定义 -- [ ] 更新 `RoleSelectorSlice` 使用规范类型 -- [ ] 创建 API Client 封装层 - -### 优先级 P2(可以后续做) - -- [ ] 组件 Props 添加类型注解 -- [ ] 添加 JSDoc 文档注释 -- [ ] 配置 TypeScript 严格模式 -- [ ] 添加运行时数据验证 - ---- - -## 🔗 相关资源 - -- [前端数据类型规范](./types/README.md) -- [Internal Types](./types/internal.types.ts) -- [SillyTavern Types](./types/sillytavern.types.ts) -- [Converters](./types/converters.ts) -- [后端 Internal 模型](../../backend/models/internal.py) - ---- - -## 结论 - -**现状**: 前端已建立完整的类型系统,但**尚未在任何地方使用**。 - -**影响**: -- ⚠️ 类型安全优势无法发挥 -- ⚠️ 容易出现运行时错误 -- ⚠️ 前后端数据结构可能不一致 -- ⚠️ 代码可维护性差 - -**建议**: -1. 立即开始逐步迁移到 TypeScript -2. 优先处理 Store 层的类型化 -3. 统一命名规范为 camelCase -4. 添加 API 响应的类型断言 - -**预期收益**: -- ✅ 编译时捕获类型错误 -- ✅ IDE 智能提示和自动补全 -- ✅ 更好的代码文档 -- ✅ 减少运行时错误 -- ✅ 提高开发效率 diff --git a/frontend/GLOBAL_MINIMALIST_STYLE_COMPLETE.md b/frontend/GLOBAL_MINIMALIST_STYLE_COMPLETE.md deleted file mode 100644 index 0bee8b4..0000000 --- a/frontend/GLOBAL_MINIMALIST_STYLE_COMPLETE.md +++ /dev/null @@ -1,382 +0,0 @@ -# 🎨 全局按钮极简风格优化报告 - -## ✅ 完成的优化 - -按照用户要求,将**所有按钮**统一为极简风格,包括: -1. ✅ ChatBox 输入框左右按钮 -2. ✅ TopBar 工具栏操作按钮 -3. ✅ TopBar 状态徽章 -4. ✅ ThemeToggle 主题切换按钮 - ---- - -## 📊 优化对比 - -### 1. **TopBar 操作按钮 (action-btn)** - -#### 之前 - 复杂风格 -```css -.action-btn { - width: 42px; - height: 42px; - border-radius: var(--radius-lg); - background-color: transparent; - color: var(--color-text-secondary); - border: 1px solid transparent; - position: relative; -} - -/* 伪元素渐变背景 */ -.action-btn::before { - content: ''; - position: absolute; - inset: 0; - background: var(--color-accent-light); - opacity: 0; -} - -/* 复杂悬停效果 */ -.action-btn:hover { - color: var(--color-accent); - border-color: var(--color-accent); - transform: translateY(-2px); /* 上浮 */ - box-shadow: var(--shadow-md); /* 阴影 */ -} - -.action-btn:hover::before { - opacity: 1; /* 渐变显示 */ -} - -.action-btn:hover svg { - transform: scale(1.1) rotate(5deg); /* 放大+旋转 */ -} -``` - -#### 之后 - 极简风格 -```css -.action-btn { - width: 32px; /* ⬇️ 减小 24% */ - height: 36px; /* ⬇️ 减小 14% */ - border-radius: var(--radius-md); /* 从 lg 改为 md */ - background-color: transparent; - color: var(--color-text-muted); /* muted 颜色 */ - border: none; /* 移除边框 */ -} - -.action-btn:hover { - background-color: var(--color-bg-tertiary); /* 简单背景色 */ - color: var(--color-text-secondary); -} - -.action-btn:active { - background-color: var(--color-accent-ultra-light); - color: var(--color-accent); -} - -.action-btn:hover svg { - transform: scale(1.05); /* 仅轻微放大,无旋转 */ -} -``` - -**移除的效果**: -- ❌ 伪元素渐变背景 -- ❌ 边框变化 -- ❌ 上浮动画 -- ❌ 阴影效果 -- ❌ SVG 旋转 - -**保留的效果**: -- ✅ 简单的背景色变化 -- ✅ SVG 轻微放大 (1.05x) - ---- - -### 2. **ThemeToggle 主题切换按钮** - -#### 之前 - 复杂风格 -```css -.theme-toggle::after { - content: ''; - position: absolute; - inset: 0; - background: var(--gradient-primary); /* 渐变背景 */ - opacity: 0; -} - -.theme-toggle:hover::after { - opacity: 0.1; -} - -.theme-toggle:hover svg { - transform: scale(1.15) rotate(15deg); /* 大幅放大+旋转 */ - color: var(--color-accent); -} -``` - -#### 之后 - 极简风格 -```css -.theme-toggle { - /* Inherits all styles from action-btn */ -} - -.theme-toggle:hover svg { - transform: scale(1.05); /* 仅轻微放大 */ -} -``` - -**移除的效果**: -- ❌ 伪元素渐变背景 -- ❌ SVG 大幅放大 (1.15x → 1.05x) -- ❌ SVG 旋转 (15° → 0°) -- ❌ 颜色变化 - ---- - -### 3. **状态徽章 (status-badge)** - -#### 之前 - 显眼风格 -```css -.status-badge { - padding: var(--spacing-sm) var(--spacing-md); - background-color: var(--color-bg-primary); /* 有背景 */ - border: 1px solid var(--color-border-light); /* 有边框 */ - border-radius: var(--radius-lg); - min-height: 36px; -} - -.status-badge:hover { - border-color: var(--color-accent); - box-shadow: 0 0 0 3px var(--color-accent-light); /* 光晕 */ - transform: translateY(-2px); /* 上浮 */ -} - -.status-icon { - font-size: 1.2rem; -} - -.status-label { - font-size: 0.95rem; - color: var(--color-text-primary); - font-weight: 500; -} -``` - -#### 之后 - 极简风格 -```css -.status-badge { - padding: var(--spacing-xs) var(--spacing-sm); /* ⬇️ 减小 */ - background-color: transparent; /* 透明背景 */ - border: none; /* 无边框 */ - border-radius: var(--radius-sm); /* 从 lg 改为 sm */ - min-height: 32px; /* ⬇️ 减小 */ -} - -.status-badge:hover { - background-color: var(--color-bg-tertiary); /* 简单背景 */ -} - -.status-icon { - font-size: 1rem; /* ⬇️ 减小 */ - opacity: 0.7; /* 降低透明度 */ -} - -.status-label { - font-size: 0.85rem; /* ⬇️ 减小 */ - color: var(--color-text-secondary); /* secondary 颜色 */ - font-weight: 400; /* 从 500 改为 400 */ -} -``` - -**改进**: -- ✅ 默认透明背景,不抢眼 -- ✅ 移除边框和光晕 -- ✅ 移除上浮动画 -- ✅ 图标和文字更小、更淡 -- ✅ 字体粗细从 500 降到 400 - ---- - -### 4. **ChatBox 输入框按钮** - -已在之前的优化中完成(见 INPUT_SIMPLIFICATION_COMPLETE.md): - -- **选项按钮**: 32x36px, 透明背景, muted 颜色 -- **发送按钮**: 32x36px, 透明背景, muted 颜色 -- **SVG 图标**: 14-16px, 轻微放大效果 - ---- - -## 📈 尺寸对比总结 - -| 元素 | 之前 | 之后 | 变化 | -|------|------|------|------| -| **TopBar 按钮** | 42x42px | 32x36px | ⬇️ 14-24% | -| **状态徽章高度** | 36px | 32px | ⬇️ 11% | -| **状态徽章内边距** | sm/md | xs/sm | ⬇️ 30% | -| **图标大小** | 1.2rem | 1rem | ⬇️ 17% | -| **文字大小** | 0.95rem | 0.85rem | ⬇️ 11% | -| **圆角** | radius-lg | radius-md/sm | ⬇️ 25% | - ---- - -## 🎨 设计哲学 - -### 极简主义原则 - -1. **透明背景** - 默认不可见,只在交互时显示 -2. **无装饰** - 移除边框、阴影、渐变等装饰元素 -3. **低调颜色** - 使用 muted/secondary 颜色,不抢视线 -4. **微小动画** - 仅保留最必要的反馈(背景色变化、轻微放大) -5. **紧凑尺寸** - 减少空间占用,提高信息密度 - -### 视觉层级 - -**之前**: 🔴 高视觉权重(边框+阴影+渐变+动画) -**之后**: 🟢 低视觉权重(透明+muted+微动画) - ---- - -## 🎯 用户体验提升 - -### 1. **视觉焦点更清晰** -- 按钮不再分散注意力 -- 主要内容更加突出 -- 界面更加清爽 - -### 2. **空间利用率更高** -- 按钮尺寸减小 14-24% -- 内边距减少 30% -- 整体布局更紧凑 - -### 3. **交互更自然** -- 悬停反馈简洁明了 -- 没有夸张的动画 -- 符合现代 UI 趋势 - -### 4. **一致性更强** -- 所有按钮统一风格 -- TopBar 和 ChatBox 保持一致 -- 整体设计语言统一 - ---- - -## 📝 技术细节 - -### CSS 变量使用 -```css -/* 颜色 */ -var(--color-text-muted) /* #6b7280 - 默认颜色 */ -var(--color-text-secondary) /* #9aa0a6 - 悬停颜色 */ -var(--color-accent) /* #6d8cff - 激活颜色 */ -var(--color-accent-ultra-light) /* rgba(109, 140, 255, 0.05) - 激活背景 */ -var(--color-bg-tertiary) /* #1c1f27 - 悬停背景 */ - -/* 间距 */ -var(--spacing-xs) /* 4px */ -var(--spacing-sm) /* 6px */ - -/* 圆角 */ -var(--radius-sm) /* 8px */ -var(--radius-md) /* 12px */ - -/* 过渡 */ -var(--transition-fast) /* 150ms */ -``` - -### 移除的复杂效果 -- ❌ `position: relative` + 伪元素 -- ❌ `box-shadow` 多层阴影 -- ❌ `transform: translateY()` 上浮动画 -- ❌ `rotate()` 旋转动画 -- ❌ `border` 边框变化 -- ❌ `opacity` 渐变显示 - -### 保留的简单效果 -- ✅ `background-color` 背景色变化 -- ✅ `transform: scale(1.05)` 轻微放大 -- ✅ `color` 颜色变化 -- ✅ `transition: 150ms` 快速过渡 - ---- - -## ✅ 验证清单 - -### TopBar 按钮 -- [x] 尺寸从 42x42 减小到 32x36 -- [x] 移除伪元素渐变 -- [x] 移除边框和阴影 -- [x] 移除上浮动画 -- [x] SVG 仅轻微放大 (1.05x) -- [x] 默认 muted 颜色 - -### ThemeToggle -- [x] 继承 action-btn 样式 -- [x] 移除渐变背景 -- [x] 移除旋转动画 -- [x] SVG 仅轻微放大 - -### 状态徽章 -- [x] 透明背景 -- [x] 无边框 -- [x] 减小内边距 -- [x] 减小图标和文字 -- [x] 降低字重 (500 → 400) -- [x] 悬停仅显示背景色 - -### ChatBox 按钮 -- [x] 已完成(见之前报告) -- [x] 与 TopBar 风格一致 - ---- - -## 🎊 最终效果 - -### 统一的极简风格 - -**所有按钮现在都遵循相同的设计原则**: -1. 透明背景,默认低调 -2. 悬停时简单背景色变化 -3. 激活时强调色反馈 -4. 无装饰性动画 -5. 紧凑的尺寸 - -### 视觉效果 - -**之前**: -``` -[🔴 显眼按钮] [🔴 显眼徽章] [🔴 显眼按钮] - ↑ ↑ ↑ - 边框+阴影 背景+边框 渐变+旋转 -``` - -**之后**: -``` -[⚪ 低调] [⚪ 低调] [⚪ 低调] - ↓ ↓ ↓ - 悬停才显 悬停才显 悬停才显 -``` - ---- - -## 🚀 下一步建议 - -### 可选优化 -1. **统一其他组件** - - SideBar 标签按钮 - - Presets 操作按钮 - - WorldBook 操作按钮 - -2. **添加键盘支持** - - Tab 导航焦点样式 - - 键盘快捷键反馈 - -3. **无障碍优化** - - 确保足够的对比度 - - 添加 aria-label - - 焦点可见性 - ---- - -**完成时间**: 2026-04-28 -**状态**: ✅ 全局按钮极简风格优化完成 -**设计风格**: 极简主义、低调优雅、简洁明了 diff --git a/frontend/INPUT_BOX_FIX_COMPLETE.md b/frontend/INPUT_BOX_FIX_COMPLETE.md deleted file mode 100644 index adcefec..0000000 --- a/frontend/INPUT_BOX_FIX_COMPLETE.md +++ /dev/null @@ -1,433 +0,0 @@ -# 🔧 输入框和布局修复报告 - -## ✅ 完成的工作 - -### 1. **修复侧边栏高度问题** - -#### 问题 -左右侧边栏的边框没有接上顶部,与 TopBar 之间有间隙。 - -#### 解决方案 -在 `index.css` 的 `.main-container` 中添加: -```css -.main-container { - margin-top: 0; /* Ensure panels start from top */ -} -``` - -#### 结果 -✅ 侧边栏现在从顶部开始,边框与 TopBar 无缝连接 - ---- - -### 2. **完全重构 ChatInput 样式** - -按照 reference 的 ChatInput.vue 设计,完全重构了输入框区域的样式和结构。 - -#### 主要变更 - -##### A. 容器结构更新 -```jsx -// 之前 -
- -
...
-