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++