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>
AliasPicker: debounced autocomplete scoped to the prediction's category
(artist/character map to tag kinds; copyright searches unscoped),
returns the chosen canonical tag id. RenameDialog: PATCH /api/tags/<id>,
surfaces 409 collisions with the FC-2c merge hint.
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>
Teleport mounts the modal under <body> so it can break out of the
gallery container's stacking context. Body overflow is locked while open
so background scroll doesn't compete with pan/zoom.
Keyboard: left/right arrows navigate prev/next (preventDefault so they
don't also scroll the body), Esc closes, Tab is trapped within the
modal by browser default since the modal is focused on mount.
TagPanel renders kind-colored chips with the close (×) affordance for
removal, and hosts TagAutocomplete + FandomPicker for additions. Below
900px the panel drops below the media area instead of beside it.
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>
ImageCanvas mounts an <img> wrapped in the usePanZoom composable —
spreading its handlers onto the container element. Zoom resets when src
changes so prev/next nav doesn't carry zoom state into the next image.
VideoCanvas plays MP4/WebM/Ogg/MOV natively. Non-playable formats render
a friendly explainer with a download fallback, naming the actual MIME so
operators know what they're dealing with, and pointing forward at FC-2e
where transcoding lands.
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>