feat(explore): show Provenance in the tag rail (post often names the character)
CI / lint (push) Successful in 2s
CI / frontend-build (push) Successful in 21s
CI / backend-lint-and-test (push) Successful in 25s
CI / integration (push) Successful in 3m18s

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) <noreply@anthropic.com>
This commit is contained in:
2026-06-26 21:31:09 -04:00
parent c8a8e23050
commit e34f79fc56
2 changed files with 22 additions and 10 deletions
@@ -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