From 33e428cc945a98730c5f4d2a53b7ab9e7a1e9377 Mon Sep 17 00:00:00 2001 From: duanshuwen Date: Fri, 17 Apr 2026 23:36:43 +0800 Subject: [PATCH] feat: update ChatHistoryPanel with improved state management and UI enhancements --- dist/index.html | 4 +- src/components/chat/ChatHistoryPanel.tsx | 322 ++++++++++++++++++----- src/pages/Home/index.tsx | 292 ++++++++++---------- 3 files changed, 409 insertions(+), 209 deletions(-) diff --git a/dist/index.html b/dist/index.html index c19a495..d53804b 100644 --- a/dist/index.html +++ b/dist/index.html @@ -8,8 +8,8 @@ http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: http://8.138.234.141 https://one-feel-bucket.oss-cn-guangzhou.aliyuncs.com; connect-src 'self' http://8.138.234.141 https://api.iconify.design wss://onefeel.brother7.cn" /> - - + +
diff --git a/src/components/chat/ChatHistoryPanel.tsx b/src/components/chat/ChatHistoryPanel.tsx index 5195b38..0cf2cc4 100644 --- a/src/components/chat/ChatHistoryPanel.tsx +++ b/src/components/chat/ChatHistoryPanel.tsx @@ -1,4 +1,17 @@ +import { useEffect, useRef, useState } from 'react'; import type { ChatHistoryBucket } from './types'; +import { + ChevronDown, + LoaderCircle, + Plus, + MoreHorizontal, + PanelLeftClose, + PanelLeftOpen, + PencilLine, + Trash2, +} from 'lucide-react'; + +import blueLogo from '../../assets/images/login/blue_logo.png'; type ChatHistoryPanelProps = { buckets: ChatHistoryBucket[]; @@ -10,6 +23,14 @@ type ChatHistoryPanelProps = { onDeleteConversation?: (conversationId: string) => void; }; +type MenuState = { + conversationId: string; +} | null; + +function cx(...classes: Array): string { + return classes.filter(Boolean).join(' '); +} + export default function ChatHistoryPanel({ buckets, selectedConversationId, @@ -19,88 +40,265 @@ export default function ChatHistoryPanel({ onRenameConversation, onDeleteConversation, }: ChatHistoryPanelProps) { + const panelRef = useRef(null); + const [collapsedBuckets, setCollapsedBuckets] = useState>({}); + const [menuState, setMenuState] = useState(null); + const [isCompact, setIsCompact] = useState(false); + const hasSessions = buckets.some((bucket) => bucket.sessions.length > 0); + useEffect(() => { + setCollapsedBuckets((current) => { + const next: Record = {}; + + for (const bucket of buckets) { + next[bucket.key] = current[bucket.key] ?? false; + } + + return next; + }); + }, [buckets]); + + useEffect(() => { + if (!menuState) { + return; + } + + const handlePointerDown = (event: PointerEvent) => { + const target = event.target as HTMLElement | null; + if (!target) { + setMenuState(null); + return; + } + + if (target.closest('[data-chat-history-menu="true"]')) { + return; + } + + if (!panelRef.current?.contains(target)) { + setMenuState(null); + return; + } + + if (!target.closest('[data-chat-history-menu-toggle="true"]')) { + setMenuState(null); + } + }; + + const handleEscape = (event: KeyboardEvent) => { + if (event.key === 'Escape') { + setMenuState(null); + } + }; + + document.addEventListener('pointerdown', handlePointerDown); + document.addEventListener('keydown', handleEscape); + + return () => { + document.removeEventListener('pointerdown', handlePointerDown); + document.removeEventListener('keydown', handleEscape); + }; + }, [menuState]); + + const panelWidthClass = isCompact ? 'md:w-[96px] lg:w-[96px]' : 'md:w-[240px] lg:w-[252px]'; + return ( -