From c31c99104a892bbf439792191f50ec86fff04cdc Mon Sep 17 00:00:00 2001 From: occupy5 <1370275510@qq.com> Date: Wed, 1 Apr 2026 16:54:26 +0800 Subject: [PATCH 1/3] feat: update tabs --- frontend/src/components/layout/RightRail.tsx | 48 ++++++++++---------- frontend/src/store/slices/layoutSlice.ts | 23 ++-------- 2 files changed, 29 insertions(+), 42 deletions(-) diff --git a/frontend/src/components/layout/RightRail.tsx b/frontend/src/components/layout/RightRail.tsx index 6822ac5..9565581 100644 --- a/frontend/src/components/layout/RightRail.tsx +++ b/frontend/src/components/layout/RightRail.tsx @@ -2,8 +2,8 @@ import { IconBolt, IconFile, IconFileText, - IconInbox, IconPhoto, + IconSparkles, IconUpload, } from '@tabler/icons-react'; import { Button } from '@/components/ui/button'; @@ -54,29 +54,29 @@ const mockArtifacts: ArtifactFile[] = [ }, ]; +const tabs = [ + { + id: 'shortcuts' as const, + label: '操作', + icon: , + }, + { + id: 'uploads' as const, + label: '上传', + icon: , + }, + { + id: 'generated' as const, + label: '生成', + icon: , + }, +]; + export function RightRail() { const { activeRightTab, setActiveRightTab } = useLayoutStore( (state) => state, ); - const tabs = [ - { - id: 'shortcuts' as const, - label: '快捷', - icon: , - }, - { - id: 'inbox' as const, - label: '收件箱', - icon: , - }, - { - id: 'artifacts' as const, - label: '工件', - icon: , - }, - ]; - return (
@@ -119,10 +119,10 @@ export function RightRail() {
)} - {activeRightTab === 'inbox' && ( + {activeRightTab === 'uploads' && (

- 文件收件箱 + 上传内容

@@ -140,10 +140,10 @@ export function RightRail() {
)} - {activeRightTab === 'artifacts' && ( + {activeRightTab === 'generated' && (

- 工件文件 + 模型生成内容

{mockArtifacts.map((artifact) => (
- 暂无工件 + 暂无生成内容

)}
diff --git a/frontend/src/store/slices/layoutSlice.ts b/frontend/src/store/slices/layoutSlice.ts index c3a9c21..a53d3c4 100644 --- a/frontend/src/store/slices/layoutSlice.ts +++ b/frontend/src/store/slices/layoutSlice.ts @@ -1,8 +1,6 @@ import type { SliceCreator } from '../types'; import { flattenActions } from '../utils'; -export type WorkspaceMode = 'remote' | 'local'; - export type Conversation = { id: string; title: string; @@ -14,7 +12,6 @@ export type Conversation = { export type Workspace = { id: string; name: string; - mode: WorkspaceMode; collapsed: boolean; }; @@ -26,7 +23,7 @@ export type LayoutState = { workspaces: Workspace[]; conversations: Conversation[]; inputFocused: boolean; - activeRightTab: 'shortcuts' | 'inbox' | 'artifacts'; + activeRightTab: 'shortcuts' | 'uploads' | 'generated'; }; export type LayoutAction = Pick; @@ -37,32 +34,22 @@ const _initialState: LayoutState = { rightRailCollapsed: false, activeConversationId: null, activeWorkspaceId: null, - workspaces: [ - { id: 'remote-1', name: '远程工作区', mode: 'remote', collapsed: false }, - { id: 'local-1', name: '本地工作区', mode: 'local', collapsed: false }, - ], + workspaces: [{ id: 'ws-1', name: '工作区', collapsed: false }], conversations: [ { id: 'conv-1', title: '代码审查讨论', - workspaceId: 'remote-1', + workspaceId: 'ws-1', createdAt: Date.now(), updatedAt: Date.now(), }, { id: 'conv-2', title: '项目规划', - workspaceId: 'remote-1', + workspaceId: 'ws-1', createdAt: Date.now() - 3600000, updatedAt: Date.now() - 3600000, }, - { - id: 'conv-3', - title: '本地测试会话', - workspaceId: 'local-1', - createdAt: Date.now() - 7200000, - updatedAt: Date.now() - 7200000, - }, ], inputFocused: false, activeRightTab: 'shortcuts', @@ -141,7 +128,7 @@ export class LayoutActionImpl { this.#set({ inputFocused: focused }); }; - setActiveRightTab = (tab: 'shortcuts' | 'inbox' | 'artifacts') => { + setActiveRightTab = (tab: 'shortcuts' | 'uploads' | 'generated') => { this.#set({ activeRightTab: tab }); }; } From 0d7711ea3b7b2061bb1aa9d7730de92e394b952a Mon Sep 17 00:00:00 2001 From: occupy5 <1370275510@qq.com> Date: Wed, 1 Apr 2026 16:55:19 +0800 Subject: [PATCH 2/3] feat: add tools --- .../src/components/layout/CenterCanvas.tsx | 287 ++++++++++++++---- frontend/src/components/layout/LeftRail.tsx | 99 ++---- frontend/src/components/ui/scroll-area.tsx | 5 +- 3 files changed, 255 insertions(+), 136 deletions(-) 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() {