text: /* global lunr */
  (() => {
    if (!window.$docsify) {
      throw new Error('Docsify must be loaded before installing this plugin.');
    }
  
    window.$docsify.plugins.push(hook => {
      // Append the search box to the sidebar
      hook.mounted(() => {
        const appName = document.querySelector('.sidebar .app-name');
        const searchBox = document.createElement('div');
        searchBox.classList.add('search-box');
        searchBox.innerHTML = `
          <sl-input
            type="search"
            placeholder="Search"
            clearable
            pill
          >
            <sl-icon slot="prefix" name="search"></sl-icon>
            <kbd slot="suffix" title="Press / to search">/</kbd>
          </sl-input>
        `;
        const searchBoxInput = searchBox.querySelector('sl-input');
  
        appName.insertAdjacentElement('afterend', searchBox);
  
        // Show the search panel when the search is clicked
        searchBoxInput.addEventListener('mousedown', event => {
          event.preventDefault();
          show();
        });
  
        // Show the search panel when a key is pressed
        searchBoxInput.addEventListener('keydown', event => {
          if (event.key === 'Tab') {
            return;
          }
  
          // Pass the character that was typed through to the search input
          if (event.key.length === 1) {
            event.preventDefault();
            input.value = event.key;
            show();
          }
        });
      });
  
      // Append the search panel to the body
      const siteSearch = document.createElement('div');
      siteSearch.classList.add('site-search');
      siteSearch.hidden = true;
      siteSearch.innerHTML = `
        <div class="site-search__overlay"></div>
        <div class="site-search__panel">
          <header class="site-search__header">
            <sl-input
              class="site-search__input"
              type="search"
              placeholder="Search this site"
              size="large"
              clearable
            >
              <sl-icon slot="prefix" name="search"></sl-icon>
            </sl-input>
          </header>
          <div class="site-search__body">
            <ul class="site-search__results"></ul>
            <div class="site-search__empty">No results found.</div>
          </div>
          <footer class="site-search__footer">
            <small><kbd>↑</kbd> <kbd>↓</kbd> navigate</small>
            <small><kbd>↲</kbd> select</small>
            <small><kbd>esc</kbd> close</small>
          </footer>
        </div>
      `;
      document.body.append(siteSearch);
  
      const overlay = siteSearch.querySelector('.site-search__overlay');
      const panel = siteSearch.querySelector('.site-search__panel');
      const input = siteSearch.querySelector('.site-search__input');
      const results = siteSearch.querySelector('.site-search__results');
      const animationDuration = 150;
      const searchDebounce = 200;
      let isShowing = false;
      let searchTimeout;
      let searchIndex;
      let map;
  
      // Load search data
      fetch('../../../search.json')
        .then(res => res.json())
        .then(data => {
          searchIndex = lunr.Index.load(data.searchIndex);
          map = data.map;
        });
  
      async function show() {
        isShowing = true;
        document.body.classList.add('site-search-visible');
        siteSearch.hidden = false;
        input.focus();
        updateResults();
  
        await Promise.all([
          panel.animate(
            [
              { opacity: 0, transform: 'scale(.9)' },
              { opacity: 1, transform: 'scale(1)' }
            ],
            { duration: animationDuration }
          ).finished,
          overlay.animate([{ opacity: 0 }, { opacity: 1 }], { duration: animationDuration }).finished
        ]);
  
        document.addEventListener('mousedown', handleDocumentMouseDown);
        document.addEventListener('keydown', handleDocumentKeyDown);
        document.addEventListener('focusin', handleDocumentFocusIn);
      }
  
      async function hide() {
        isShowing = false;
        document.body.classList.remove('site-search-visible');
  
        await Promise.all([
          panel.animate(
            [
              { opacity: 1, transform: 'scale(1)' },
              { opacity: 0, transform: 'scale(.9)' }
            ],
            { duration: animationDuration }
          ).finished,
          overlay.animate([{ opacity: 1 }, { opacity: 0 }], { duration: animationDuration }).finished
        ]);
  
        siteSearch.hidden = true;
        input.value = '';
        updateResults();
  
        document.removeEventListener('mousedown', handleDocumentMouseDown);
        document.removeEventListener('keydown', handleDocumentKeyDown);
        document.removeEventListener('focusin', handleDocumentFocusIn);
      }
  
      function handleInput() {
        // Debounce search queries
        clearTimeout(searchTimeout);
        searchTimeout = setTimeout(() => updateResults(input.value), searchDebounce);
      }
  
      function handleDocumentFocusIn(event) {
        // Close when focus leaves the panel
        if (event.target.closest('.site-search__panel') !== panel) {
          hide();
        }
      }
  
      function handleDocumentMouseDown(event) {
        // Close when clicking outside of the panel
        if (event.target.closest('.site-search__overlay') === overlay) {
          hide();
        }
      }
  
      function handleDocumentKeyDown(event) {
        // Close when pressing escape
        if (event.key === 'Escape') {
          event.preventDefault();
          hide();
          return;
        }
  
        // Handle keyboard selections
        if (['ArrowDown', 'ArrowUp', 'Home', 'End', 'Enter'].includes(event.key)) {
          event.preventDefault();
  
          const currentEl = results.querySelector('[aria-selected="true"]');
          const items = [...results.querySelectorAll('li')];
          const index = items.indexOf(currentEl);
          let nextEl;
  
          if (items.length === 0) {
            return;
          }
  
          switch (event.key) {
            case 'ArrowUp':
              nextEl = items[Math.max(0, index - 1)];
              break;
            case 'ArrowDown':
              nextEl = items[Math.min(items.length - 1, index + 1)];
              break;
            case 'Home':
              nextEl = items[0];
              break;
            case 'End':
              nextEl = items[items.length - 1];
              break;
            case 'Enter':
              currentEl?.querySelector('a')?.click();
              break;
          }
  
          // Update the selected item
          items.forEach(item => {
            if (item === nextEl) {
              item.setAttribute('aria-selected', 'true');
              nextEl.scrollIntoView({ block: 'nearest' });
            } else {
              item.setAttribute('aria-selected', 'false');
            }
          });
        }
      }
  
      async function updateResults(query = '') {
        try {
          await searchIndex;
  
          const hasQuery = query.length > 0;
          const searchTokens = query
            .split(' ')
            .map((term, index, arr) => `${term}${index === arr.length - 1 ? `* ${term}~1` : '~1'}`)
            .join(' ');
          const matches = hasQuery ? searchIndex.search(`${query} ${searchTokens}`) : [];
          const hasResults = hasQuery && matches.length > 0;
          siteSearch.classList.toggle('site-search--has-results', hasQuery && hasResults);
          siteSearch.classList.toggle('site-search--no-results', hasQuery && !hasResults);
          panel.setAttribute('aria-expanded', hasQuery && hasResults ? 'true' : 'false');
  
          results.innerHTML = '';
  
          matches.forEach((match, index) => {
            const page = map[match.ref];
            const li = document.createElement('li');
            const a = document.createElement('a');
            let icon = 'file-text';
  
            if (page.url.includes('getting-started/')) {
              icon = 'lightbulb';
            }
            if (page.url.includes('resources/')) {
              icon = 'book';
            }
            if (page.url.includes('components/')) {
              icon = 'puzzle';
            }
            if (page.url.includes('tokens/')) {
              icon = 'palette2';
            }
            if (page.url.includes('utilities/')) {
              icon = 'wrench';
            }
            if (page.url.includes('tutorials/')) {
              icon = 'joystick';
            }
  
            a.href = window.$docsify.routerMode === 'hash' ? `/#/${page.url}` : `/${page.url}`;
            a.innerHTML = `
              <div class="site-search__result-icon">
                <sl-icon name="${icon}" aria-hidden="true"></sl-icon>
              </div>
              <div class="site-search__result__details">
                <h3>${page.title}</h3>
                <small>${page.url}</small>
              </div>
            `;
  
            li.classList.add('site-search__result');
            li.setAttribute('aria-selected', index === 0 ? 'true' : 'false');
            li.appendChild(a);
            results.appendChild(li);
          });
        } catch {
          // Ignore query errors as the user types
        }
      }
  
      // Show the search panel slash is pressed outside of a form element
      document.addEventListener('keydown', event => {
        if (
          !isShowing &&
          event.key === '/' &&
          !event.composedPath().some(el => ['input', 'textarea'].includes(el?.tagName?.toLowerCase()))
        ) {
          event.preventDefault();
          show();
        }
      });
  
      input.addEventListener('sl-input', handleInput);
  
      // Close when a result is selected
      results.addEventListener('click', event => {
        if (event.target.closest('a')) {
          hide();
        }
      });
    });
  })();
  