// 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 });