| export default function Sidebar({ |
| open, sessions, currentSessionId, searchQ, |
| onSearchChange, onNewSession, onSelectSession, onDeleteSession, |
| onFeedback, onSettings, |
| }) { |
| return ( |
| <aside className={`sidebar ${open ? '' : 'collapsed'}`}> |
| {/* Logo */} |
| <div className="sidebar-logo"> |
| <div className="sidebar-logo-icon">✦</div> |
| <div className="sidebar-logo-text"> |
| <span className="sidebar-logo-name">OwnGPT</span> |
| <span className="sidebar-logo-version">v2 · Private AI</span> |
| </div> |
| </div> |
| |
| {/* New chat */} |
| <div className="sidebar-top"> |
| <button className="btn-new-chat" onClick={onNewSession} title="New Conversation"> |
| <svg width="14" height="14" viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg> |
| <span className="btn-new-chat-text">New Conversation</span> |
| </button> |
| </div> |
| |
| {/* Search */} |
| <div className="sidebar-search" title="Search"> |
| <input |
| className="search-box" |
| type="text" |
| placeholder="Search conversations…" |
| value={searchQ} |
| onChange={e => onSearchChange(e.target.value)} |
| /> |
| </div> |
| |
| <div className="sidebar-label">Recent</div> |
| |
| {/* Session list */} |
| <ul className="session-list"> |
| {sessions.length === 0 && ( |
| <li style={{ padding: '16px 12px', color: 'var(--t4)', fontSize: '12.5px', textAlign: 'center' }}> |
| No conversations yet |
| </li> |
| )} |
| {sessions.map((s, i) => ( |
| <li key={s.session_id} style={{ animationDelay: `${i * 30}ms` }}> |
| <div |
| className={`session-item ${s.session_id === currentSessionId ? 'active' : ''}`} |
| onClick={() => onSelectSession(s)} |
| title={s.title || 'New Chat'} |
| > |
| <div className="session-title">{s.title || 'New Chat'}</div> |
| <button |
| className="session-delete" |
| onClick={e => { e.stopPropagation(); onDeleteSession(s.session_id) }} |
| title="Delete" |
| > |
| <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"> |
| <polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/><path d="M9 6V4h6v2"/> |
| </svg> |
| </button> |
| </div> |
| </li> |
| ))} |
| </ul> |
| |
| {/* Bottom actions */} |
| <div className="sidebar-bottom"> |
| <button className="sidebar-action" onClick={onFeedback} title="Send Feedback"> |
| <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> |
| <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/> |
| </svg> |
| <span className="sidebar-action-text">Send Feedback</span> |
| </button> |
| <button className="sidebar-action" onClick={onSettings} title="Settings"> |
| <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> |
| <circle cx="12" cy="12" r="3"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14M4.93 4.93a10 10 0 0 0 0 14.14"/> |
| </svg> |
| <span className="sidebar-action-text">Settings</span> |
| </button> |
| </div> |
| </aside> |
| ) |
| } |
|
|