refactor: tighten sidebars with eyebrow section headers

Compact the left/right sidebars and the photo info panel: shrink panel
widths, drop header height, switch top-level tree groups and metadata
sections to small uppercase eyebrow labels, and tighten row padding,
icon sizes, and count badges throughout.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-09 19:03:22 +02:00
parent 12a616d4b5
commit 8f25c58b74
6 changed files with 91 additions and 66 deletions

View File

@@ -395,19 +395,31 @@ export function LeftSidebar({ onCollapse, onOpenSettings }: LeftSidebarProps) {
const isSelected = isItemActive(item.id)
const acceptsDrop = isDropTarget(item.id)
const isDropHover = dropTargetId === item.id
// Top-level entries (Views, Folders) render as small uppercase
// section eyebrows rather than another tree row, so the panel reads
// as distinct sections with the actual items beneath them.
const isSectionHeader = depth === 0
return (
<div key={item.id}>
<div
className={clsx(
'group flex cursor-pointer items-center gap-1 rounded px-2 py-1 text-sm',
isSelected ? 'bg-primary/20 text-primary' : 'text-text hover:bg-surface-2',
'group flex cursor-pointer items-center gap-1',
isSectionHeader
? 'mt-2 px-3 py-1 text-[10px] font-semibold uppercase tracking-[0.14em] text-text-muted hover:text-text'
: clsx(
'rounded px-2 py-0.5 text-[12px]',
isSelected ? 'bg-primary/20 text-primary' : 'text-text hover:bg-surface-2'
),
isDropHover && (item.id === 'discarded'
? 'ring-2 ring-reject bg-reject/10'
: 'ring-2 ring-primary bg-primary/10'),
depth > 0 && 'text-[13px]'
: 'ring-2 ring-primary bg-primary/10')
)}
style={{ paddingLeft: `${8 + depth * 16}px` }}
style={
isSectionHeader
? undefined
: { paddingLeft: `${8 + (depth - 1) * 12}px` }
}
onClick={() => {
if (renamingId === item.id) return
// Folder rows are always filterable, parent or leaf — clicking
@@ -470,13 +482,19 @@ export function LeftSidebar({ onCollapse, onOpenSettings }: LeftSidebarProps) {
)}
</button>
) : (
<div className="w-4" />
!isSectionHeader && <div className="w-3" />
)}
{/* Item Icon */}
{item.icon && (
<span className={clsx('flex-shrink-0', isSelected ? 'text-primary' : 'text-text-muted')}>
{item.icon}
{/* Item Icon — section headers drop their icon in favor of the
* uppercase eyebrow label. */}
{item.icon && !isSectionHeader && (
<span
className={clsx(
'flex-shrink-0',
isSelected ? 'text-primary' : 'text-text-muted'
)}
>
<span className="[&>svg]:h-3.5 [&>svg]:w-3.5">{item.icon}</span>
</span>
)}
@@ -510,13 +528,16 @@ export function LeftSidebar({ onCollapse, onOpenSettings }: LeftSidebarProps) {
)}
{/* Count Badge — fixed-width slot so counts line up in a column
* across rows regardless of digit count. */}
{item.count !== undefined && item.count > 0 ? (
<span className="flex h-5 min-w-[24px] flex-shrink-0 items-center justify-center rounded bg-surface-offset px-1.5 text-xs tabular-nums text-text-muted">
{item.count}
</span>
) : (
<span className="h-5 min-w-[24px] flex-shrink-0" aria-hidden="true" />
* across rows regardless of digit count. Section headers skip
* the badge entirely (they're labels, not navigable rows). */}
{!isSectionHeader && (
item.count !== undefined && item.count > 0 ? (
<span className="flex h-4 min-w-[20px] flex-shrink-0 items-center justify-center rounded bg-surface-offset px-1 text-[10px] tabular-nums text-text-muted">
{item.count}
</span>
) : (
<span className="h-4 min-w-[20px] flex-shrink-0" aria-hidden="true" />
)
)}
{/* Folder kebab menu — only on folder rows. Hidden (display:none)
@@ -658,15 +679,17 @@ export function LeftSidebar({ onCollapse, onOpenSettings }: LeftSidebarProps) {
<div className="flex h-full flex-col bg-surface">
{/* Header with collapse button. Matches the right sidebar header
* so both panels have symmetric affordances. */}
<div className="flex h-11 flex-shrink-0 items-center justify-between border-b border-border px-4">
<h2 className="text-sm font-semibold text-text">Library</h2>
<div className="flex h-9 flex-shrink-0 items-center justify-between border-b border-border px-3">
<h2 className="text-[11px] font-semibold uppercase tracking-[0.14em] text-text-muted">
Library
</h2>
<button
onClick={onCollapse}
className="rounded p-1 text-text-muted hover:bg-surface-2 hover:text-text"
className="rounded p-0.5 text-text-muted hover:bg-surface-2 hover:text-text"
title="Collapse panel (Tab)"
aria-label="Collapse panel"
>
<PanelLeftClose className="h-4 w-4" />
<PanelLeftClose className="h-3.5 w-3.5" />
</button>
</div>
{/* Active heap card — pinned just below the Library header so
@@ -675,20 +698,20 @@ export function LeftSidebar({ onCollapse, onOpenSettings }: LeftSidebarProps) {
<ActiveHeapCard />
{/* Tree View */}
<div className="flex-1 overflow-y-auto py-2">
<div className="flex-1 overflow-y-auto pb-2">
{libraryTree.map((item) => renderTreeItem(item))}
<HeapsPanel />
</div>
{/* Settings entry point — pinned to the bottom of the panel so it
* sits out of the way of the library tree but is always reachable. */}
<div className="border-t border-border p-2">
<div className="border-t border-border p-1.5">
<button
onClick={onOpenSettings}
className="flex w-full items-center gap-2 rounded px-2 py-1.5 text-sm text-text-muted hover:bg-surface-2 hover:text-text"
className="flex w-full items-center gap-2 rounded px-2 py-1 text-[12px] text-text-muted hover:bg-surface-2 hover:text-text"
title="Settings"
>
<Settings className="h-4 w-4" />
<Settings className="h-3.5 w-3.5" />
Settings
</button>
</div>