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 }"
|
:class="{ 'fc-canvas__region--whole': !regionStyle }"
|
||||||
:style="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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -48,10 +51,12 @@ import { usePanZoom } from '../../composables/usePanZoom.js'
|
|||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
src: String,
|
src: String,
|
||||||
alt: String,
|
alt: String,
|
||||||
// Hovered-suggestion grounding, or null when nothing is hovered:
|
// Hovered-suggestion state, or null when nothing is hovered:
|
||||||
// null → no overlay
|
// null → no overlay
|
||||||
// { g: {bbox,kind,detector} } → highlight that crop region
|
// { g: {bbox,kind,detector}, tag } → highlight that crop, label with the tag
|
||||||
// { g: null } → whole-image frame (not localized)
|
// { 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 },
|
hover: { type: Object, default: null },
|
||||||
})
|
})
|
||||||
const emit = defineEmits(['close-request'])
|
const emit = defineEmits(['close-request'])
|
||||||
@@ -86,7 +91,12 @@ const regionStyle = computed(() => {
|
|||||||
width: `${rw * 100}%`, height: `${rh * 100}%`,
|
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
|
const g = props.hover?.g
|
||||||
return g ? (g.detector || g.kind || 'region') : 'whole image'
|
return g ? (g.detector || g.kind || 'region') : 'whole image'
|
||||||
})
|
})
|
||||||
@@ -176,15 +186,26 @@ function onCanvasClick(ev) {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0; left: 0;
|
top: 0; left: 0;
|
||||||
transform: translateY(-100%);
|
transform: translateY(-100%);
|
||||||
font-size: 11px;
|
display: flex; flex-direction: column;
|
||||||
font-family: 'JetBrains Mono', monospace;
|
line-height: 1.25;
|
||||||
line-height: 1.4;
|
padding: 2px 6px;
|
||||||
padding: 1px 6px;
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
background: rgb(var(--v-theme-accent));
|
background: rgb(var(--v-theme-accent));
|
||||||
color: rgb(var(--v-theme-on-surface));
|
color: rgb(var(--v-theme-on-surface));
|
||||||
border-radius: 3px 3px 0 0;
|
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 {
|
.fc-canvas__region--whole .fc-canvas__region-label {
|
||||||
background: rgb(var(--v-theme-on-surface-variant));
|
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.
|
// it highlights that region. Provided by ImageViewer/Explore; a no-op elsewhere.
|
||||||
const hover = inject('fcSuggestionHover', null)
|
const hover = inject('fcSuggestionHover', null)
|
||||||
function onEnter () {
|
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 () {
|
function onLeave () {
|
||||||
if (hover) hover.value = null
|
if (hover) hover.value = null
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ async function onEnter () {
|
|||||||
if (seq !== hoverSeq) return // pointer already left / moved to another chip
|
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,
|
// 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").
|
// 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 () {
|
function onLeave () {
|
||||||
hoverSeq++
|
hoverSeq++
|
||||||
|
|||||||
Reference in New Issue
Block a user