import { useState, useCallback, useEffect, useMemo } from "react"; import type { ACPClient } from "../src/acp/client"; import type { AgentSessionInfo } from "../src/acp/types"; import { ChatInterface } from "./ChatInterface"; import { cn } from "../src/lib/utils"; import { MessageSquare, Plus, PanelLeftClose, PanelLeft } from "lucide-react"; interface ACPMainProps { client: ACPClient; agentId?: string; } /** * Main container — Anthropic sidebar + chat layout. * Sidebar: sectioned by recency, orange active state, warm raised bg. */ export function ACPMain({ client, agentId }: ACPMainProps) { const [sidebarCollapsed, setSidebarCollapsed] = useState(false); // Handle session selection const handleSelectSession = useCallback(async (session: AgentSessionInfo) => { try { if (client.supportsLoadSession) { await client.loadSession({ sessionId: session.sessionId, cwd: session.cwd }); } else if (client.supportsResumeSession) { await client.resumeSession({ sessionId: session.sessionId, cwd: session.cwd }); } else { throw new Error("Loading or resuming sessions is not supported by this agent."); } } catch (error) { console.error("Failed to load/resume session:", error); } }, [client]); return (
{/* 侧边栏 — Anthropic warm sidebar, hidden on mobile */}
{/* 头部 */}
{!sidebarCollapsed && ( 会话 )}
{!sidebarCollapsed && ( )}
{/* 会话列表 */} {!sidebarCollapsed && (
)}
{/* 聊天区域 */}
); } // ============================================================================= // 侧边栏会话列表 — Anthropic 分段式(今天/昨天/更早) // ============================================================================= function SidebarSessionList({ client, onSelectSession, }: { client: ACPClient; onSelectSession: (session: AgentSessionInfo) => void; }) { const [sessions, setSessions] = useState([]); const [loading, setLoading] = useState(true); const [activeId, setActiveId] = useState(null); const loadSessions = useCallback(async () => { if (!client.supportsSessionList) { setLoading(false); return; } setLoading(true); try { const response = await client.listSessions(); setSessions(response.sessions); } catch (err) { console.warn("[SidebarSessionList] Failed to load:", err); } finally { setLoading(false); } }, [client]); useEffect(() => { if (client.getState() === "connected" && client.supportsSessionList) { loadSessions(); } }, [client, loadSessions]); useEffect(() => { const handler = (state: string) => { if (state === "connected") { setTimeout(loadSessions, 200); } }; client.setConnectionStateHandler(handler); return () => client.removeConnectionStateHandler(handler); }, [client, loadSessions]); useEffect(() => { const interval = setInterval(loadSessions, 10000); return () => clearInterval(interval); }, [loadSessions]); const sorted = useMemo( () => [...sessions].sort((a, b) => { const dateA = a.updatedAt ? new Date(a.updatedAt).getTime() : 0; const dateB = b.updatedAt ? new Date(b.updatedAt).getTime() : 0; return dateB - dateA; }), [sessions], ); if (loading && sessions.length === 0) { return (
加载中...
); } if (sessions.length === 0) { return (
暂无会话
); } // 按日期分组 const groups = groupByRecency(sorted); return ( ); } // ============================================================================= // 按日期分组:今天 / 昨天 / 更早 // ============================================================================= interface SessionGroup { label: string; sessions: AgentSessionInfo[]; } function groupByRecency(sessions: AgentSessionInfo[]): SessionGroup[] { const now = new Date(); const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); const yesterday = new Date(today.getTime() - 86400000); const groups: SessionGroup[] = [ { label: "今天", sessions: [] }, { label: "昨天", sessions: [] }, { label: "更早", sessions: [] }, ]; for (const session of sessions) { const date = session.updatedAt ? new Date(session.updatedAt) : new Date(0); if (date >= today) { groups[0].sessions.push(session); } else if (date >= yesterday) { groups[1].sessions.push(session); } else { groups[2].sessions.push(session); } } return groups.filter((g) => g.sessions.length > 0); }