工作階段管理
建立具有工作階段列表和載入功能的對話歷史 UI。
管理對話歷史,讓使用者可以檢視過去的對話並從上次中斷的地方繼續。
概觀
Lens OS 會自動儲存對話。你可以使用 Sessions API 讓使用者:
- 檢視過去對話的列表
- 載入並繼續先前的對話
- 開始新的對話
歷史側邊欄狀態
狀態管理
interface Session {
id: string;
title: string;
messageCount: number;
created_at: string;
}
const [sessions, setSessions] = useState<Session[]>([]);
const [showHistory, setShowHistory] = useState(false);
const [loadingHistory, setLoadingHistory] = useState(false);取得工作階段
載入工作階段列表
const loadSessions = async () => {
setLoadingHistory(true);
try {
const res = await fetch("/api/lens/agent/sessions");
if (res.ok) {
const data = await res.json();
if (data.success) {
setSessions(data.sessions);
}
}
} finally {
setLoadingHistory(false);
}
};
// Load when opening sidebar
const toggleHistory = () => {
const opening = !showHistory;
setShowHistory(opening);
if (opening) loadSessions();
};載入工作階段
載入並還原對話
const { loadSession, sessionId } = useLensAgent({...});
const handleLoadSession = async (targetId: string) => {
const res = await fetch(`/api/lens/agent/sessions/${targetId}`);
if (!res.ok) return;
const data = await res.json();
if (data.success) {
// Messages may need to be reversed
loadSession(targetId, data.messages.reverse());
setShowHistory(false);
}
};格式化日期
日期格式化輔助函式
const formatDate = (dateStr: string, id?: string) => {
let date = new Date(dateStr);
// Fallback: extract timestamp from session ID
if (isNaN(date.getTime()) && id) {
const match = id.match(/^session-(\d+)-/);
if (match) date = new Date(Number(match[1]));
}
if (isNaN(date.getTime())) return "";
const now = new Date();
const diffDays = Math.floor(
(now.getTime() - date.getTime()) / (1000 * 60 * 60 * 24)
);
if (diffDays === 0) return "今天";
if (diffDays === 1) return "昨天";
if (diffDays < 7) return `${diffDays} 天前`;
return date.toLocaleDateString("zh-TW", {
month: "short",
day: "numeric",
});
};UI 實作
歷史側邊欄
{/* History Button */}
<button onClick={toggleHistory}>
<svg /* icon */ />
歷史
</button>
{/* Sidebar Backdrop */}
{showHistory && (
<div
className="fixed inset-0 bg-black/20 z-40"
onClick={() => setShowHistory(false)}
/>
)}
{/* Sidebar */}
<div className={`
fixed top-0 left-0 h-full w-72 bg-white shadow-lg z-50
transform transition-transform
${showHistory ? "translate-x-0" : "-translate-x-full"}
`}>
{/* Header */}
<div className="flex items-center justify-between p-4 border-b">
<span className="font-semibold">對話歷史</span>
<button onClick={() => setShowHistory(false)}>✕</button>
</div>
{/* List */}
<div className="overflow-auto h-[calc(100%-60px)]">
{loadingHistory ? (
<div className="p-4 text-center text-gray-400">載入中...</div>
) : sessions.length === 0 ? (
<div className="p-4 text-center text-gray-400">沒有對話歷史</div>
) : (
sessions.map((s) => (
<div
key={s.id}
className={`
p-4 border-b cursor-pointer hover:bg-gray-50
${s.id === sessionId ? "bg-blue-50" : ""}
`}
onClick={() => handleLoadSession(s.id)}
>
<div className="font-medium truncate">
{s.title || "新對話"}
</div>
<div className="text-xs text-gray-400 mt-1 flex justify-between">
<span>{formatDate(s.created_at, s.id)}</span>
<span>{s.messageCount} 則訊息</span>
</div>
</div>
))
)}
</div>
</div>新工作階段
開始新對話
const { newSession } = useLensAgent({...});
const handleNewSession = () => {
newSession();
// Clear tool cards and other state
setToolCards([]);
};完整範例
完整實作
export function LensChat({ userId }: { userId: string }) {
const [showHistory, setShowHistory] = useState(false);
const [sessions, setSessions] = useState<Session[]>([]);
const [loadingHistory, setLoadingHistory] = useState(false);
const {
messages,
sessionId,
loadSession,
newSession,
// ...
} = useLensAgent({ endpoint: "/api/lens/agent/chat", userId });
const loadSessions = async () => {
setLoadingHistory(true);
const res = await fetch("/api/lens/agent/sessions");
const data = await res.json();
if (data.success) setSessions(data.sessions);
setLoadingHistory(false);
};
const toggleHistory = () => {
setShowHistory(!showHistory);
if (!showHistory) loadSessions();
};
const handleLoadSession = async (targetId: string) => {
const res = await fetch(`/api/lens/agent/sessions/${targetId}`);
const data = await res.json();
if (data.success) {
loadSession(targetId, data.messages.reverse());
setShowHistory(false);
}
};
return (
<div className="relative">
{/* Main Chat UI */}
{/* ... */}
{/* History Sidebar */}
{/* ... */}
</div>
);
}Note
工作階段由 Lens OS 自動管理。你不需要手動處理工作階段的建立或儲存。