[object Object]

← 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 →