feat(modal): green ✓ / red ✗ verdict pair on suggestion rows
Replace the single "Accept" pill in the modal Suggestions rail with the eval card's green ✓ / red ✗ language: ✓ accepts the tag (positive), ✗ dismisses it for this image — which already persists a TagSuggestionRejection (hard negative the heads train on). The pair occupies ~the footprint of the old pill, so per-image rejection becomes a one-click peer of accepting instead of being buried in the kebab. Dismiss moves off the 3-dot menu, so the kebab now only carries alias actions and is hidden when none apply (centroid hits with no alias option). Toward #1134 (native per-image negatives in the rail). The bigger piece — heads as a suggestion source feeding this panel — is still ahead. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ttrj5P7upUTueSfoJcxEqa
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<!-- Chip-card row: visible border + hover/focus state unifies the
|
||||
name, score, and action buttons as one "object" (operator-asked
|
||||
2026-06-01). The row itself is informational; the explicit
|
||||
Accept button + 3-dot menu are the action affordances. -->
|
||||
2026-06-01). The row itself is informational; the green ✓ / red ✗
|
||||
verdict pair + 3-dot alias menu are the action affordances. -->
|
||||
<div class="fc-suggestion">
|
||||
<span class="fc-suggestion__name">
|
||||
{{ suggestion.display_name }}
|
||||
@@ -12,18 +12,32 @@
|
||||
:title="`Mapped from the tagger's “${suggestion.raw_name}” via an alias`">alias</span>
|
||||
</span>
|
||||
<span class="fc-suggestion__score">{{ scorePct }}</span>
|
||||
<v-btn
|
||||
class="fc-suggestion__accept"
|
||||
size="small" variant="tonal" color="accent"
|
||||
density="compact" rounded="pill"
|
||||
:aria-label="`Accept ${suggestion.display_name}`"
|
||||
@click="$emit('accept', suggestion)"
|
||||
>
|
||||
Accept
|
||||
</v-btn>
|
||||
<!-- Green ✓ / red ✗ pair (operator-asked 2026-06-28) mirrors the eval
|
||||
card's verdict buttons: ✓ accepts the tag (positive), ✗ dismisses it
|
||||
for this image (records a TagSuggestionRejection — a hard negative the
|
||||
heads train on). Together they occupy ~the footprint of the old single
|
||||
Accept pill, so rejecting is now a one-click peer of accepting rather
|
||||
than buried in the kebab. -->
|
||||
<div class="fc-suggestion__acts">
|
||||
<button
|
||||
class="fc-act fc-act--yes" type="button"
|
||||
:aria-label="`Accept ${suggestion.display_name}`"
|
||||
:title="`Yes — tag ${suggestion.display_name}`"
|
||||
@click="$emit('accept', suggestion)"
|
||||
><v-icon size="16">mdi-check</v-icon></button>
|
||||
<button
|
||||
class="fc-act fc-act--no" type="button"
|
||||
:aria-label="`Reject ${suggestion.display_name}`"
|
||||
:title="`No — not ${suggestion.display_name}`"
|
||||
@click="$emit('dismiss', suggestion)"
|
||||
><v-icon size="16">mdi-close</v-icon></button>
|
||||
</div>
|
||||
<!-- Modal-safe kebab is baked into KebabMenu (this row lives in the
|
||||
teleported image modal — #711). -->
|
||||
teleported image modal — #711). Only rendered when an alias action
|
||||
applies — dismiss now lives on the red ✗, so a centroid hit with no
|
||||
alias option has no menu. -->
|
||||
<KebabMenu
|
||||
v-if="hasMenu"
|
||||
class="fc-suggestion__menu" size="small" variant="outlined"
|
||||
:label="`More actions for ${suggestion.display_name}`"
|
||||
>
|
||||
@@ -42,9 +56,6 @@
|
||||
>
|
||||
<v-list-item-title>Remove alias</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item @click="$emit('dismiss', suggestion)">
|
||||
<v-list-item-title>Dismiss for this image</v-list-item-title>
|
||||
</v-list-item>
|
||||
</KebabMenu>
|
||||
</div>
|
||||
</template>
|
||||
@@ -57,6 +68,12 @@ const props = defineProps({ suggestion: { type: Object, required: true } })
|
||||
defineEmits(['accept', 'alias', 'remove-alias', 'dismiss'])
|
||||
|
||||
const scorePct = computed(() => `${Math.round(props.suggestion.score * 100)}%`)
|
||||
// Kebab now only carries alias actions: show it when this suggestion can be
|
||||
// aliased (raw model key, not yet aliased) or is already aliased (so it can be
|
||||
// un-aliased). Centroid hits (no raw_name, no alias) have an empty menu → hide.
|
||||
const hasMenu = computed(() =>
|
||||
Boolean(props.suggestion.raw_name) || Boolean(props.suggestion.via_alias)
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -104,11 +121,22 @@ const scorePct = computed(() => `${Math.round(props.suggestion.score * 100)}%`)
|
||||
color: rgb(var(--v-theme-on-surface-variant, var(--v-theme-on-surface)));
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
}
|
||||
/* Vuetify's compact density doesn't shrink the tonal button enough
|
||||
for a tight row; clamp the min-width so Accept stays compact. */
|
||||
.fc-suggestion__accept :deep(.v-btn__content) {
|
||||
font-size: 12px; letter-spacing: 0.02em;
|
||||
/* Green ✓ / red ✗ verdict pair — same circular language as the eval card
|
||||
(TagEvalCard .fc-act) so accept/reject read identically across surfaces. */
|
||||
.fc-suggestion__acts {
|
||||
flex: 0 0 auto; display: flex; gap: 4px;
|
||||
}
|
||||
.fc-act {
|
||||
width: 26px; height: 26px; border-radius: 50%; border: none; cursor: pointer;
|
||||
display: flex; align-items: center; justify-content: center; color: #fff;
|
||||
opacity: 0.9; transition: transform 0.1s, opacity 0.1s;
|
||||
}
|
||||
.fc-act:hover { opacity: 1; transform: scale(1.1); }
|
||||
.fc-act:focus-visible {
|
||||
outline: 2px solid rgb(var(--v-theme-accent)); outline-offset: 1px;
|
||||
}
|
||||
.fc-act--yes { background: rgb(var(--v-theme-success)); }
|
||||
.fc-act--no { background: rgb(var(--v-theme-error)); }
|
||||
.fc-suggestion__menu {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user