feat: 新增会话历史侧边栏,支持新建/切换/删除/重命名会话

类似 ChatGPT 的侧边栏布局,消息历史持久化到 localStorage。

- 新增 SessionSummary 类型和 localStorage 读写函数
- 新增 useSessionList Hook 管理会话列表 CRUD
- 新增 SessionSidebar 组件(展开/折叠、行内重命名)
- App.tsx 协调 useSessionList 和 useVisionSession
- 消息变化时自动持久化,切换/新建会话时保存并加载
- 新增 i18n 翻译 key(中/英/日)
This commit is contained in:
2026-06-14 15:44:25 +08:00
parent da8818c3be
commit 92f3f45c41
10 changed files with 635 additions and 5 deletions

View File

@@ -81,6 +81,15 @@ export const enUS: TranslationMap = {
"error.INTERNAL_ERROR": "Internal server error, please retry",
"error.unknown": "Unknown error",
// Sidebar
"sidebar.new": "New Chat",
"sidebar.expand": "Expand sidebar",
"sidebar.collapse": "Collapse sidebar",
"sidebar.empty": "No chat history",
"sidebar.messages": " messages",
"sidebar.rename": "Rename",
"sidebar.delete": "Delete",
// Device errors
"error.vadInit": "VAD initialization failed",
"error.cameraAccess": "Cannot access camera",

View File

@@ -81,6 +81,15 @@ export const jaJP: TranslationMap = {
"error.INTERNAL_ERROR": "サーバー内部エラー。再試行してください",
"error.unknown": "不明なエラー",
// Sidebar
"sidebar.new": "新しいチャット",
"sidebar.expand": "サイドバーを展開",
"sidebar.collapse": "サイドバーを折りたたむ",
"sidebar.empty": "会話履歴がありません",
"sidebar.messages": "件のメッセージ",
"sidebar.rename": "名前を変更",
"sidebar.delete": "削除",
// Device errors
"error.vadInit": "VAD初期化に失敗しました",
"error.cameraAccess": "カメラにアクセスできません",

View File

@@ -81,6 +81,15 @@ export const zhCN: TranslationMap = {
"error.INTERNAL_ERROR": "服务内部错误,请重试",
"error.unknown": "未知错误",
// Sidebar
"sidebar.new": "新对话",
"sidebar.expand": "展开侧边栏",
"sidebar.collapse": "收起侧边栏",
"sidebar.empty": "暂无会话记录",
"sidebar.messages": "条消息",
"sidebar.rename": "重命名",
"sidebar.delete": "删除",
// Device errors
"error.vadInit": "VAD 初始化失败",
"error.cameraAccess": "无法访问摄像头",

View File

@@ -3,7 +3,7 @@
// 职责:会话配置持久化
// ============================================================
import type { SessionConfig, Theme } from "../types";
import type { ChatMessage, SessionConfig, SessionSummary, Theme } from "../types";
const CONFIG_KEY = "camtalk:config";
const THEME_KEY = "camtalk:theme";
@@ -52,3 +52,51 @@ export function saveTheme(theme: Theme): void {
localStorage.setItem(THEME_KEY, theme);
} catch { /* ignore */ }
}
// ---- 会话历史存储 ----
const SESSIONS_KEY = "camtalk:sessions";
const SESSION_MSG_PREFIX = "camtalk:session:";
/** 加载所有会话摘要(按 lastActiveAt 倒序) */
export function loadSessionSummaries(): SessionSummary[] {
try {
const raw = localStorage.getItem(SESSIONS_KEY);
if (!raw) return [];
return (JSON.parse(raw) as SessionSummary[]).sort((a, b) => b.lastActiveAt - a.lastActiveAt);
} catch {
return [];
}
}
/** 保存会话摘要列表 */
export function saveSessionSummaries(sessions: SessionSummary[]): void {
try {
localStorage.setItem(SESSIONS_KEY, JSON.stringify(sessions));
} catch { /* ignore */ }
}
/** 加载单个会话的消息历史 */
export function loadSessionMessages(sessionId: string): ChatMessage[] {
try {
const raw = localStorage.getItem(SESSION_MSG_PREFIX + sessionId);
if (!raw) return [];
return JSON.parse(raw) as ChatMessage[];
} catch {
return [];
}
}
/** 保存单个会话的消息历史 */
export function saveSessionMessages(sessionId: string, messages: ChatMessage[]): void {
try {
localStorage.setItem(SESSION_MSG_PREFIX + sessionId, JSON.stringify(messages));
} catch { /* ignore */ }
}
/** 删除单个会话的消息历史 */
export function deleteSessionMessages(sessionId: string): void {
try {
localStorage.removeItem(SESSION_MSG_PREFIX + sessionId);
} catch { /* ignore */ }
}