Merge pull request 'Auto-tag accept/reject as an in-pill yes/no pair; confirm refocuses the tag input' (#200) from dev into main
Build images / sign-extension (push) Successful in 3s
CI / lint (push) Successful in 3s
Build images / build-ml (push) Successful in 7s
Build images / build-agent (push) Successful in 8s
Build images / build-web (push) Successful in 10s
CI / frontend-build (push) Successful in 22s
CI / backend-lint-and-test (push) Successful in 27s
CI / integration (push) Successful in 3m44s
Build images / sign-extension (push) Successful in 3s
CI / lint (push) Successful in 3s
Build images / build-ml (push) Successful in 7s
Build images / build-agent (push) Successful in 8s
Build images / build-web (push) Successful in 10s
CI / frontend-build (push) Successful in 22s
CI / backend-lint-and-test (push) Successful in 27s
CI / integration (push) Successful in 3m44s
This commit was merged in pull request #200.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<span class="fc-tag-chip" @mouseenter="onEnter" @mouseleave="onLeave">
|
||||
<v-chip
|
||||
size="small" closable
|
||||
size="small" :closable="!unconfirmedAuto"
|
||||
:color="store.colorFor(tag.kind)" variant="tonal"
|
||||
class="fc-tag-chip__nav"
|
||||
role="link"
|
||||
@@ -16,20 +16,26 @@
|
||||
>mdi-shield-outline</v-icon><span
|
||||
v-if="tag.fandom_id" class="fc-tag-chip__fandom"
|
||||
:title="tag.fandom_name ? `Fandom: ${tag.fandom_name}` : 'Has a fandom'"
|
||||
>→<template v-if="fandomLabel"> {{ fandomLabel }}</template></span><span
|
||||
v-if="unconfirmedAuto" class="fc-tag-chip__auto"
|
||||
title="Auto-applied — provisional: it won't train the model and can be retracted until you confirm it."
|
||||
>auto</span>
|
||||
</v-chip>
|
||||
<!-- Keep/confirm an auto-applied tag: promotes it to a training positive and
|
||||
shields it from the retraction sweep (milestone 139). Only shown for
|
||||
provisional (unconfirmed) auto-tags. -->
|
||||
>→<template v-if="fandomLabel"> {{ fandomLabel }}</template></span>
|
||||
<!-- Provisional auto-tag: an in-pill yes/no pair REPLACES the ✕ (the pair
|
||||
itself signals "auto" — no separate label, operator 2026-07-07). Yes
|
||||
confirms it (trains + shields from retraction); No removes it (records
|
||||
a negative). Both hand focus back to the tag input via TagPanel. -->
|
||||
<span v-if="unconfirmedAuto" class="fc-tag-chip__verdict">
|
||||
<button
|
||||
v-if="unconfirmedAuto" class="fc-tag-chip__confirm" type="button"
|
||||
:title="`Keep “${tag.name}” — confirm this auto-tag so it trains the model and won't be retracted`"
|
||||
type="button" class="fc-tag-chip__yes"
|
||||
:title="`Yes — keep “${tag.name}” (trains the model, won't be retracted)`"
|
||||
:aria-label="`Confirm ${tag.name}`"
|
||||
@click.stop="$emit('confirm', tag)"
|
||||
><v-icon size="16">mdi-check</v-icon></button>
|
||||
><v-icon size="13">mdi-check</v-icon></button>
|
||||
<button
|
||||
type="button" class="fc-tag-chip__no"
|
||||
:title="`No — remove “${tag.name}”`"
|
||||
:aria-label="`Reject ${tag.name}`"
|
||||
@click.stop="$emit('remove', tag.id)"
|
||||
><v-icon size="13">mdi-close</v-icon></button>
|
||||
</span>
|
||||
</v-chip>
|
||||
<!-- Modal-safe kebab is baked into KebabMenu (this chip lives in the
|
||||
teleported image modal — #711). System tags hide it entirely: rename
|
||||
is refused server-side (the hygiene machinery keys on the row) and
|
||||
@@ -71,8 +77,8 @@ const store = useTagStore()
|
||||
const api = useApi()
|
||||
|
||||
// An auto-applied tag the operator hasn't confirmed yet — provisional (milestone
|
||||
// 139): it doesn't train the model and the retraction sweep can drop it. Shows
|
||||
// the "auto" badge + a Keep/confirm button. `source`/`confirmed` come from the
|
||||
// 139): it doesn't train the model and the retraction sweep can drop it. Shows an
|
||||
// in-pill yes/no pair in place of the ✕. `source`/`confirmed` come from the
|
||||
// applied-tags payload (list_for_image / get_image_with_tags).
|
||||
const unconfirmedAuto = computed(() =>
|
||||
AUTO_SOURCES.includes(props.tag.source) && !props.tag.confirmed
|
||||
@@ -130,10 +136,11 @@ function iconFor (k) { return KIND_ICONS[k] || 'mdi-tag' }
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* A chip must never exceed the rail — a long character+fandom(+AUTO) chip used
|
||||
to overflow the right edge and clip its close ✕ (operator-flagged 2026-07-06).
|
||||
The name is the elastic part: it ellipsis-truncates so the ✕, fandom, and AUTO
|
||||
badge stay reachable. Full name is still available via the chip's hover title. */
|
||||
/* A chip must never exceed the rail — a long character+fandom chip used to
|
||||
overflow the right edge and clip its trailing control (operator-flagged
|
||||
2026-07-06). The name is the elastic part: it ellipsis-truncates so the ✕ (or
|
||||
the auto-tag yes/no pair) and fandom stay reachable. Full name is on the
|
||||
chip's hover title. */
|
||||
.fc-tag-chip { display: inline-flex; align-items: center; gap: 1px; max-width: 100%; min-width: 0; }
|
||||
.fc-tag-chip__nav { max-width: 100%; min-width: 0; }
|
||||
.fc-tag-chip__nav :deep(.v-chip__content) { min-width: 0; overflow: hidden; }
|
||||
@@ -152,28 +159,23 @@ function iconFor (k) { return KIND_ICONS[k] || 'mdi-tag' }
|
||||
.fc-tag-chip__kebab { opacity: 0.7; }
|
||||
.fc-tag-chip:hover .fc-tag-chip__kebab { opacity: 1; }
|
||||
.fc-tag-chip__fandom { opacity: 0.7; font-size: 0.85em; }
|
||||
/* "auto" = provisional (auto-applied, unconfirmed). Quiet pill inside the chip. */
|
||||
.fc-tag-chip__auto {
|
||||
display: inline-block; vertical-align: middle; margin-left: 4px;
|
||||
font-size: 9px; font-weight: 700;
|
||||
text-transform: uppercase; letter-spacing: 0.04em;
|
||||
color: rgb(var(--v-theme-on-surface-variant));
|
||||
background: rgb(var(--v-theme-surface-light));
|
||||
border: 1px solid rgb(var(--v-theme-on-surface-variant), 0.3);
|
||||
padding: 0 4px; border-radius: 999px;
|
||||
/* Provisional auto-tag: a compact green ✓ / red ✗ pair in place of the ✕. The
|
||||
yes/no is obvious enough on its own, so there's no "auto" label (operator-asked
|
||||
2026-07-07). flex:0 0 auto keeps it visible while the name ellipsis-truncates. */
|
||||
.fc-tag-chip__verdict {
|
||||
flex: 0 0 auto; display: inline-flex; align-items: center; gap: 1px;
|
||||
margin-left: 3px;
|
||||
}
|
||||
/* Keep/confirm — the SAME filled green ✓ circle as the suggestion accept button
|
||||
(.fc-act--yes in SuggestionItem), so "accept this tag" reads identically
|
||||
wherever it appears (operator-asked 2026-07-06). */
|
||||
.fc-tag-chip__confirm {
|
||||
flex: 0 0 auto;
|
||||
width: 26px; height: 26px; border-radius: 50%; border: none; cursor: pointer;
|
||||
.fc-tag-chip__yes, .fc-tag-chip__no {
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
color: #fff; background: rgb(var(--v-theme-success));
|
||||
opacity: 0.9; transition: transform 0.1s, opacity 0.1s;
|
||||
width: 18px; height: 18px; padding: 0; border: none; border-radius: 50%;
|
||||
background: transparent; cursor: pointer; transition: background 0.1s;
|
||||
}
|
||||
.fc-tag-chip__confirm:hover { opacity: 1; transform: scale(1.1); }
|
||||
.fc-tag-chip__confirm:focus-visible {
|
||||
.fc-tag-chip__yes { color: rgb(var(--v-theme-success)); }
|
||||
.fc-tag-chip__no { color: rgb(var(--v-theme-error)); }
|
||||
.fc-tag-chip__yes:hover { background: rgb(var(--v-theme-success), 0.16); }
|
||||
.fc-tag-chip__no:hover { background: rgb(var(--v-theme-error), 0.16); }
|
||||
.fc-tag-chip__yes:focus-visible, .fc-tag-chip__no:focus-visible {
|
||||
outline: 2px solid rgb(var(--v-theme-accent)); outline-offset: 1px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -116,6 +116,10 @@ async function onConfirm(tag) {
|
||||
try {
|
||||
await api.post(`/api/images/${host.currentImageId}/tags/${tag.id}/confirm`)
|
||||
await host.reloadTags()
|
||||
// Return the cursor to the tag input — its resting position in these views,
|
||||
// matching every other chip action (remove/accept) so the keyboard flow
|
||||
// never leaves the field (operator-asked 2026-07-07).
|
||||
focusTagInput()
|
||||
}
|
||||
catch (e) { errorMsg.value = e.message }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user