UI batch: tagging flow, series browse, fandom chips, nav #89

Merged
bvandeusen merged 5 commits from dev into main 2026-06-09 20:48:19 -04:00
3 changed files with 18 additions and 0 deletions
Showing only changes of commit 4958e8f7d4 - Show all commits
@@ -46,6 +46,9 @@ import SuggestionsCategoryGroup from './SuggestionsCategoryGroup.vue'
import AliasPickerDialog from './AliasPickerDialog.vue'
const props = defineProps({ imageId: { type: Number, required: true } })
// 'accepted' lets the parent return focus to the tag input after a suggestion is
// applied (operator-asked 2026-06-08).
const emit = defineEmits(['accepted'])
const store = useSuggestionsStore()
const modal = useModalStore()
@@ -72,6 +75,7 @@ async function onAccept(s) {
try {
await store.accept(s)
await modal.reloadTags()
emit('accepted')
} catch (e) {
toast({ text: `Accept failed: ${e.message}`, type: 'error' })
}
@@ -85,6 +89,7 @@ async function onAliasConfirm(canonicalTagId) {
await store.aliasAccept(aliasTarget.value, canonicalTagId)
aliasDialog.value = false
await modal.reloadTags()
emit('accepted')
} catch (e) {
toast({ text: `Alias failed: ${e.message}`, type: 'error' })
}
@@ -123,6 +123,10 @@ function focusInput () {
nextTick(() => inputRef.value?.focus?.())
}
onMounted(focusInput)
// Exposed so the parent (TagPanel) can hand focus back to this field after an
// auto-suggestion is accepted, keeping the keyboard flow on the input instead
// of dropping to <body> (operator-asked 2026-06-08). Mobile guard preserved.
defineExpose({ focus: focusInput })
// Single text input; no kind dropdown. Client-side mirror of the
// backend's parse_kind_prefix lives below — kept in sync with
@@ -13,6 +13,7 @@
<v-divider class="my-3" />
<TagAutocomplete
ref="tagInputRef"
@pick-existing="onPickExisting" @pick-new="onPickNew"
@accept-suggestion="onAcceptSuggestion"
/>
@@ -24,6 +25,7 @@
<SuggestionsPanel
v-if="modal.currentImageId != null"
:image-id="modal.currentImageId"
@accepted="focusTagInput"
/>
<v-dialog v-model="renameDialog" max-width="420">
@@ -60,6 +62,12 @@ import FandomSetDialog from './FandomSetDialog.vue'
const modal = useModalStore()
const suggestions = useSuggestionsStore()
const errorMsg = ref(null)
const tagInputRef = ref(null)
// Return focus to the tag input after a suggestion is accepted (from the
// Suggestions panel or the autocomplete dropdown) so the operator can keep
// typing the next tag without re-clicking the field (operator-asked 2026-06-08).
function focusTagInput() { tagInputRef.value?.focus?.() }
async function onRemove(tagId) {
errorMsg.value = null
@@ -84,6 +92,7 @@ async function onAcceptSuggestion(s) {
try {
await suggestions.accept(s)
await modal.reloadTags()
focusTagInput()
} catch (e) { errorMsg.value = e.message }
}