feat: 多功能升级 — 流式逐字渲染、对话缓存、会话组织优化、记忆管理修复、性能仪表盘
- 前端消息流式逐字渲染 (AI-Core ChatStream → SSE → Gateway → WebSocket stream_chunk → fadeInUp + cursorBlink) - 后端对话缓存 (conversationCache sync.Map, GET /sessions/:id/messages) - 前端侧边栏历史多轮对话显示 - DevTools 性能监控图标移至首页仪表盘 - DevTools 用户记忆查询/删减功能修复 (补全 DELETE 数据链路) - 后端和 DevTools 按用户分类组织实时活动会话 (map[userID]map[sessionID]*Client) - 新增 docs/api-reference/ 路由参考文档 - 新增 docs/message-flow-architecture.md 消息链路架构文档
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import { useSession } from '@/hooks/useSession';
|
||||
import { useSessionStore } from '@/store/sessionStore';
|
||||
import { useEffect } from 'react';
|
||||
import { CyreneAvatar } from '@/components/persona/CyreneAvatar';
|
||||
|
||||
interface SidebarProps {
|
||||
@@ -8,20 +7,51 @@ interface SidebarProps {
|
||||
}
|
||||
|
||||
export function Sidebar({ onClose }: SidebarProps) {
|
||||
const { sessions, currentSessionId, loadSessions, createSession, deleteSession, setCurrentSession } = useSession();
|
||||
const {
|
||||
sessions,
|
||||
currentSessionId,
|
||||
createSession,
|
||||
deleteSession,
|
||||
setCurrentSession,
|
||||
} = useSession();
|
||||
const storeSessions = useSessionStore((s) => s.sessions);
|
||||
|
||||
useEffect(() => {
|
||||
loadSessions();
|
||||
}, [loadSessions]);
|
||||
const storeCurrentSessionId = useSessionStore((s) => s.currentSessionId);
|
||||
|
||||
const displaySessions = sessions.length > 0 ? sessions : storeSessions;
|
||||
const activeSessionId = currentSessionId || storeCurrentSessionId;
|
||||
|
||||
const handleNewChat = async () => {
|
||||
const session = await createSession();
|
||||
if (session && onClose) onClose();
|
||||
};
|
||||
|
||||
const handleSelectSession = (id: string) => {
|
||||
setCurrentSession(id);
|
||||
if (onClose) onClose();
|
||||
};
|
||||
|
||||
const handleDeleteSession = (e: { stopPropagation: () => void }, id: string) => {
|
||||
e.stopPropagation();
|
||||
deleteSession(id);
|
||||
};
|
||||
|
||||
/** 格式化时间戳为可读字符串 */
|
||||
const formatTime = (ts: string | number): string => {
|
||||
if (!ts) return '';
|
||||
const date = new Date(typeof ts === 'string' ? parseInt(ts, 10) : ts);
|
||||
if (isNaN(date.getTime())) return '';
|
||||
const now = new Date();
|
||||
const diffMs = now.getTime() - date.getTime();
|
||||
const diffMin = Math.floor(diffMs / 60000);
|
||||
if (diffMin < 1) return '刚刚';
|
||||
if (diffMin < 60) return `${diffMin}分钟前`;
|
||||
const diffHour = Math.floor(diffMin / 60);
|
||||
if (diffHour < 24) return `${diffHour}小时前`;
|
||||
const diffDay = Math.floor(diffHour / 24);
|
||||
if (diffDay < 7) return `${diffDay}天前`;
|
||||
return date.toLocaleDateString('zh-CN');
|
||||
};
|
||||
|
||||
return (
|
||||
<aside className="h-full bg-white/90 dark:bg-gray-900/90 border-r border-pink-100 dark:border-pink-900 flex flex-col">
|
||||
{/* 侧边栏头部 */}
|
||||
@@ -45,14 +75,11 @@ export function Sidebar({ onClose }: SidebarProps) {
|
||||
displaySessions.map((session) => (
|
||||
<div
|
||||
key={session.id}
|
||||
onClick={() => {
|
||||
setCurrentSession(session.id);
|
||||
if (onClose) onClose();
|
||||
}}
|
||||
onClick={() => handleSelectSession(session.id)}
|
||||
className={`
|
||||
group flex items-center justify-between px-4 py-2.5 mx-2 rounded-lg cursor-pointer transition-colors
|
||||
${
|
||||
currentSessionId === session.id || session.id === useSessionStore.getState().currentSessionId
|
||||
activeSessionId === session.id
|
||||
? 'bg-pink-50 dark:bg-pink-900/30 text-pink-600'
|
||||
: 'hover:bg-gray-50 dark:hover:bg-gray-800 text-gray-600 dark:text-gray-300'
|
||||
}
|
||||
@@ -63,15 +90,13 @@ export function Sidebar({ onClose }: SidebarProps) {
|
||||
<div className="min-w-0">
|
||||
<p className="text-sm font-medium truncate">{session.title || '新的对话'}</p>
|
||||
<p className="text-xs text-gray-400 truncate">
|
||||
{session.message_count || 0} 条消息
|
||||
{session.message_count != null ? `${session.message_count} 条消息` : ''}
|
||||
{session.updated_at ? ` · ${formatTime(session.updated_at)}` : ''}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
deleteSession(session.id);
|
||||
}}
|
||||
onClick={(e) => handleDeleteSession(e, session.id)}
|
||||
className="opacity-0 group-hover:opacity-100 p-1 text-gray-400 hover:text-red-400 transition-all"
|
||||
title="删除会话"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user