ui: rework selection/heap visuals, contextual shortcut hints, inline scan activity

- Selection now reads as a blue ring + tint with a springy scale-down,
  hover stays a subtle gray ring so keyboard-driven and mouse-driven
  states are tellable apart.
- Heap membership is signalled with a green tint only (no badge, no
  ring, no scale).
- Discard/restore is optimistic and non-yanking: photos stay in the
  grid greyed out until the next reload, X toggles based on the
  current state, and the same treatment applies in preview.
- Filmstrip mirrors the grid styling (selection blue, heap green,
  discarded grey).
- Preview close restores the LAST viewed photo as the focused/selected
  one in the grid.
- Right sidebar collapses on view change and re-opens when a photo is
  in focus; Esc clears active selection so the panel collapses too.
- Keyboard hints panel is context-aware (grid / preview / discarded
  section), collapsible with H, persisted, and rendered inside the
  preview column above the filmstrip.
- "Pick (P)" renamed to "Select (S)" everywhere.
- Needs review moved into the Flag pill dropdown.
- Fixed vertical videos overflowing the preview column (min-h-0).
- Replaced the bottom-right ScanProgress popover with an inline
  spinner next to the FOLDERS sidebar header (and on the specific
  folder row being scanned). ScanProgress is now a headless
  invalidator; useScanActivity exposes the live status.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-14 23:45:36 +02:00
parent a6eb406052
commit 8529771122
17 changed files with 501 additions and 408 deletions

View File

@@ -155,7 +155,7 @@ export function RightSidebar() {
const { data: allTags = [] } = useTagsQuery()
const [tagInput, setTagInput] = useState('')
// Active heap membership for the bulk Pick toggle.
// Active heap membership for the bulk Select toggle.
const { activeHeap, memberIds: activeHeapMembers } = useActiveHeapMembers()
const heapMutation = useMutation({
@@ -385,7 +385,7 @@ export function RightSidebar() {
}
>
<ShoppingBasket className="h-3 w-3" />
{allMembers ? 'Picked' : 'Pick'}
{allMembers ? 'Selected' : 'Select'}
</button>
<button
onClick={() => bulkDiscardMutation.mutate(selectedPhotos)}