Connor McCutcheon
/ SkyCode
search.js
js
// Search module
// Requires: editor, openFile, getFileIcon, setStatus, escapeHtml, escapeAttr from global scope
function toggleSearchPanel() {
  const panel = document.getElementById('search-panel');
  if (panel.classList.contains('hidden')) {
    panel.classList.remove('hidden');
    document.getElementById('search-input').focus();
  } else {
    panel.classList.add('hidden');
  }
}
function closeSearchPanel() {
  document.getElementById('search-panel').classList.add('hidden');
}
async function performSearch() {
  const query = document.getElementById('search-input').value.trim();
  if (!query) return;
  const isRegex = document.getElementById('regex-toggle').checked;
  const resultsDiv = document.getElementById('search-results');
  resultsDiv.innerHTML = '<div class="p-3 text-sm text-base-content/50">Searching...</div>';
  try {
    const params = new URLSearchParams({ q: query });
    if (isRegex) params.append('regex', 'true');
    const res = await fetch(`/api/search?${params}`);
    if (!res.ok) {
      const err = await res.json();
      const errorDiv = document.createElement('div');
      errorDiv.className = 'p-3 text-sm text-error';
      errorDiv.textContent = err.error || 'Search failed';
      resultsDiv.innerHTML = '';
      resultsDiv.appendChild(errorDiv);
      return;
    }
    const results = await res.json();
    if (results.length === 0) {
      resultsDiv.innerHTML = '<div class="p-3 text-sm text-base-content/50">No results found</div>';
      return;
    }
    // Group results by file
    const grouped = {};
    results.forEach(r => {
      if (!grouped[r.path]) grouped[r.path] = [];
      grouped[r.path].push(r);
    });
    let html = '';
    for (const [path, matches] of Object.entries(grouped)) {
      html += `
        <div class="border-b border-base-100">
          <div class="px-3 py-2 bg-base-200/50 text-xs font-semibold flex items-center gap-1">
            <span>${getFileIcon(path)}</span>
            <span>${path}</span>
            <span class="text-base-content/50">(${matches.length})</span>
          </div>
          <ul class="menu menu-xs p-1">
      `;
      matches.forEach(m => {
        html += `
          <li>
            <a onclick="goToSearchResult('${escapeAttr(path)}', ${m.line}, ${m.column})" class="py-1">
              <span class="text-base-content/50 w-8 text-right">${m.line}</span>
              <span class="font-mono text-xs truncate">${escapeHtml(m.context)}</span>
            </a>
          </li>
        `;
      });
      html += '</ul></div>';
    }
    resultsDiv.innerHTML = html;
    setStatus(`Found ${results.length} results in ${Object.keys(grouped).length} files`);
  } catch (err) {
    resultsDiv.innerHTML = '<div class="p-3 text-sm text-error">Search failed</div>';
  }
}
async function goToSearchResult(path, line, column) {
  await openFile(path);
  editor.revealLineInCenter(line);
  editor.setPosition({ lineNumber: line, column: column });
  editor.focus();
}
// Keyboard shortcut for search (Ctrl+Shift+F)
document.addEventListener('keydown', (e) => {
  if (e.ctrlKey && e.shiftKey && e.key === 'F') {
    e.preventDefault();
    toggleSearchPanel();
  }
});
No comments yet.