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);
}