feat(fc2b): AliasPickerDialog + TagRenameDialog
AliasPicker: debounced autocomplete scoped to the prediction's category (artist/character map to tag kinds; copyright searches unscoped), returns the chosen canonical tag id. RenameDialog: PATCH /api/tags/<id>, surfaces 409 collisions with the FC-2c merge hint. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<v-card>
|
||||
<v-card-title>Treat as alias for…</v-card-title>
|
||||
<v-card-text>
|
||||
<p class="text-caption mb-2">
|
||||
Pick the existing tag the model's prediction should map to. All
|
||||
future predictions of this name will resolve to your tag.
|
||||
</p>
|
||||
<v-autocomplete
|
||||
v-model="selectedId"
|
||||
:items="results"
|
||||
:item-title="(t) => t.fandom_name ? `${t.name} — ${t.fandom_name}` : t.name"
|
||||
:item-value="(t) => t.id"
|
||||
:loading="loading"
|
||||
label="Canonical tag"
|
||||
no-filter clearable density="compact"
|
||||
@update:search="onSearch"
|
||||
/>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn @click="$emit('cancel')">Cancel</v-btn>
|
||||
<v-btn
|
||||
color="primary" rounded="pill" :disabled="!selectedId"
|
||||
@click="$emit('confirm', selectedId)"
|
||||
>Use this tag</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { useApi } from '../../composables/useApi.js'
|
||||
|
||||
const props = defineProps({ category: { type: String, required: true } })
|
||||
defineEmits(['confirm', 'cancel'])
|
||||
|
||||
const api = useApi()
|
||||
const results = ref([])
|
||||
const loading = ref(false)
|
||||
const selectedId = ref(null)
|
||||
let debounce = null
|
||||
|
||||
function onSearch(q) {
|
||||
if (debounce) clearTimeout(debounce)
|
||||
debounce = setTimeout(async () => {
|
||||
const query = (q || '').trim()
|
||||
if (!query) { results.value = []; return }
|
||||
loading.value = true
|
||||
try {
|
||||
// Scope the autocomplete to the prediction's category where it maps
|
||||
// to a tag kind. 'copyright' has no tag kind; search unscoped there.
|
||||
const kind = ['artist', 'character'].includes(props.category)
|
||||
? props.category : null
|
||||
const params = { q: query, limit: 20 }
|
||||
if (kind) params.kind = kind
|
||||
results.value = await api.get('/api/tags/autocomplete', { params })
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}, 200)
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user