Spaces:
Runtime error
Runtime error
File size: 7,266 Bytes
204e35b ddac6a2 e1fb264 204e35b ddac6a2 204e35b e1fb264 204e35b ddac6a2 204e35b ddac6a2 f2fe7c8 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
import React, { useState } from 'react';
function ChatList({ chats, currentChat, onSelectChat, onDeleteChat, onCreateNewChat, collapsed, isStreamingChat }) {
const [hoveredChat, setHoveredChat] = useState(null);
const formatDate = (timestamp) => {
const date = new Date(timestamp);
const now = new Date();
const diffTime = Math.abs(now - date);
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
if (diffDays === 1) return 'Today';
if (diffDays === 2) return 'Yesterday';
if (diffDays <= 7) return `${diffDays} days ago`;
return date.toLocaleDateString();
};
const truncateTitle = (title, maxLength = 25) => {
if (title.length <= maxLength) return title;
return title.substring(0, maxLength) + '...';
};
return (
<div className="flex flex-col h-full bg-background-elevated">
{/* New Chat Button */}
<div className="p-3 border-b border-border">
<button
className="w-full flex items-center justify-center gap-2 py-3 px-4 bg-primary hover:bg-primary-hover text-white border-none rounded-xl cursor-pointer text-sm font-medium transition-all duration-fast shadow-sm hover:shadow-md active:scale-[0.98]"
onClick={onCreateNewChat}
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
</svg>
<span>New Conversation</span>
</button>
</div>
{/* Chat List */}
<div className="flex-1 overflow-y-auto p-2">
{chats.length === 0 ? (
<div className="flex flex-col items-center justify-center py-12 px-4 text-center">
<div className="w-16 h-16 bg-background-secondary rounded-full flex items-center justify-center mb-4">
<svg className="w-8 h-8 text-muted" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
</svg>
</div>
<h3 className="text-sm font-medium text-secondary mb-2">No conversations yet</h3>
<p className="text-xs text-muted">Start a new conversation to get started</p>
</div>
) : (
<div className="space-y-1">
{chats.map(chat => {
const isActive = chat.id === currentChat?.id;
const isStreaming = isStreamingChat && typeof isStreamingChat === 'function' && isStreamingChat(chat.id);
const isHovered = hoveredChat === chat.id;
return (
<div
key={chat.id}
className={`group relative flex items-center p-3 rounded-lg cursor-pointer transition-all duration-fast ${
isActive
? 'bg-primary-light border border-primary/20 shadow-sm'
: 'hover:bg-background-secondary border border-transparent'
}`}
onClick={() => onSelectChat(chat.id)}
onMouseEnter={() => setHoveredChat(chat.id)}
onMouseLeave={() => setHoveredChat(null)}
>
{/* Streaming indicator */}
{isStreaming && (
<div className="absolute left-0 top-0 bottom-0 w-1 bg-gradient-to-b from-info to-primary rounded-r-full animate-pulse" />
)}
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-1">
{/* Chat icon */}
<div className={`flex-shrink-0 w-8 h-8 rounded-lg flex items-center justify-center ${
isActive ? 'bg-primary text-white' : 'bg-background-tertiary text-muted'
}`}>
{isStreaming ? (
<div className="w-2 h-2 bg-current rounded-full animate-pulse" />
) : (
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
</svg>
)}
</div>
{/* Chat title */}
<div className="flex-1 min-w-0">
<h4 className={`text-sm font-medium truncate ${
isActive ? 'text-primary' : 'text-secondary'
}`}>
{truncateTitle(chat.title)}
</h4>
{/* Message count and date */}
<div className="flex items-center justify-between mt-1">
<span className="text-xs text-muted">
{chat.messages?.length || 0} messages
</span>
{chat.messages?.length > 0 && (
<span className="text-xs text-muted">
{formatDate(chat.messages[chat.messages.length - 1]?.timestamp || Date.now())}
</span>
)}
</div>
</div>
</div>
</div>
{/* Delete button */}
<button
className={`flex-shrink-0 ml-2 p-1.5 rounded-md transition-all duration-fast ${
isHovered || isActive
? 'opacity-100 hover:bg-error-light hover:text-error'
: 'opacity-0 group-hover:opacity-100'
}`}
onClick={(e) => {
e.stopPropagation();
if (window.confirm('Are you sure you want to delete this conversation?')) {
onDeleteChat(chat.id);
}
}}
title="Delete conversation"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</button>
</div>
);
})}
</div>
)}
</div>
{/* Footer */}
<div className="p-3 border-t border-border">
<div className="text-xs text-muted text-center">
{chats.length} conversation{chats.length !== 1 ? 's' : ''}
</div>
</div>
</div>
);
}
export default ChatList;
|