From e34f79fc56d26b5c41842c165c8e76fb9b4b1384 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Fri, 26 Jun 2026 21:31:09 -0400 Subject: [PATCH] feat(explore): show Provenance in the tag rail (post often names the character) The post title/description frequently names the character, so surface it while tagging in Explore (operator-asked 2026-06-26). ProvenancePanel gains optional imageId/image props (default = modal store, so the modal is unchanged) since provenance is its own system loaded by id; ExploreView renders it above TagPanel in the right rail, hosted on the anchor. Self-collapses when the image has no provenance. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../src/components/modal/ProvenancePanel.vue | 18 ++++++++++++++---- frontend/src/views/ExploreView.vue | 14 ++++++++------ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/frontend/src/components/modal/ProvenancePanel.vue b/frontend/src/components/modal/ProvenancePanel.vue index 95d0630..2bd8309 100644 --- a/frontend/src/components/modal/ProvenancePanel.vue +++ b/frontend/src/components/modal/ProvenancePanel.vue @@ -97,9 +97,19 @@ import { useProvenanceStore } from '../../stores/provenance.js' import { formatPostDate } from '../../utils/date.js' import { toPlainText } from '../../utils/htmlSanitize.js' +// `imageId`/`image` let a non-modal surface (the Explore workspace) render +// provenance for its anchor. Default to the modal store's current image so the +// image modal is unchanged. Provenance is its own system (loaded by id via the +// provenance store), so it only needs the right id + the artist fallback. +const props = defineProps({ + imageId: { type: Number, default: null }, + image: { type: Object, default: null }, +}) const modal = useModalStore() const prov = useProvenanceStore() const router = useRouter() +const effectiveId = computed(() => props.imageId ?? modal.currentImageId) +const effectiveImage = computed(() => props.image ?? modal.current) // Per-post description collapse state (keyed by provenance_id). Default // collapsed so multiple posts don't each eat ~180px of the panel — the @@ -107,21 +117,21 @@ const router = useRouter() // 2026-05-28. Reset when the viewed image changes. const expanded = reactive({}) function toggleDesc(id) { expanded[id] = !expanded[id] } -watch(() => modal.currentImageId, () => { +watch(() => effectiveId.value, () => { for (const k of Object.keys(expanded)) delete expanded[k] }) watch( - () => modal.currentImageId, + () => effectiveId.value, (id) => { if (id != null) prov.loadForImage(id) }, { immediate: true } ) const state = computed(() => - modal.currentImageId == null ? null : prov.imageProv(modal.currentImageId) + effectiveId.value == null ? null : prov.imageProv(effectiveId.value) ) -const fallbackArtist = computed(() => modal.current?.artist || null) +const fallbackArtist = computed(() => effectiveImage.value?.artist || null) const showArtistFallback = computed(() => { const st = state.value diff --git a/frontend/src/views/ExploreView.vue b/frontend/src/views/ExploreView.vue index e7be32f..4b60970 100644 --- a/frontend/src/views/ExploreView.vue +++ b/frontend/src/views/ExploreView.vue @@ -96,12 +96,13 @@ - -