Files
mule-image/frontend/src/components/KeyboardHints.tsx
dtoro f4c51f6f92 fix: make keyboard hint pill legible over busy thumbnails
The bottom-center hint bar used bg-surface/40 + text-text-muted, which
left busy photos leaking through and washed the text out — users
couldn't actually read the shortcut labels when a bright thumbnail sat
directly behind the pill. Switched to a near-opaque bg-black/80 with
white-alpha text + white/15 key caps. Backdrop blur stays for the
subtle glass feel, but the contrast is now unconditional on the
photo underneath.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 17:44:04 +02:00

63 lines
2.4 KiB
TypeScript

import { usePhotoStore } from '../store/photoStore'
export function KeyboardHints() {
const selectedCount = usePhotoStore((state) => state.selectedPhotos.length)
const viewMode = usePhotoStore((state) => state.viewMode)
// In preview mode the viewer has its own context, so the grid hints
// would just be confusing. Hide them.
if (viewMode === 'preview') return null
const hints = selectedCount > 0
? [
{ key: '1-5', action: 'Rate' },
{ key: 'P', action: 'Pick → heap' },
{ key: 'X', action: 'Discard' },
{ key: 'Space', action: 'Preview' },
{ key: 'I', action: 'Info panel' },
{ key: 'Esc', action: 'Deselect' },
]
: [
{ key: '↑↓←→', action: 'Navigate' },
{ key: 'Space', action: 'Preview' },
{ key: 'Tab', action: 'Library panel' },
{ key: 'I', action: 'Info panel' },
{ key: '/', action: 'Search' },
]
return (
// Absolute (not fixed) so the parent's flex/position context can
// center it relative to the timeline area, not the viewport. Mount
// inside the main column in App.tsx so it isn't offset by the
// sidebar widths.
<div className="pointer-events-none absolute bottom-4 left-1/2 z-30 -translate-x-1/2">
{/* Near-opaque dark pill so the hints stay legible against busy
* thumbnails. The previous bg-surface/40 + 5% white ring left
* text washed out when a bright photo sat directly behind it. */}
<div className="pointer-events-auto flex items-center gap-3 whitespace-nowrap rounded-full border border-white/15 bg-black/80 px-4 py-1.5 shadow-xl ring-1 ring-black/40 backdrop-blur-md">
{hints.map((hint, i) => (
<div key={i} className="flex items-center gap-1.5">
<kbd className="rounded bg-white/15 px-1.5 py-0.5 text-[11px] font-medium text-white shadow-sm">
{hint.key}
</kbd>
<span className="whitespace-nowrap text-xs text-white/85">
{hint.action}
</span>
{i < hints.length - 1 && (
<span className="ml-1 text-white/30"></span>
)}
</div>
))}
{selectedCount > 0 && (
<>
<span className="text-white/30"></span>
<span className="whitespace-nowrap text-xs font-semibold text-primary">
{selectedCount} selected
</span>
</>
)}
</div>
</div>
)
}