From 7939dba9ed88d9d35b4a8fdd7fd76bd033256ca8 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Mon, 6 Jul 2026 15:11:10 -0400 Subject: [PATCH] feat(ui): grounding overlay leads with the hovered tag, crop origin as subtext (#1206) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The overlay label showed only the crop origin (booru:head / panel / person-m). Put the hovered TAG on top as the headline and demote the crop origin to a small, dimmed, monospace subline — the tag is what you're evaluating; the origin is just provenance. Threads the tag name through the fcSuggestionHover payload ({g, tag}) from both setters (SuggestionItem for suggestions, TagChip for applied chips). Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01CDgx8bQS5YrGRK76v8HUnM --- frontend/src/components/modal/ImageCanvas.vue | 41 ++++++++++++++----- .../src/components/modal/SuggestionItem.vue | 2 +- frontend/src/components/modal/TagChip.vue | 2 +- 3 files changed, 33 insertions(+), 12 deletions(-) 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++