Spaces:
Sleeping
Sleeping
| import { useConversation } from '../context/ConversationContext'; | |
| function formatTime(ts) { | |
| const d = new Date(ts); | |
| const now = new Date(); | |
| const diff = now - d; | |
| if (diff < 60000) return 'Just now'; | |
| if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`; | |
| if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`; | |
| return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); | |
| } | |
| export default function Sidebar({ collapsed, onToggle }) { | |
| const { | |
| state, | |
| createConversation, | |
| deleteConversation, | |
| setActiveConversation, | |
| } = useConversation(); | |
| const conversations = Object.values(state.conversations).sort( | |
| (a, b) => b.updatedAt - a.updatedAt | |
| ); | |
| const statusLabel = { | |
| unloaded: 'Model not loaded', | |
| loading: `Loading model… ${state.modelLoadProgress}%`, | |
| ready: 'Model ready', | |
| error: 'Model error', | |
| }; | |
| return ( | |
| <aside className={`sidebar ${collapsed ? 'collapsed' : ''}`}> | |
| <div className="sidebar-header"> | |
| <div className="sidebar-logo"> | |
| <span className="logo-icon">⑂</span> | |
| BranchChat | |
| </div> | |
| </div> | |
| <button className="new-chat-btn" onClick={() => createConversation()}> | |
| + New Chat | |
| </button> | |
| <div className="conversation-list"> | |
| {conversations.length === 0 && ( | |
| <div style={{ padding: '20px 12px', color: 'var(--text-tertiary)', fontSize: '12px', textAlign: 'center' }}> | |
| No conversations yet | |
| </div> | |
| )} | |
| {conversations.map((conv) => ( | |
| <div | |
| key={conv.id} | |
| className={`conversation-item ${ | |
| state.activeConversationId === conv.id ? 'active' : '' | |
| }`} | |
| onClick={() => setActiveConversation(conv.id)} | |
| > | |
| <span className="conversation-item-title"> | |
| {conv.title || 'New Chat'} | |
| </span> | |
| <span className="conversation-item-time"> | |
| {formatTime(conv.updatedAt)} | |
| </span> | |
| <button | |
| className="conversation-item-delete" | |
| onClick={(e) => { | |
| e.stopPropagation(); | |
| if (window.confirm('Delete this conversation?')) { | |
| deleteConversation(conv.id); | |
| } | |
| }} | |
| title="Delete" | |
| > | |
| ✕ | |
| </button> | |
| </div> | |
| ))} | |
| </div> | |
| <div className="sidebar-footer"> | |
| <span | |
| className={`model-status-dot ${state.modelLoadStatus}`} | |
| /> | |
| <span>{statusLabel[state.modelLoadStatus]}</span> | |
| </div> | |
| </aside> | |
| ); | |
| } | |