feat: Cookbook Search Results UI & Lazy Metrics

This commit is contained in:
Hitonabi
2026-06-20 23:48:28 +02:00
parent e180adf21a
commit b2b586093d
+57 -3
View File
@@ -229,15 +229,69 @@ function renderResults(results) {
grid.innerHTML = results.map((m, i) => ` grid.innerHTML = results.map((m, i) => `
<div class="card" style="display:flex; flex-direction:column; cursor:pointer" onclick="window.openModelModal(${i})"> <div class="card" style="display:flex; flex-direction:column; cursor:pointer" onclick="window.openModelModal(${i})">
<div style="display:flex; justify-content:space-between; align-items:flex-start; gap:8px;">
<div>
<h3 style="margin:0; font-size:16px; word-break:break-all">${esc(m.id.split('/').pop())}</h3> <h3 style="margin:0; font-size:16px; word-break:break-all">${esc(m.id.split('/').pop())}</h3>
<div class="meta" style="font-size:12px; margin-top:4px">${esc(m.author)}</div> <div class="meta" style="font-size:12px; margin-top:4px">${esc(m.author)}</div>
</div>
<span id="cb-s-badge-${i}" class="badge b-load" style="white-space:nowrap;">Analysiere...</span>
</div>
<div style="flex:1; margin-top:16px;"></div> <div style="flex:1; margin-top:16px;"></div>
<div style="display:flex; justify-content:space-between; align-items:center; margin-top:16px"> <div style="display:flex; justify-content:space-between; align-items:center; margin-top:16px; font-size:12px" class="meta">
<span class="badge b-idle" style="font-size:11px">GGUF</span> <span id="cb-s-metrics-${i}">Berechne Hardware-Fit...</span>
<span class="meta" style="font-size:12px">⬇ ${(m.downloads||0).toLocaleString()}</span> <span style="display:flex; gap:8px; align-items:center">
<span>⬇ ${(m.downloads||0).toLocaleString()}</span>
<span id="cb-s-quant-${i}" class="badge b-idle" style="font-size:11px">GGUF</span>
</span>
</div> </div>
</div> </div>
`).join(""); `).join("");
// Lade Metriken im Hintergrund
results.forEach((m, i) => fetchAnalysisForCard(i, m.id));
}
async function fetchAnalysisForCard(index, repo_id) {
try {
const res = await api("/api/cookbook/analyze", {
method: "POST", body: JSON.stringify({ repo_id, ctx: 8192 })
});
const badgeEl = document.getElementById(`cb-s-badge-${index}`);
const metricsEl = document.getElementById(`cb-s-metrics-${index}`);
const quantEl = document.getElementById(`cb-s-quant-${index}`);
if (!badgeEl || !metricsEl || !quantEl) return; // card might be gone
if (!res.files || res.files.length === 0) {
badgeEl.className = "badge b-err";
badgeEl.textContent = "Keine GGUFs";
metricsEl.textContent = "Keine Dateien gefunden";
return;
}
let best = res.files.find(f => f.quant && f.quant.includes("Q4_K_M"));
if (!best) best = res.files[0];
const fit = best.fit;
const cls = fit.level === "perfect" ? "b-run" : (fit.level === "marginal" ? "b-load" : "b-err");
badgeEl.className = `badge ${cls}`;
badgeEl.textContent = fit.text;
metricsEl.innerHTML = `~${fit.req_gb.toFixed(1)} GB RAM/VRAM &middot; ~${Math.round(fit.tps)} t/s`;
quantEl.textContent = best.quant || "GGUF";
} catch(e) {
const badgeEl = document.getElementById(`cb-s-badge-${index}`);
const metricsEl = document.getElementById(`cb-s-metrics-${index}`);
if (badgeEl) {
badgeEl.className = "badge b-err";
badgeEl.textContent = "Fehler";
}
if (metricsEl) {
metricsEl.textContent = "Metriken konnten nicht geladen werden";
}
}
} }
// Global hook for inline onclick // Global hook for inline onclick