function createTitleAutocomplete({ inputId, dropdownId, onSelect }) { const input = document.getElementById(inputId); const dropdown = document.getElementById(dropdownId); let candidates = []; let highlight = -1; let timer = null; function itemTemplate(c, index) { const img = c.artworkUrl ? `` : ``; return `
${img}
${escapeHtml(c.title)}
${escapeHtml(c.artist)}
`; } function render() { if (!candidates.length) { dropdown.style.display = 'none'; dropdown.innerHTML = ''; return; } dropdown.innerHTML = candidates.map(itemTemplate).join(''); dropdown.style.display = 'block'; dropdown.querySelectorAll('.autocomplete-item').forEach((el) => { el.addEventListener('mousedown', (e) => { e.preventDefault(); select(candidates[parseInt(el.dataset.index, 10)]); }); }); } function close() { candidates = []; highlight = -1; render(); } function select(candidate) { close(); onSelect(candidate); } input.addEventListener('input', () => { const query = input.value.trim(); clearTimeout(timer); if (query.length < 2) { close(); return; } timer = setTimeout(async () => { try { candidates = await api.get(`/api/music-search?q=${encodeURIComponent(query)}`); highlight = -1; render(); } catch (err) { close(); } }, 300); }); input.addEventListener('keydown', (e) => { if (!candidates.length) return; if (e.key === 'ArrowDown') { e.preventDefault(); highlight = Math.min(highlight + 1, candidates.length - 1); render(); } else if (e.key === 'ArrowUp') { e.preventDefault(); highlight = Math.max(highlight - 1, 0); render(); } else if (e.key === 'Enter' && highlight >= 0) { e.preventDefault(); select(candidates[highlight]); } else if (e.key === 'Escape') { close(); } }); input.addEventListener('blur', () => { setTimeout(close, 150); }); return { close }; }