diff --git a/frontend/Orbita_Arch.md b/frontend/Orbita_Arch.md index 585bc4f..8da3e45 100644 --- a/frontend/Orbita_Arch.md +++ b/frontend/Orbita_Arch.md @@ -10,8 +10,8 @@ │ (~260px) │ (弹性宽度) │ (~280px) │ ├─────────────┼─────────────────────┼──────────────┤ │ 会话导航 │ 标题栏 │ 快捷功能 │ -│ 会话列表 │ 聊天时间轴 │ 收件箱文件 │ -│ 工作区操作 │ 浮动输入框 │ 工件文件 │ +│ 会话列表 │ 聊天时间轴 │ 上传内容 │ +│ 工作区操作 │ 浮动输入框 │ 生成内容 │ └─────────────┴─────────────────────┴──────────────┘ ``` @@ -31,9 +31,10 @@ ## 组件模式 ### 左栏(导航区) -- 按工作区模式分组(远程/本地) -- 可折叠分组项 -- 悬停显示操作按钮 +- 顶部标题栏:会话标题 + 新建会话按钮 +- 会话列表:直接展示所有会话(不区分工作区) +- 底部区域:新建工作区操作 +- 悬停显示删除按钮 ### 中栏(交互区) - 用户消息:圆角气泡 @@ -44,8 +45,8 @@ ### 右栏(信息区) - 功能快捷方式(标签页形式) -- 文件收件箱(上传区 + 文件列表) -- 工件预览(Markdown/图片) +- 上传内容(用户上传的文件) +- 生成内容(模型生成的内容) ## 交互规则 diff --git a/frontend/src/components/layout/CenterCanvas.tsx b/frontend/src/components/layout/CenterCanvas.tsx index ccfc8cc..bbdf4fa 100644 --- a/frontend/src/components/layout/CenterCanvas.tsx +++ b/frontend/src/components/layout/CenterCanvas.tsx @@ -1,55 +1,204 @@ import { IconAt, + IconCheck, IconChevronDown, + IconChevronRight, + IconCode, + IconLoader, IconPaperclip, IconPlayerStop, + IconSearch, IconSend, + IconTerminal, + IconX, } from '@tabler/icons-react'; -import { useState } from 'react'; +import { useCallback, useMemo, useState } from 'react'; +import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from '@/components/ui/collapsible'; import { ScrollArea } from '@/components/ui/scroll-area'; import { cn } from '@/lib/utils'; import { useLayoutStore } from '@/store/appStore'; +type ToolCall = { + id: string; + name: string; + input: string; + output: string; + status: 'running' | 'completed' | 'error'; +}; + type Message = { id: string; role: 'user' | 'assistant'; content: string; timestamp: number; + tools?: ToolCall[]; }; const mockMessages: Message[] = [ { id: '1', role: 'user', - content: '帮我审查这个 Pull Request,检查代码质量和潜在问题。', - timestamp: Date.now() - 60000, + content: '请帮我审查这个 PR:添加 OAuth 2.0 认证功能', + timestamp: Date.now() - 120000, }, { id: '2', role: 'assistant', - content: `好的,我来帮你审查这个 Pull Request。 + content: '好的,我正在审查这个 Pull Request。让我先获取变更内容...', + timestamp: Date.now() - 115000, + tools: [ + { + id: 't1', + name: 'Bash', + input: '{"command": "git diff main...feature/add-oauth"}', + output: 'Found 12 changed files, +342 -89 lines', + status: 'completed', + }, + { + id: 't2', + name: 'Grep', + input: '{"pattern": "TODO|FIXME", "include": "*.ts"}', + output: 'Found 3 matches in 2 files', + status: 'completed', + }, + { + id: 't3', + name: 'Read', + input: '{"filePath": "src/auth/oauth-provider.ts"}', + output: 'Reading file (156 lines)...', + status: 'completed', + }, + ], + }, + { + id: '3', + role: 'assistant', + content: `## 代码审查完成 + +已完成对 12 个文件的审查,发现以下问题: + +### 需要修改 -## 代码审查总结 +1. **src/auth/oauth-provider.ts:45** - 缺少空值检查 + \`token\` 参数可能为 null,建议添加验证: + \`\`\`ts + if (!token) throw new Error('Token is required'); + \`\`\` -### 发现的问题 +2. **src/hooks/useAuth.ts:78** - 内存泄漏风险 + useEffect 中未返回清理函数,可能导致组件卸载后仍在执行异步操作 -1. **潜在空指针异常** - 在 \`src/utils/parser.ts\` 第 45 行,未对输入参数进行空值检查。 +### 建议改进 -2. **性能优化建议** - \`src/components/List.tsx\` 中的 renderItem 回调建议使用 useMemo 包裹以避免不必要的重渲染。 +- **src/utils/jwt.ts** - 建议使用更安全的密钥管理方案 +- **src/middleware/auth.ts** - 错误处理可以更详细 -### 改进建议 +### ✅ 优点 -- 添加类型注释以提高代码可维护性 -- 考虑提取公共逻辑到独立的工具函数中 +- 代码结构清晰,模块化良好 +- 测试覆盖率 87% +- 文档完善 -整体代码质量良好,建议处理上述问题后合并。`, +建议修复以上问题后可以合并。`, timestamp: Date.now() - 30000, }, ]; const modelOptions = ['GPT-4', 'Claude-3', 'DeepSeek']; +const formatTime = (timestamp: number) => { + const date = new Date(timestamp); + return date.toLocaleTimeString('zh-CN', { + hour: '2-digit', + minute: '2-digit', + }); +}; + +const getToolIcon = (toolName: string) => { + switch (toolName) { + case 'Bash': + return IconTerminal; + case 'Read': + case 'Write': + return IconCode; + case 'Grep': + return IconSearch; + default: + return IconCode; + } +}; + +function ToolCallItem({ tool }: { tool: ToolCall }) { + const [isOpen, setIsOpen] = useState(false); + const Icon = getToolIcon(tool.name); + + const StatusIcon = { + completed: IconCheck, + error: IconX, + running: IconLoader, + }[tool.status]; + + return ( + +
+ +
+ {isOpen ? ( + + ) : ( + + )} + + + {tool.name} + + + + +
+
+ +
+
+
+ Input +
+
+                {tool.input}
+              
+
+ {tool.output && ( +
+
+ Output +
+
+                  {tool.output}
+                
+
+ )} +
+
+
+
+ ); +} + export function CenterCanvas() { const { activeConversationId, setInputFocused } = useLayoutStore( (state) => state, @@ -58,18 +207,68 @@ export function CenterCanvas() { const [isGenerating] = useState(false); const [selectedModel] = useState(modelOptions[0]); - const handleSend = () => { + const handleSend = useCallback(() => { if (!inputValue.trim()) return; setInputValue(''); - }; + }, [inputValue]); - const formatTime = (timestamp: number) => { - const date = new Date(timestamp); - return date.toLocaleTimeString('zh-CN', { - hour: '2-digit', - minute: '2-digit', - }); - }; + const handleInputFocus = useCallback( + () => setInputFocused(true), + [setInputFocused], + ); + const handleInputBlur = useCallback( + () => setInputFocused(false), + [setInputFocused], + ); + + const messageElements = useMemo( + () => + mockMessages.map((message) => ( +
+
+ + {message.role === 'user' ? '你' : 'AI 助手'} + + + {formatTime(message.timestamp)} + +
+
+ {message.content.split('\n').map((line, index) => ( +

+ {line || '\u00A0'} +

+ ))} +
+ {message.tools && message.tools.length > 0 && ( +
+ {message.tools.map((tool) => ( + + ))} +
+ )} +
+ )), + [], + ); return (
@@ -84,52 +283,14 @@ export function CenterCanvas() {
- +
{!activeConversationId ? (

选择或创建一个会话开始对话

) : ( -
- {mockMessages.map((message) => ( -
-
- - {message.role === 'user' ? '你' : 'AI 助手'} - - - {formatTime(message.timestamp)} - -
-
- {message.content.split('\n').map((line) => ( -

- {line || '\u00A0'} -

- ))} -
-
- ))} -
+
{messageElements}
)}
@@ -140,8 +301,8 @@ export function CenterCanvas() {