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() {