feat: retire the sketch/doodle WIP title tier, and the review strip asks "Is this a WIP?" (milestone 430)
CI and images / lint (push) Successful in 2s
CI and images / extension-version (push) Successful in 3s
CI and images / extension-test (push) Successful in 20s
CI and images / frontend-build (push) Successful in 23s
CI and images / backend-lint-and-test (push) Successful in 32s
CI and images / integration (push) Successful in 2m24s
CI and images / sign-extension (push) Successful in 3s
CI and images / build-agent (push) Successful in 5s
CI and images / build-web (push) Successful in 1m35s
CI and images / smoke-web (push) Successful in 56s
CI and images / promote (push) Successful in 1s
CI and images / lint (push) Successful in 2s
CI and images / extension-version (push) Successful in 3s
CI and images / extension-test (push) Successful in 20s
CI and images / frontend-build (push) Successful in 23s
CI and images / backend-lint-and-test (push) Successful in 32s
CI and images / integration (push) Successful in 2m24s
CI and images / sign-extension (push) Successful in 3s
CI and images / build-agent (push) Successful in 5s
CI and images / build-web (push) Successful in 1m35s
CI and images / smoke-web (push) Successful in 56s
CI and images / promote (push) Successful in 1s
The soft tier (#1474) tagged `wip` on any post titled sketch, doodle or scribble: 6,096 of the library's 8,876 wip tags. Its conflict audit flagged most of them, because finished art scores >= 0.5 on some content head, which filled the Gallery strip with 2,086 cards. A "sketch" is usually finished work, so the operator retired the tier. The artist's own "WIP" / "work in progress" title rule and human wip tags stay. - Removed: the soft matcher, source and prefilter; the importer and backfill soft branches; soft_wip_conflict_audit with its task and beat; the wip_soft_title_tagging_enabled setting, API field and toggle; and wip_title_soft from _AUTO_SOURCES. - Migration 0112: a soft tag the operator confirmed, or kept from the strip, becomes `manual`. Every other soft tag is deleted, along with the open review cards whose tag is gone. The column is dropped. - Review strip (#4424): each card asks "Is this a WIP?" (or banner, and so on), and the buttons read "Is a WIP" / "Is not a WIP". The content tag it also scored on is shown as the reason it was flagged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVjrnpQjRgHdvq95rASoiR
This commit is contained in:
@@ -2,14 +2,14 @@
|
||||
<!-- System-tag auto-applies (chrome hides / process WIP tags) that ALSO looked
|
||||
like real content — surfaced PROACTIVELY atop the gallery whenever there's
|
||||
something to review (NOT gated on the Show-hidden toggle, so misfires can't
|
||||
go unnoticed), most-concerning first, with keep / remove (#141, #1464).
|
||||
go unnoticed), most-concerning first (#141, #1464). Each card asks one
|
||||
question — is this image a <tag>? — and its buttons answer it (#4424).
|
||||
Renders nothing when there's nothing to review. -->
|
||||
<section v-if="items.length" class="fc-review" aria-label="Auto-tagged images to review">
|
||||
<div class="fc-review__head">
|
||||
<v-icon size="18" color="warning">mdi-alert-outline</v-icon>
|
||||
<span class="fc-review__title">
|
||||
{{ items.length }} auto-tagged {{ items.length === 1 ? 'image' : 'images' }}
|
||||
may be real content — review
|
||||
{{ items.length }} {{ items.length === 1 ? 'auto-tag' : 'auto-tags' }} to check
|
||||
</span>
|
||||
</div>
|
||||
<div class="fc-review__cards">
|
||||
@@ -21,22 +21,23 @@
|
||||
class="fc-review-card__thumb" loading="lazy"
|
||||
>
|
||||
<div class="fc-review-card__body">
|
||||
<div class="fc-review-card__question">{{ question(it) }}</div>
|
||||
<div
|
||||
class="fc-review-card__conflict"
|
||||
:title="`Scored ${Math.round(it.conflict_score * 100)}% on “${it.conflict_name || 'a content tag'}”`"
|
||||
class="fc-review-card__reason"
|
||||
:title="reasonTitle(it)"
|
||||
>
|
||||
also looks like <strong>{{ it.conflict_name || 'content' }}</strong>
|
||||
also {{ Math.round(it.conflict_score * 100) }}%
|
||||
<strong>{{ it.conflict_name || 'content' }}</strong>
|
||||
</div>
|
||||
<div class="fc-review-card__tag">{{ tagLine(it) }}</div>
|
||||
<div class="fc-review-card__acts">
|
||||
<button
|
||||
type="button" class="fc-review-btn fc-review-btn--keep"
|
||||
:disabled="busy.includes(keyOf(it))" @click="resolve(it, 'keep')"
|
||||
>{{ keepLabel(it) }}</button>
|
||||
>Is {{ withArticle(it) }}</button>
|
||||
<button
|
||||
type="button" class="fc-review-btn fc-review-btn--unhide"
|
||||
:disabled="busy.includes(keyOf(it))" @click="resolve(it, 'unhide')"
|
||||
>{{ removeLabel(it) }}</button>
|
||||
>Is not {{ withArticle(it) }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -55,11 +56,18 @@ const items = ref([])
|
||||
const busy = ref([])
|
||||
|
||||
function keyOf(it) { return `${it.image_id}:${it.tag_id}` }
|
||||
// Chrome flags hide the image (keep-hidden / un-hide); process flags leave it
|
||||
// visible and just tagged (keep-tag / remove-tag). Same endpoints, different words.
|
||||
function tagLine(it) { return (it.mode === 'process' ? 'auto-tagged ' : 'hidden as ') + it.tag_name }
|
||||
function keepLabel(it) { return it.mode === 'process' ? 'Keep tag' : 'Keep hidden' }
|
||||
function removeLabel(it) { return it.mode === 'process' ? 'Remove tag' : 'Un-hide' }
|
||||
// The card asks whether the image IS the auto-applied system tag, and the buttons
|
||||
// answer that (operator, #4424: "is a <tag>" / "is not a <tag>"). "Is" keeps the
|
||||
// tag ('keep'); "Is not" removes it, un-hiding a chrome image ('unhide'). The
|
||||
// content tag it also scored on is the reason it was flagged, not the question.
|
||||
function noun(it) { return it.tag_name === 'wip' ? 'WIP' : it.tag_name }
|
||||
function withArticle(it) { return (/^[aeiou]/i.test(noun(it)) ? 'an ' : 'a ') + noun(it) }
|
||||
function question(it) { return `Is this ${withArticle(it)}?` }
|
||||
function reasonTitle(it) {
|
||||
const hidden = it.mode === 'process' ? '' : ' It is hidden from the gallery until you answer.'
|
||||
return `Auto-tagged “${it.tag_name}”, but it also scored ${Math.round(it.conflict_score * 100)}% `
|
||||
+ `on “${it.conflict_name || 'a content tag'}”, so it may be finished art.${hidden}`
|
||||
}
|
||||
|
||||
async function load() {
|
||||
// Fetched unconditionally on mount — the strip prompts for pending misfires
|
||||
@@ -77,14 +85,11 @@ async function resolve(it, action) {
|
||||
await api.post(`/api/gallery/hidden-review/${it.image_id}/${it.tag_id}/${action}`)
|
||||
items.value = items.value.filter((x) => keyOf(x) !== k)
|
||||
if (action === 'unhide') {
|
||||
const verb = it.mode === 'process' ? 'Removed' : 'Un-hidden'
|
||||
toast({ text: `${verb} — “${it.tag_name}” removed; it'll train the head`, type: 'success' })
|
||||
const shown = it.mode === 'process' ? '' : ', back in the gallery'
|
||||
toast({ text: `Not ${withArticle(it)} — “${it.tag_name}” removed${shown}; the tagger learns from it`, type: 'success' })
|
||||
}
|
||||
} catch (e) {
|
||||
toast({
|
||||
text: `Could not ${action === 'keep' ? 'keep hidden' : 'un-hide'}: ${e.message}`,
|
||||
type: 'error',
|
||||
})
|
||||
toast({ text: `Could not save your answer: ${e.message}`, type: 'error' })
|
||||
} finally {
|
||||
busy.value = busy.value.filter((x) => x !== k)
|
||||
}
|
||||
@@ -112,7 +117,7 @@ onMounted(load)
|
||||
display: flex; gap: 10px; overflow-x: auto; padding-bottom: 4px;
|
||||
}
|
||||
.fc-review-card {
|
||||
flex: 0 0 auto; width: 150px;
|
||||
flex: 0 0 auto; width: 170px;
|
||||
display: flex; flex-direction: column;
|
||||
border: 1px solid rgb(var(--v-theme-surface-light));
|
||||
border-radius: 6px; overflow: hidden;
|
||||
@@ -123,16 +128,16 @@ onMounted(load)
|
||||
background: rgb(var(--v-theme-surface-light));
|
||||
}
|
||||
.fc-review-card__body { padding: 6px 8px; }
|
||||
.fc-review-card__conflict {
|
||||
font-size: 11px; color: rgb(var(--v-theme-on-surface));
|
||||
.fc-review-card__question {
|
||||
font-size: 12px; font-weight: 600; color: rgb(var(--v-theme-on-surface));
|
||||
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||
}
|
||||
.fc-review-card__conflict strong { color: rgb(var(--v-theme-warning)); }
|
||||
.fc-review-card__tag {
|
||||
.fc-review-card__reason {
|
||||
font-size: 10px; color: rgb(var(--v-theme-on-surface-variant));
|
||||
margin: 1px 0 6px;
|
||||
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||
}
|
||||
.fc-review-card__reason strong { color: rgb(var(--v-theme-warning)); font-weight: 600; }
|
||||
.fc-review-card__acts { display: flex; gap: 4px; }
|
||||
.fc-review-btn {
|
||||
flex: 1; font-size: 11px; padding: 3px 4px; border-radius: 4px;
|
||||
|
||||
@@ -103,17 +103,6 @@
|
||||
the Explore browse. Applies to new imports; run the scan below to catch
|
||||
posts already in your library.
|
||||
</div>
|
||||
<v-switch
|
||||
v-model="local.wip_soft_title_tagging_enabled"
|
||||
label="Also tag “sketch” / “doodle” titles (lower precision)"
|
||||
density="compact" hide-details color="primary" @change="save"
|
||||
/>
|
||||
<div class="fc-help mb-3">
|
||||
Extends the above to softer cues (<code>sketch</code>, <code>doodle</code>,
|
||||
<code>scribble</code>). These stay <strong>visible</strong> and never train
|
||||
the tagging model — a daily audit flags any that actually look like finished
|
||||
art for review. Off by default.
|
||||
</div>
|
||||
<v-btn
|
||||
variant="tonal" color="primary" size="small"
|
||||
:loading="store.wipScanBusy" prepend-icon="mdi-magnify"
|
||||
@@ -156,7 +145,6 @@ const local = reactive({
|
||||
skip_single_color: false, single_color_threshold: 0.95,
|
||||
phash_threshold: 24,
|
||||
wip_title_tagging_enabled: true,
|
||||
wip_soft_title_tagging_enabled: false,
|
||||
})
|
||||
|
||||
watch(() => store.settings, (s) => { if (s) Object.assign(local, s) }, { immediate: true })
|
||||
|
||||
Reference in New Issue
Block a user