// Topbar — view switcher, sort, selection controls, capture indicator, analytics toggle function Topbar({ view, setView, sort, setSort, density, setDensity, selected, onSelectAll, onClearSelect, onBulkPick, onBulkReject, onBulkShare, onBulkDownload, onBulkDelete, onSearch, searchQuery, stats, capturing, lastCapture, analyticsOpen, setAnalyticsOpen, onToggleSidebar, onOpenCollections, onShare, onShareView, onOpenShortcuts, onSecondScreen, }) { const bulkMode = selected.size > 0; return (
{onOpenCollections && ( )} {bulkMode ? ( <> {selected.size} selected Pick Reject }>Share }>Download }/>
Clear ) : ( <> {/* View switcher */}
{[['grid', ], ['list', ]].map(([v, icon]) => ( ))}
{view === 'grid' && (
{['tight','normal','loose'].map(d => ( ))}
)} {/* Sort */} Sort {/* Search */}
onSearch(e.target.value)} placeholder="Search filename, tag, EXIF…" style={{ width: '100%', height: 26, background: 'var(--bg-2)', border: '1px solid var(--line-1)', borderRadius: 4, padding: '0 8px 0 26px', color: 'var(--ink-0)', fontSize: 11.5, outline: 'none', }}/> {searchQuery && ( )}
{/* Capture indicator */}
{capturing ? 'CAPTURING' : 'TETHER IDLE'} {lastCapture && · {lastCapture}}
setAnalyticsOpen(!analyticsOpen)} icon={} active={analyticsOpen} title="Review stats"/> ? {onSecondScreen && ( }/> )} } title="Preview as client"/> }>Share )}
); } const iconBtnS = { width: 28, height: 26, borderRadius: 4, background: 'transparent', border: '1px solid transparent', color: 'var(--ink-2)', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', }; function AnalyticsBar({ stats, open }) { if (!open) return null; const pickPct = stats.total ? Math.round((stats.picks / stats.total) * 100) : 0; const rejectPct = stats.total ? Math.round((stats.rejects / stats.total) * 100) : 0; const reviewPct = stats.total ? Math.round(((stats.picks + stats.rejects) / stats.total) * 100) : 0; return (
0 ? `${stats.total} files` : ''}/> {(stats.blurred || 0) > 0 && }
Review progress {reviewPct}%
); } function Stat({ label, value, hint, sub, color }) { return (
{label} {value} {(hint || sub) && {hint || sub}}
); } function ShortcutsModal({ onClose }) { const groups = [ { title: 'Navigation', items: [ ['←/→', 'Previous / Next photo'], ['↑/↓', 'Row up / down (grid)'], ['⏎', 'Open lightbox'], ['Esc', 'Close lightbox / clear selection'], ]}, { title: 'Culling', items: [ ['P', 'Pick'], ['X', 'Reject'], ['U', 'Clear flag'], ['1–5', 'Rate stars'], ['0', 'Clear rating'], ['6/7/8/9', 'Color label red/yellow/green/blue'], ]}, { title: 'View', items: [ ['Z', 'Zoom 100% toggle'], ['I', 'Show EXIF'], ['E', 'Develop panel'], ['\\', 'Before/After compare'], ['G', 'Grid view'], ['L', 'List view'], ]}, { title: 'Selection', items: [ ['⌘/Ctrl + A', 'Select all'], ['⌘/Ctrl + D', 'Deselect'], ['Shift + click', 'Range select'], ['⌘/Ctrl + S', 'Create share link'], ['?', 'This help'], ]}, ]; return (
Studio
Keyboard shortcuts
}/>
{groups.map(g => (
{g.title}
{g.items.map(([k, desc]) => (
{desc} {k}
))}
))}
); } Object.assign(window, { Topbar, AnalyticsBar, ShortcutsModal });