a2d1ed935d
- Promote .fc-section-h to a global token (app.css). It was copied identically into 4 cards, and TranslationCard used the class with NO local def — so its section headers rendered unstyled. Now fixed everywhere. - Promote .fc-good / .fc-weak status colours to globals; delete the local copies in the GPU/heads cards. (.fc-ok stays local — divergent: on-surface in HeadsCard vs success in QueuesTable. .fc-bad stays — different name.) - Delete 10 identical local .fc-muted redefinitions that crept back after the 2026-06-09 sweep; the global utility already covers them. - DbMaintenanceCard: opacity:0.6 muted text → the .fc-muted token (the exact anti-pattern that token's comment forbids). - HeadsCard: collapse byte-identical ratePct() into pct(). CSS-only + one template class swap; no logic change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NsmJSQxnNxGgtM5Yz4GAqi
122 lines
4.0 KiB
Vue
122 lines
4.0 KiB
Vue
<template>
|
|
<MaintenanceTile
|
|
icon="mdi-nuke"
|
|
title="Reset content tagging (whole instance)"
|
|
blurb="Delete ALL general/character tags and their applications — a start-over. Requires a confirmation code."
|
|
destructive
|
|
>
|
|
<p class="text-body-2 mb-2">
|
|
Deletes every <code>general</code> and <code>character</code> tag and
|
|
removes them from every image — <strong>including the examples the
|
|
tagging heads learned from</strong>. Suggestions will <strong>not</strong>
|
|
repopulate on their own: you re-tag from scratch, and the heads retrain
|
|
from your new tags as they accumulate. Fandoms and series (with their
|
|
page order) are kept.
|
|
</p>
|
|
<v-alert type="error" variant="tonal" density="compact" class="mb-3">
|
|
Irreversible — no undo except restoring a DB backup
|
|
(Settings → Maintenance → Backup). Back one up first.
|
|
</v-alert>
|
|
|
|
<v-btn
|
|
color="accent" variant="flat" rounded="pill"
|
|
prepend-icon="mdi-magnify"
|
|
:loading="loadingPreview"
|
|
class="mb-3"
|
|
@click="onPreview"
|
|
>Preview content-tag reset</v-btn>
|
|
|
|
<div v-if="preview">
|
|
<p class="text-body-2 mb-2">
|
|
<strong>{{ preview.count }}</strong> content tag(s)
|
|
<span v-for="(n, k) in preview.by_kind" :key="k" class="fc-muted">
|
|
({{ k }}: {{ n }})
|
|
</span>
|
|
across <strong>{{ preview.applications }}</strong> image
|
|
application(s).
|
|
</p>
|
|
<SampleNameGrid
|
|
v-if="preview.sample_names?.length"
|
|
:names="preview.sample_names" class="mb-3"
|
|
/>
|
|
<template v-if="preview.count">
|
|
<p class="text-body-2 mb-2">
|
|
To arm the reset, type the confirmation code
|
|
<code class="fc-code">{{ preview.confirm }}</code> below.
|
|
</p>
|
|
<div class="d-flex align-center mb-1" style="gap: 12px">
|
|
<v-text-field
|
|
v-model="typed" density="compact" hide-details variant="outlined"
|
|
label="Confirmation code" style="max-width: 200px"
|
|
autocomplete="off" spellcheck="false"
|
|
/>
|
|
<v-btn
|
|
color="error" variant="flat" rounded="pill"
|
|
prepend-icon="mdi-delete-alert"
|
|
:disabled="typed !== preview.confirm"
|
|
:loading="committing"
|
|
@click="onCommit"
|
|
>Delete {{ preview.count }} tag(s) +
|
|
{{ preview.applications }} application(s)</v-btn>
|
|
</div>
|
|
<p class="fc-muted text-caption mb-0">
|
|
The code is derived from the counts above — if tagging changes
|
|
between preview and apply, the server rejects the stale code.
|
|
</p>
|
|
</template>
|
|
</div>
|
|
</MaintenanceTile>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref } from 'vue'
|
|
|
|
import { toast } from '../../utils/toast.js'
|
|
import MaintenanceTile from '../common/MaintenanceTile.vue'
|
|
import SampleNameGrid from '../common/SampleNameGrid.vue'
|
|
import { useAdminStore } from '../../stores/admin.js'
|
|
|
|
const store = useAdminStore()
|
|
const preview = ref(null)
|
|
const loadingPreview = ref(false)
|
|
const committing = ref(false)
|
|
const typed = ref('')
|
|
|
|
async function onPreview() {
|
|
loadingPreview.value = true
|
|
typed.value = ''
|
|
try {
|
|
preview.value = await store.resetContentTagging({ dryRun: true })
|
|
} catch (e) {
|
|
toast({ text: `Preview failed: ${e.message}`, type: 'error' })
|
|
} finally {
|
|
loadingPreview.value = false
|
|
}
|
|
}
|
|
|
|
async function onCommit() {
|
|
committing.value = true
|
|
try {
|
|
const res = await store.resetContentTagging({
|
|
dryRun: false, confirm: typed.value,
|
|
})
|
|
toast({ text: `Deleted ${res.deleted} content tag(s) — re-tagging starts fresh`, type: 'success' })
|
|
preview.value = null
|
|
typed.value = ''
|
|
} catch (e) {
|
|
toast({ text: `Reset rejected: ${e.message}`, type: 'error' })
|
|
} finally {
|
|
committing.value = false
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.fc-code {
|
|
background: rgb(var(--v-theme-surface-light));
|
|
border-radius: 4px; padding: 2px 8px;
|
|
font-family: 'JetBrains Mono', monospace; font-weight: 700;
|
|
letter-spacing: 0.06em;
|
|
}
|
|
</style>
|