feat(ui): grounding overlay leads with the hovered tag, crop origin as subtext (#1206)
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) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CDgx8bQS5YrGRK76v8HUnM
This commit is contained in:
@@ -35,7 +35,10 @@
|
||||
:class="{ 'fc-canvas__region--whole': !regionStyle }"
|
||||
:style="regionStyle || {}"
|
||||
>
|
||||
<span class="fc-canvas__region-label">{{ regionLabel }}</span>
|
||||
<span class="fc-canvas__region-label">
|
||||
<span v-if="regionTag" class="fc-canvas__region-tag">{{ regionTag }}</span>
|
||||
<span class="fc-canvas__region-origin">{{ regionOrigin }}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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++
|
||||
|
||||
Reference in New Issue
Block a user