← back to Reid Witlin Onboarding
Cody-gate cycle3: <img> lazy+onerror (404->red card, perf), real confidence-rank sort, search incl town/colorway; dropped phantom rwltd-403 (all imgs are cdn.shopify.com/200)
72892c7d2277e9014012a0a489576eca3ac70258 · 2026-07-30 16:41:12 -0700 · Steve
Files touched
Diff
commit 72892c7d2277e9014012a0a489576eca3ac70258
Author: Steve <steve@designerwallcoverings.com>
Date: Thu Jul 30 16:41:12 2026 -0700
Cody-gate cycle3: <img> lazy+onerror (404->red card, perf), real confidence-rank sort, search incl town/colorway; dropped phantom rwltd-403 (all imgs are cdn.shopify.com/200)
---
preview.py | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/preview.py b/preview.py
index 0ba1c2b..a9b831d 100644
--- a/preview.py
+++ b/preview.py
@@ -44,8 +44,10 @@ select,input[type=search]{font:13px inherit;padding:5px 8px;border:1px solid #d4
#grid{display:grid;grid-template-columns:repeat(var(--cols),1fr);gap:14px;padding:20px}
.card{background:#fff;border:1px solid #e4e4e7;border-radius:10px;overflow:hidden;display:flex;flex-direction:column}
.card.held{border-color:#f59e0b;border-width:2px}
-.thumb{aspect-ratio:1;background:#fafafa center/cover no-repeat;border-bottom:1px solid #f1f1f4}
-.thumb.none{display:flex;align-items:center;justify-content:center;color:#a1a1aa;font-size:12px}
+.thumb{aspect-ratio:1;width:100%;background:#fafafa center/cover no-repeat;border-bottom:1px solid #f1f1f4}
+img.thumb{object-fit:cover;display:block}
+.thumb.none{display:flex;align-items:center;justify-content:center;color:#a1a1aa;font-size:12px;text-align:center}
+.card.img-failed{border-color:#ef4444;border-width:2px}
.body{padding:10px 12px;display:flex;flex-direction:column;gap:5px}
.title{font-weight:600;font-size:14px}
.sku{font:11px ui-monospace,monospace;color:#71717a}
@@ -86,18 +88,20 @@ function specLine(r){
function render(){
const q=$('q').value.toLowerCase().trim(), heldonly=$('heldonly').checked;
let rows=DATA.filter(r=>!heldonly||r._pile==='held');
- if(q) rows=rows.filter(r=>(r.title+' '+r.sku+' '+r.tags).toLowerCase().includes(q));
+ if(q) rows=rows.filter(r=>(r.title+' '+r.sku+' '+r.tags+' '+r.town+' '+r.colorway).toLowerCase().includes(q));
const s=$('sort').value;
+ // least-trusted first, so "sort by confidence" bubbles the dodgy images up
+ const confRank={'collision-held':0,'unconfirmed-held':1,none:2,prefix:3,'vendor-js':4,exact:5};
const cmp={title:(a,b)=>a.title.localeCompare(b.title),sku:(a,b)=>a.sku.localeCompare(b.sku),
town:(a,b)=>(a.town+a.colorway).localeCompare(b.town+b.colorway),
- conf:(a,b)=>a.image_confidence.localeCompare(b.image_confidence),
+ conf:(a,b)=>(confRank[a.image_confidence]??9)-(confRank[b.image_confidence]??9),
pile:(a,b)=>(a._pile==='held'?0:1)-(b._pile==='held'?0:1)};
if(cmp[s]) rows=rows.slice().sort(cmp[s]);
document.documentElement.style.setProperty('--cols',$('dens').value);
grid.innerHTML=rows.map(r=>{
const img=r.image_url&&r.image_url.startsWith('http');
return `<div class="card ${r._pile==='held'?'held':''}">
- ${img?`<div class="thumb" style="background-image:url('${r.image_url}')"></div>`
+ ${img?`<img class="thumb" loading="lazy" src="${r.image_url}" onerror="this.closest('.card').classList.add('img-failed');this.replaceWith(Object.assign(document.createElement('div'),{className:'thumb none',textContent:'image failed'}))">`
:`<div class="thumb none">no image</div>`}
<div class="body">
<span class="title">${r.title||'(untitled)'}</span>
← 267c9f8 Cycle 3: QA preview grid (preview.py -> preview.html) — 859
·
back to Reid Witlin Onboarding
·
Fix preview.py %-format crash (width:100% broke str%); token 9dd09fa →