fix(modal): autocomplete picks resolve by tag_id, not just by name
Clicking an autocomplete row (or pressing Enter on a highlighted row) now sends tag_id in the add-tag POST. Server attaches that exact tag, skipping parse_kind_prefix. Same-name ambiguity — e.g. a character tag 'Yidhari' and a user tag 'Yidhari' both matching the autocomplete query 'yid' — now resolves by whichever row the user picked, instead of silently routing to the user-kind fallback. Bare free-text input (no click) still goes through parse_kind_prefix with its kind: shortcut and kind='user' default. The fandom picker path is untouched. The click handler also stashes tag_id on the dataset so the keyboard Enter path on a highlighted row picks up the same resolution. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+25
-13
@@ -658,13 +658,20 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
});
|
||||
}
|
||||
|
||||
async function addTag(imageId, name, { fandomId, fandomName } = {}) {
|
||||
async function addTag(imageId, name, { tagId, fandomId, fandomName } = {}) {
|
||||
const fd = new FormData();
|
||||
fd.append('name', name);
|
||||
if (fandomId) {
|
||||
fd.append('fandom_id', String(fandomId));
|
||||
} else if (fandomName) {
|
||||
fd.append('fandom_name', fandomName);
|
||||
if (tagId) {
|
||||
// Autocomplete-picker path: server attaches this exact tag, skipping
|
||||
// parse_kind_prefix so a same-name character/user ambiguity is
|
||||
// resolved by the row the user clicked.
|
||||
fd.append('tag_id', String(tagId));
|
||||
} else {
|
||||
fd.append('name', name);
|
||||
if (fandomId) {
|
||||
fd.append('fandom_id', String(fandomId));
|
||||
} else if (fandomName) {
|
||||
fd.append('fandom_name', fandomName);
|
||||
}
|
||||
}
|
||||
const r = await fetch(`/image/${imageId}/tags/add`, { method: 'POST', body: fd });
|
||||
const j = await r.json();
|
||||
@@ -747,7 +754,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
tagAutocomplete.innerHTML = tags.map((t, i) => {
|
||||
const icon = getTagIcon(t.kind);
|
||||
const displayName = getTagDisplayName(t.name);
|
||||
return `<div class="tag-autocomplete-item" data-index="${i}" data-name="${t.name}">
|
||||
return `<div class="tag-autocomplete-item" data-index="${i}" data-tag-id="${t.id}" data-name="${t.name}" data-kind="${t.kind || ''}">
|
||||
<span>${icon} ${displayName}</span>
|
||||
<span class="tag-kind">${t.kind || 'user'}</span>
|
||||
</div>`;
|
||||
@@ -778,14 +785,18 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
}
|
||||
|
||||
// Shared function to submit a tag (avoids page refresh issues with form dispatch)
|
||||
async function submitTag() {
|
||||
// Shared function to submit a tag (avoids page refresh issues with form dispatch).
|
||||
// `pickedTagId` is set when the user clicks an autocomplete row or presses
|
||||
// Enter on a highlighted row — in that case, skip parsing and attach that
|
||||
// exact tag so same-name character/user ambiguity is resolved by the pick.
|
||||
async function submitTag(pickedTagId = null) {
|
||||
const id = getEditorImageId();
|
||||
const name = (tagInput?.value || '').trim();
|
||||
if (!id || !name) return;
|
||||
if (!id || (!name && !pickedTagId)) return;
|
||||
const fandomId = fandomPickerInput?.dataset?.fandomId;
|
||||
const fandomName = (fandomPickerInput?.value || '').trim();
|
||||
const ok = await addTag(id, name, {
|
||||
tagId: pickedTagId || null,
|
||||
fandomId: fandomId ? Number(fandomId) : null,
|
||||
fandomName: fandomName || null,
|
||||
});
|
||||
@@ -839,8 +850,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
e.preventDefault();
|
||||
const selected = autocompleteItems[autocompleteSelectedIndex];
|
||||
if (selected) {
|
||||
tagInput.value = selected.name;
|
||||
submitTag();
|
||||
tagInput.value = selected.display_name || selected.name;
|
||||
submitTag(selected.id);
|
||||
}
|
||||
} else if (e.key === 'Escape') {
|
||||
selectAutocompleteItem(-1);
|
||||
@@ -857,9 +868,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const item = e.target.closest('.tag-autocomplete-item');
|
||||
if (!item) return;
|
||||
const name = item.dataset.name;
|
||||
const tagId = item.dataset.tagId ? Number(item.dataset.tagId) : null;
|
||||
if (tagInput && name) {
|
||||
tagInput.value = name;
|
||||
submitTag();
|
||||
submitTag(tagId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user