// app.js — catalog gallery: load parts, functional search over use-cases, class filter, PBR thumbnails. import { renderThumbnail } from '/assets/viewer.js'; const $ = (s) => document.querySelector(s); let PARTS = [], activeClass = null; function haystack(p) { return [p.role, p.summary, p.when_to_use, p.usage, p.mpn, p.class, p.subclass, p.package, (p.use_cases || []).join(' '), Object.values(p.tags || {}).flat().join(' ')].join(' ').toLowerCase(); } function score(p, terms) { const h = haystack(p); let s = 0; for (const t of terms) { if (!t) continue; const uc = (p.use_cases || []).join(' ').toLowerCase(); if (uc.includes(t)) s += 3; // functional use-case match weighs most if ((p.class || '').toLowerCase().includes(t)) s += 2; if ((p.mpn || '').toLowerCase().includes(t)) s += 2; if (h.includes(t)) s += 1; } return s; } function chip(label, val) { const c = document.createElement('span'); c.className = 'chip' + (activeClass === val ? ' on' : ''); c.textContent = label; c.onclick = () => { activeClass = activeClass === val ? null : val; render(); }; return c; } function renderChips() { const counts = {}; for (const p of PARTS) counts[p.class] = (counts[p.class] || 0) + 1; const box = $('#chips'); box.innerHTML = ''; box.appendChild(chip('All', null)); Object.entries(counts).sort((a, b) => b[1] - a[1]).forEach(([c, n]) => box.appendChild(chip(`${c} (${n})`, c))); } function card(p) { const el = document.createElement('a'); el.className = 'card reveal'; el.href = `/part.html?id=${p.id}`; el.innerHTML = `