bad7bfcc36
- 新增 Server MCP API 路由 (/api/mcp/*) - GET /servers - 获取所有服务器状态 - POST /servers/:name/connect|disconnect|enable|disable - GET /tools - 获取所有 MCP 工具 - GET /config - 获取 MCP 配置 - 新增 UI MCPPanel 组件 - 显示服务器列表和状态指示灯 - 支持连接/断开/启用/禁用操作 - 展开查看服务器配置和工具列表 - 响应式设计支持移动端 - 集成到 Web 和 Desktop - 添加 Plug 图标按钮到工具栏 - 点击打开 MCP 管理面板
132 lines
3.8 KiB
TypeScript
132 lines
3.8 KiB
TypeScript
/**
|
|
* App Component
|
|
*/
|
|
|
|
import { useState, useEffect, useCallback } from 'react';
|
|
import {
|
|
Sidebar,
|
|
FileBrowser,
|
|
ConfigPanel,
|
|
CommandPanel,
|
|
MCPPanel,
|
|
Toaster,
|
|
listSessions,
|
|
createSession,
|
|
type Session,
|
|
} from '@ai-assistant/ui';
|
|
import { ChatPage } from './pages/Chat';
|
|
|
|
export function App() {
|
|
const [currentSessionId, setCurrentSessionId] = useState<string | null>(null);
|
|
const [isInitializing, setIsInitializing] = useState(true);
|
|
const [showFileBrowser, setShowFileBrowser] = useState(false);
|
|
const [showConfig, setShowConfig] = useState(false);
|
|
const [showCommands, setShowCommands] = useState(false);
|
|
const [showMCP, setShowMCP] = useState(false);
|
|
const [sessionTitleUpdate, setSessionTitleUpdate] = useState<{ sessionId: string; name: string } | null>(null);
|
|
|
|
// 初始化:加载或创建会话
|
|
useEffect(() => {
|
|
async function init() {
|
|
try {
|
|
const { data: sessions } = await listSessions();
|
|
|
|
if (sessions.length > 0) {
|
|
// 选择最近的会话
|
|
setCurrentSessionId(sessions[0].id);
|
|
} else {
|
|
// 创建新会话
|
|
const { data: newSession } = await createSession();
|
|
setCurrentSessionId(newSession.id);
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to initialize:', error);
|
|
} finally {
|
|
setIsInitializing(false);
|
|
}
|
|
}
|
|
|
|
init();
|
|
}, []);
|
|
|
|
const handleSelectSession = (id: string) => {
|
|
setCurrentSessionId(id);
|
|
};
|
|
|
|
const handleCreateSession = (session: Session) => {
|
|
setCurrentSessionId(session.id);
|
|
};
|
|
|
|
// 会话标题更新回调
|
|
const handleSessionUpdated = useCallback((sessionId: string, name: string) => {
|
|
setSessionTitleUpdate({ sessionId, name });
|
|
}, []);
|
|
|
|
if (isInitializing) {
|
|
return (
|
|
<div className="h-screen flex items-center justify-center bg-gray-900">
|
|
<div className="text-center">
|
|
<div className="w-8 h-8 border-2 border-primary-500 border-t-transparent rounded-full animate-spin mx-auto mb-4" />
|
|
<p className="text-gray-400">Initializing...</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="h-screen flex bg-gray-900">
|
|
<Sidebar
|
|
currentSessionId={currentSessionId}
|
|
onSelectSession={handleSelectSession}
|
|
onCreateSession={handleCreateSession}
|
|
sessionTitleUpdate={sessionTitleUpdate}
|
|
/>
|
|
|
|
<div className="flex-1 flex">
|
|
{/* 聊天区域 */}
|
|
<div className={`flex-1 ${showFileBrowser ? 'w-1/2' : 'w-full'}`}>
|
|
{currentSessionId ? (
|
|
<ChatPage
|
|
key={currentSessionId}
|
|
sessionId={currentSessionId}
|
|
onSessionUpdated={handleSessionUpdated}
|
|
showFileBrowser={showFileBrowser}
|
|
onToggleFileBrowser={() => setShowFileBrowser(!showFileBrowser)}
|
|
onOpenConfig={() => setShowConfig(true)}
|
|
onOpenCommands={() => setShowCommands(true)}
|
|
onOpenMCP={() => setShowMCP(true)}
|
|
/>
|
|
) : (
|
|
<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>
|
|
|
|
{/* 配置面板 */}
|
|
{showConfig && <ConfigPanel onClose={() => setShowConfig(false)} />}
|
|
|
|
{/* 命令面板 */}
|
|
{showCommands && <CommandPanel onClose={() => setShowCommands(false)} />}
|
|
|
|
{/* MCP 面板 */}
|
|
{showMCP && <MCPPanel onClose={() => setShowMCP(false)} />}
|
|
|
|
{/* Toast 通知 */}
|
|
<Toaster />
|
|
</div>
|
|
);
|
|
}
|