feat: 实现文件浏览器功能

- 添加 /api/files 路由支持目录浏览和文件读取
- 支持 list、read、stat、tree 四个端点
- 安全路径检查防止目录遍历攻击
- 创建 FileBrowser React 组件
- 支持目录导航、文件预览、隐藏文件切换
- 在 Web UI 中添加可切换的文件浏览面板
This commit is contained in:
2025-12-12 11:55:11 +08:00
parent da1773b950
commit 6438ecf2a6
6 changed files with 719 additions and 7 deletions
+42 -6
View File
@@ -5,11 +5,13 @@
import { useState, useEffect } from 'react';
import { Sidebar } from './components/Sidebar';
import { ChatPage } from './pages/Chat';
import { FileBrowser } from './components/FileBrowser';
import { listSessions, createSession, type Session } from './api/client';
export function App() {
const [currentSessionId, setCurrentSessionId] = useState<string | null>(null);
const [isInitializing, setIsInitializing] = useState(true);
const [showFileBrowser, setShowFileBrowser] = useState(false);
// 初始化:加载或创建会话
useEffect(() => {
@@ -62,13 +64,47 @@ export function App() {
onCreateSession={handleCreateSession}
/>
{currentSessionId ? (
<ChatPage key={currentSessionId} sessionId={currentSessionId} />
) : (
<div className="flex-1 flex items-center justify-center">
<p className="text-gray-400">Select or create a session</p>
{/* 文件浏览器切换按钮 */}
<button
onClick={() => setShowFileBrowser(!showFileBrowser)}
className={`absolute top-3 right-4 z-10 p-2 rounded-lg transition-colors ${
showFileBrowser ? 'bg-blue-600 text-white' : 'bg-gray-700 text-gray-300 hover:bg-gray-600'
}`}
title={showFileBrowser ? 'Hide Files' : 'Show Files'}
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z"
/>
</svg>
</button>
<div className="flex-1 flex">
{/* 聊天区域 */}
<div className={`flex-1 ${showFileBrowser ? 'w-1/2' : 'w-full'}`}>
{currentSessionId ? (
<ChatPage key={currentSessionId} sessionId={currentSessionId} />
) : (
<div className="flex-1 flex items-center justify-center h-full">
<p className="text-gray-400">Select or create a session</p>
</div>
)}
</div>
)}
{/* 文件浏览器 */}
{showFileBrowser && (
<div className="w-1/2 border-l border-gray-700">
<FileBrowser
onFileSelect={(path, _content) => {
console.log('Selected file:', path);
}}
/>
</div>
)}
</div>
</div>
);
}