AppSnackbar mounts once at the app root and exposes a window.__fcToast
function the stores call from error paths. This avoids prop-drilling a
toast emitter through every component while keeping the snackbar
component itself testable in isolation.
GalleryGrid renders shimmer-skeleton placeholders on initial load so the
first paint isn't empty. Skeleton uses the same auto-fill grid columns
as the real items so layout doesn't shift when content arrives.
Modal arrow nav now ignores text-entry elements so typing in the tag
autocomplete doesn't navigate prev/next. Small-screen breakpoint
(<600px) tightens gallery thumbnails to 120px min.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GalleryView wires the grid + timeline + modal together. The URL is the
source of truth for which image (if any) is open — clicking an item pushes
?image=N; closing the modal pops it. This makes deep links shareable and
the back button work intuitively.
Below 900px viewport, the timeline drops below the grid as a horizontal
strip so it doesn't compete with thumbnail space on mobile.
Modal store + ImageViewer are placeholders here; real implementations land
in Batch 5 (Tasks 19–22).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Grid uses CSS grid auto-fill with 160px minimums so it adapts naturally
from phone to large displays. Date headers render once per (year, month)
group; the API's date_groups payload tells us where to slot them without
client-side grouping. IntersectionObserver with rootMargin: 600px loads
the next page well before the sentinel reaches the viewport.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Gallery store handles cursor pagination, date-group merging across pages,
tag filter, timeline buckets, and jump-to-month. Stale response detection
via an inflightId counter prevents out-of-order page additions when the
user toggles filters quickly.
GalleryItem is a square thumbnail card with hover/focus accent outline,
keyboard activation (Enter/Space → open), video badge for video MIME types,
and a broken-image fallback if the thumbnail URL fails to load.
EmptyState points operators to /settings?tab=import so the empty path is
self-guiding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>