learn-searchcraft/site/index.html
2025-07-15 12:30:09 -05:00

103 lines
3.4 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Searchcraft UI</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gray-900 text-white min-h-screen flex items-center justify-center px-4">
<div class="w-full max-w-2xl text-center">
<h1 class="text-3xl font-bold mb-4">🔍 Searchcraft</h1>
<div class="mb-4 flex flex-col md:flex-row gap-2 items-center justify-center">
<label for="index-select" class="text-gray-300 mr-2">Index:</label>
<select id="index-select" class="bg-gray-800 border border-gray-700 text-white p-2 rounded-md">
<option value="thoughts-links">thoughts-links</option>
<option value="waylonwalker.com">blog-posts</option>
</select>
</div>
<input
id="search-query"
type="text"
placeholder="Start typing to search..."
class="w-full p-4 text-lg rounded-md bg-gray-800 border border-gray-700 focus:outline-none focus:ring-2 focus:ring-cyan-500"
oninput="debouncedSearch()"
/>
<div id="results" class="mt-8 space-y-4 text-left"></div>
</div>
<script>
const BASE_URL = "http://localhost:8081";
let debounceTimer = null;
function getSelectedIndex() {
return document.getElementById("index-select").value;
}
function debouncedSearch() {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(search, 300);
}
async function search() {
const query = document.getElementById("search-query").value;
const resBox = document.getElementById("results");
const index = getSelectedIndex();
resBox.innerHTML = '';
if (!query) return;
const response = await fetch(`${BASE_URL}/index/${index}/search`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
limit: 10,
offset: 0,
query: { fuzzy: { ctx: query } }
}),
});
const result = await response.json();
const hits = result?.data?.hits || [];
for (const hit of hits) {
const { title, description, body, url, image } = hit.doc;
const preview = (description || body || "").slice(0, 1000) + ((description || body).length > 1000 ? "..." : "");
const link = url || "#";
const card = document.createElement("a");
card.href = link;
card.target = "_blank";
card.rel = "noopener noreferrer";
card.className = "flex gap-4 bg-gray-800 border border-gray-700 rounded-lg overflow-hidden hover:border-cyan-500 transition";
if (image) {
const img = document.createElement("img");
img.src = image;
img.alt = title || "preview";
img.className = "w-32 h-32 object-cover flex-shrink-0";
card.appendChild(img);
}
const content = document.createElement("div");
content.className = "p-4 flex flex-col justify-center";
const titleEl = document.createElement("h3");
titleEl.className = "text-lg font-semibold text-cyan-400 hover:underline";
titleEl.textContent = title || "Untitled";
const descEl = document.createElement("p");
descEl.className = "text-gray-300 text-sm mt-1";
descEl.textContent = preview || "(no description)";
content.appendChild(titleEl);
content.appendChild(descEl);
card.appendChild(content);
resBox.appendChild(card);
}
}
</script>
</body>
</html>