{
  "version": 3,
  "sources": ["../../assets/js/search-typeahead.js"],
  "sourcesContent": ["/**\n * Search typeahead / autocomplete for the header search input.\n *\n * Attaches to .search-input inside .search-form, fetches suggestions from\n * /search/suggestions/?q=..., and renders a dropdown with categorized\n * results including optional thumbnails.\n */\n\nconst DEBOUNCE_MS = 200;\nconst MIN_CHARS = 2;\nconst SUGGESTION_URL = '/search/suggestions/';\n\nfunction escapeHtml(str) {\n  const div = document.createElement('div');\n  div.textContent = str;\n  return div.innerHTML;\n}\n\nfunction init() {\n  const form = document.querySelector('.search-form');\n  if (!form) return;\n\n  const input = form.querySelector('.search-input');\n  if (!input) return;\n\n  // Create dropdown container\n  const dropdown = document.createElement('div');\n  dropdown.className = 'search-suggestions';\n  dropdown.setAttribute('role', 'listbox');\n  dropdown.style.display = 'none';\n\n  // Position the form as relative anchor for the absolute dropdown\n  form.style.position = 'relative';\n  form.appendChild(dropdown);\n\n  let debounceTimer = null;\n  let activeIndex = -1;\n  let currentItems = [];\n\n  function hide() {\n    dropdown.style.display = 'none';\n    dropdown.innerHTML = '';\n    activeIndex = -1;\n    currentItems = [];\n  }\n\n  function show() {\n    if (dropdown.children.length > 0) {\n      dropdown.style.display = 'block';\n    }\n  }\n\n  function setActive(index) {\n    const items = dropdown.querySelectorAll('.search-suggestion-item');\n    items.forEach((el) => el.classList.remove('active'));\n    activeIndex = index;\n    if (index >= 0 && index < items.length) {\n      items[index].classList.add('active');\n      items[index].scrollIntoView({ block: 'nearest' });\n    }\n  }\n\n  function renderSuggestions(suggestions) {\n    dropdown.innerHTML = '';\n    currentItems = suggestions;\n    activeIndex = -1;\n\n    if (suggestions.length === 0) {\n      hide();\n      return;\n    }\n\n    suggestions.forEach((item, i) => {\n      const el = document.createElement('a');\n      el.className = 'search-suggestion-item';\n      el.href = item.url;\n      el.setAttribute('role', 'option');\n\n      let thumbHtml = '';\n      if (item.image) {\n        thumbHtml = `<img src=\"${escapeHtml(item.image)}\" alt=\"\" class=\"search-suggestion-thumb\" loading=\"lazy\">`;\n      } else {\n        thumbHtml = '<span class=\"search-suggestion-thumb search-suggestion-thumb-placeholder\"></span>';\n      }\n\n      const typeLabels = { book: 'Book', author: 'Author', category: 'Category' };\n      const typeLabel = typeLabels[item.type] || item.type;\n\n      el.innerHTML = `${thumbHtml}<span class=\"search-suggestion-text\">${escapeHtml(item.text)}</span><span class=\"search-suggestion-type\">${escapeHtml(typeLabel)}</span>`;\n\n      el.addEventListener('mouseenter', () => setActive(i));\n      dropdown.appendChild(el);\n    });\n\n    show();\n  }\n\n  async function fetchSuggestions(query) {\n    try {\n      const res = await fetch(`${SUGGESTION_URL}?q=${encodeURIComponent(query)}`);\n      if (!res.ok) return;\n      const data = await res.json();\n      // Only render if input still matches (user may have typed more)\n      if (input.value.trim() === query) {\n        renderSuggestions(data.suggestions || []);\n      }\n    } catch {\n      // Silently fail \u2014 search form still works\n    }\n  }\n\n  input.addEventListener('input', () => {\n    clearTimeout(debounceTimer);\n    const q = input.value.trim();\n    if (q.length < MIN_CHARS) {\n      hide();\n      return;\n    }\n    debounceTimer = setTimeout(() => fetchSuggestions(q), DEBOUNCE_MS);\n  });\n\n  input.addEventListener('keydown', (e) => {\n    if (dropdown.style.display === 'none') return;\n\n    const items = dropdown.querySelectorAll('.search-suggestion-item');\n    const count = items.length;\n\n    if (e.key === 'ArrowDown') {\n      e.preventDefault();\n      setActive(activeIndex < count - 1 ? activeIndex + 1 : 0);\n    } else if (e.key === 'ArrowUp') {\n      e.preventDefault();\n      setActive(activeIndex > 0 ? activeIndex - 1 : count - 1);\n    } else if (e.key === 'Enter' && activeIndex >= 0) {\n      e.preventDefault();\n      const selected = items[activeIndex];\n      if (selected) window.location = selected.href;\n    } else if (e.key === 'Escape') {\n      hide();\n    }\n  });\n\n  // Dismiss on outside click\n  document.addEventListener('click', (e) => {\n    if (!form.contains(e.target)) {\n      hide();\n    }\n  });\n\n  // Dismiss on blur (with slight delay so click on suggestion registers)\n  input.addEventListener('blur', () => {\n    setTimeout(hide, 150);\n  });\n\n  // Re-show on focus if there are existing suggestions\n  input.addEventListener('focus', () => {\n    if (input.value.trim().length >= MIN_CHARS && dropdown.children.length > 0) {\n      show();\n    }\n  });\n}\n\nif (document.readyState === 'loading') {\n  document.addEventListener('DOMContentLoaded', init);\n} else {\n  init();\n}\n"],
  "mappings": "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",
  "names": ["SUGGESTION_URL", "escapeHtml", "str", "div", "init", "form", "input", "dropdown", "debounceTimer", "activeIndex", "currentItems", "hide", "show", "setActive", "index", "items", "el", "renderSuggestions", "suggestions", "item", "thumbHtml", "typeLabel", "fetchSuggestions", "query", "res", "data", "q", "count", "selected"]
}
