Thin wrappers over the FC-2b API: ml (settings GET/PATCH, backfill +
recompute triggers), allowlist (list, threshold patch, remove with
optimistic local update), aliases (list, remove with optimistic update).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
TagPanel now renders SuggestionsPanel below the current-tags area and
adds a per-chip kebab → Rename… (TagRenameDialog). modal store's
removeTag now also POSTs suggestions/dismiss after the delete so removing
an auto-applied tag records a per-image rejection and the allowlist
maintenance sweep won't re-apply it (closes the remove→re-apply loop
identified in the spec).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Store: load per-image suggestions, accept (creates the tag first for
raw/creates_new_tag suggestions, then accepts by id), aliasAccept,
dismiss (client-side hide for raw tags), success toasts folded in.
Panel: people/sources groups always open, General collapsed by default;
alias picker dialog wired; shimmer skeleton while loading. SuggestionItem
shows score %, +new badge for raw tags, kebab menu. AliasPickerDialog
referenced (lands in Task 15).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
TagAutocomplete: kind selector + debounced (200ms) search + keyboard nav
(up/down/enter/esc). When the operator types a character that doesn't
exist, the create flow first opens FandomPicker so the new character tag
lands with a valid fandom_id (otherwise the API rejects it).
FandomPicker lists existing fandoms (cached after first load) with an
inline create-new affordance, so the operator never has to leave the modal
to manage fandom hierarchies.
Tag store also exposes a kind→icon→color mapping that the TagPanel reuses
in Task 22 — keeping chip colors visually distinct per kind without per-app
accent collisions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
modal store loads image detail (with neighbors) on open, exposes goPrev/
goNext that re-fetch the next image, and implements optimistic tag
remove with rollback on API failure. createAndAdd is the bridge between
the autocomplete UI and a brand-new tag.
usePanZoom is a small composable: click-to-toggle 2x zoom centered on
click point, wheel-to-zoom up to 4x, drag-to-pan when zoomed. Pointer
events are used (touch + mouse + pen unified). Caller spreads handlers
onto the element it wants pannable.
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>
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>
useApi centralizes fetch error handling — every API call throws ApiError
on non-2xx with the parsed body attached, so components don't repeat the
'check response.ok' boilerplate.
import store wraps import settings, batch status, task list (with cursor
pagination), and the trigger/retry/clear admin actions. system store
gains a refreshStats() that powers the Settings overview tab.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Nav lists every FC-2 and FC-3 surface with a placeholder view that names
the sub-project that will fill it. System store polls /api/health on mount
and renders a status pip in the nav footer.
Favicon uses Curator's aged amber accent (#A87338).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>