diff --git a/frontend/src/components/modal/ImageCanvas.vue b/frontend/src/components/modal/ImageCanvas.vue index efe2e52..f3af01e 100644 --- a/frontend/src/components/modal/ImageCanvas.vue +++ b/frontend/src/components/modal/ImageCanvas.vue @@ -35,7 +35,10 @@ :class="{ 'fc-canvas__region--whole': !regionStyle }" :style="regionStyle || {}" > - {{ regionLabel }} + + {{ regionTag }} + {{ regionOrigin }} + @@ -48,10 +51,12 @@ import { usePanZoom } from '../../composables/usePanZoom.js' const props = defineProps({ src: String, alt: String, - // Hovered-suggestion grounding, or null when nothing is hovered: - // null → no overlay - // { g: {bbox,kind,detector} } → highlight that crop region - // { g: null } → whole-image frame (not localized) + // Hovered-suggestion state, or null when nothing is hovered: + // null → no overlay + // { g: {bbox,kind,detector}, tag } → highlight that crop, label with the tag + // { g: null, tag } → whole-image frame (not localized) + // `tag` is the hovered concept's name (primary label line); `g` is where the + // crop came from (secondary provenance line). hover: { type: Object, default: null }, }) const emit = defineEmits(['close-request']) @@ -86,7 +91,12 @@ const regionStyle = computed(() => { width: `${rw * 100}%`, height: `${rh * 100}%`, } }) -const regionLabel = computed(() => { +// The hovered tag/suggestion name — the PRIMARY label line (what you care about). +const regionTag = computed(() => props.hover?.tag || '') +// Where the winning crop came from (the region's detector_version, else kind) — +// shown small + dimmed beneath the tag as provenance, not the headline. "whole +// image" = null grounding (the global vector won, not a crop). +const regionOrigin = computed(() => { const g = props.hover?.g return g ? (g.detector || g.kind || 'region') : 'whole image' }) @@ -176,15 +186,26 @@ function onCanvasClick(ev) { position: absolute; top: 0; left: 0; transform: translateY(-100%); - font-size: 11px; - font-family: 'JetBrains Mono', monospace; - line-height: 1.4; - padding: 1px 6px; + display: flex; flex-direction: column; + line-height: 1.25; + padding: 2px 6px; white-space: nowrap; background: rgb(var(--v-theme-accent)); color: rgb(var(--v-theme-on-surface)); border-radius: 3px 3px 0 0; } +/* The hovered tag is the headline. */ +.fc-canvas__region-tag { + font-size: 12px; + font-weight: 600; +} +/* Crop origin is provenance, not the point — subordinate: smaller, dimmed, and + monospace (it's a detector token like booru:head, not prose). */ +.fc-canvas__region-origin { + font-size: 10px; + font-family: 'JetBrains Mono', monospace; + color: rgb(var(--v-theme-on-surface), 0.72); +} .fc-canvas__region--whole .fc-canvas__region-label { background: rgb(var(--v-theme-on-surface-variant)); } diff --git a/frontend/src/components/modal/SuggestionItem.vue b/frontend/src/components/modal/SuggestionItem.vue index 4d00b70..7a47e3c 100644 --- a/frontend/src/components/modal/SuggestionItem.vue +++ b/frontend/src/components/modal/SuggestionItem.vue @@ -85,7 +85,7 @@ defineEmits(['accept', 'alias', 'remove-alias', 'dismiss', 'undismiss']) // it highlights that region. Provided by ImageViewer/Explore; a no-op elsewhere. const hover = inject('fcSuggestionHover', null) function onEnter () { - if (hover) hover.value = { g: props.suggestion.grounding ?? null } + if (hover) hover.value = { g: props.suggestion.grounding ?? null, tag: props.suggestion.display_name } } function onLeave () { if (hover) hover.value = null diff --git a/frontend/src/components/modal/TagChip.vue b/frontend/src/components/modal/TagChip.vue index bcb1cdf..6437255 100644 --- a/frontend/src/components/modal/TagChip.vue +++ b/frontend/src/components/modal/TagChip.vue @@ -83,7 +83,7 @@ async function onEnter () { if (seq !== hoverSeq) return // pointer already left / moved to another chip // No head → nothing to localize; don't draw an overlay at all. With a head, // null grounding still draws the whole-image frame ("the global vector won"). - if (res.has_head) hover.value = { g: res.grounding ?? null } + if (res.has_head) hover.value = { g: res.grounding ?? null, tag: props.tag.name } } function onLeave () { hoverSeq++