feat(explore): 3-pane tagging workspace — gallery | viewer | tag rail
Reworks Explore from "anchor + neighbour grid + cluster tag-gap rail" into a persistent 3-pane workspace that unfolds the image modal so you can tag while rabbit-holing (operator concept 2026-06-26): - LEFT neighbour grid (larger thumbs), click = walk; breadcrumb retained. - CENTER light viewer — reuses ImageCanvas + ImageMetaBar(:image) for the focused image; "Open full viewer" still launches the overlay modal. - RIGHT the modal's TagPanel, hosted on the anchor for modal-parity tagging (chips, autocomplete, suggestions + Accept, fandom-on-chip, T/"/" focus). Reuse without destabilising the audited modal store: TagPanel and SuggestionsPanel gain an optional `host` prop (default = modal store, so the image modal is unchanged); the explore store implements the same small tag-CRUD surface (current/currentImageId + reloadTags/addExistingTag/ removeTag/createAndAdd) over the anchor. ImageMetaBar gains an optional `image` prop for the same reason. Drops the mass/cluster tagger (TagGapPanel deleted; clusterIds/thumbById removed) — per-image tagging feeds the per-tag reference-embedding centroid better than bulk ops. Nav: keep the Explore tab but bare /explore now SEEDS a random image (GET /api/showcase?limit=1 → /explore/:id) so the tab kick-starts a rabbit hole; explicit meta.navOrder pins nav order (Explore after Gallery) since router.getRoutes() doesn't preserve declaration order. Note: the backend cluster tag-gaps route/service (#94a) is now frontend-orphaned — left in place; flag for a separate cleanup. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -38,8 +38,12 @@ import { useModalStore } from '../../stores/modal.js'
|
||||
import { copyText } from '../../utils/clipboard.js'
|
||||
import { toast } from '../../utils/toast.js'
|
||||
|
||||
// `image` lets a non-modal surface (the Explore workspace) render the same
|
||||
// meta + download block for its anchor. Defaults to the modal store's current
|
||||
// image so the image modal is unchanged.
|
||||
const props = defineProps({ image: { type: Object, default: null } })
|
||||
const modal = useModalStore()
|
||||
const img = computed(() => modal.current)
|
||||
const img = computed(() => props.image ?? modal.current)
|
||||
|
||||
function humanSize (bytes) {
|
||||
const units = ['B', 'KB', 'MB', 'GB']
|
||||
|
||||
Reference in New Issue
Block a user