← back to Dw Substitute Viewer
cards: manual Card-fields toggles (12 fields) + density-driven progressive text reduction (hex→name→vendor→… down to model # only)
0a88e52a388a5a27a35b19992c553b9237548bd5 · 2026-07-07 15:27:35 -0700 · Steve
Files touched
Diff
commit 0a88e52a388a5a27a35b19992c553b9237548bd5
Author: Steve <steve@designerwallcoverings.com>
Date: Tue Jul 7 15:27:35 2026 -0700
cards: manual Card-fields toggles (12 fields) + density-driven progressive text reduction (hex→name→vendor→… down to model # only)
---
index.html | 36 +++++++++++++++++++++++++++++++++---
1 file changed, 33 insertions(+), 3 deletions(-)
diff --git a/index.html b/index.html
index 27e4c62..4fad40c 100644
--- a/index.html
+++ b/index.html
@@ -60,6 +60,14 @@ select{padding:6px 10px;border:1px solid var(--line);border-radius:8px;backgroun
.reset{margin-top:10px;width:100%;font-size:12px;color:var(--muted);background:none;border:1px solid var(--line);border-radius:8px;padding:7px 12px;cursor:pointer}
.count{color:var(--muted);font-size:13px;margin:0 0 10px}
.grid{display:grid;grid-template-columns:repeat(var(--cols),1fr);gap:14px}
+.meta{display:flex;flex-wrap:wrap;gap:4px 8px;align-items:center}
+.m-hex .hx{display:inline-block;width:10px;height:10px;border-radius:2px;border:1px solid rgba(0,0,0,.15);vertical-align:-1px;margin-right:3px}
+.m-sku{font-weight:700;color:var(--ink)}
+/* MANUAL per-field toggles (Card fields panel) */
+#grid.h-rank .rank,#grid.h-color .sw,#grid.h-status .st,#grid.h-vend .vend,#grid.h-title .title,#grid.h-hex .m-hex,#grid.h-specs .m-specs,#grid.h-price .m-price,#grid.h-sku .m-sku,#grid.h-score .scorebar,#grid.h-link .rowlink,#grid.h-urls .editimg{display:none}
+/* AUTO density-driven progressive text reduction (more columns → less text, down to model # only) */
+#grid.x-hex .m-hex,#grid.x-score .scorebar,#grid.x-title .title,#grid.x-vend .vend,#grid.x-specs .m-specs,#grid.x-price .m-price,#grid.x-status .st,#grid.x-color .sw{display:none}
+#grid.x-title.x-vend.x-specs.x-price .body{padding:5px 7px}
.card{background:#fff;border:1px solid var(--line);border-radius:12px;overflow:hidden;display:flex;flex-direction:column;transition:box-shadow .15s,transform .15s;position:relative}
.card:hover{box-shadow:0 8px 22px rgba(0,0,0,.09);transform:translateY(-2px)}
.card.sel{outline:2px solid var(--accent);outline-offset:-2px}
@@ -121,6 +129,7 @@ footer{color:var(--muted);font-size:12px;margin-top:28px;text-align:center}
<div class="dispctl"><label>Density <b id="vDen"></b></label><input type="range" id="den" min="3" max="12" step="1"></div>
<button class="reset" id="reset">Reset</button>
</div></details>
+ <details class="sec" data-k="fields"><summary>Card fields<span class="n" id="nFields"></span></summary><div class="fbody" id="fieldsRow"></div></details>
<details class="sec" open data-k="vendor"><summary>Vendor<span class="n" id="nVendor"></span></summary><div class="fbody" id="vendorRow"></div></details>
<details class="sec" data-k="material"><summary>Material<span class="n" id="nMaterial"></span></summary><div class="fbody" id="matRow"></div></details>
<details class="sec" data-k="style"><summary>Style<span class="n" id="nStyle"></span></summary><div class="fbody" id="styleRow"></div></details>
@@ -328,8 +337,23 @@ function renderPrio(){
box.querySelectorAll('button[data-d]').forEach(b=>b.onclick=()=>{const i=+b.dataset.d;[S.prio[i+1],S.prio[i]]=[S.prio[i],S.prio[i+1]];saveS();renderPrio();draw();});
}
function saveS(){localStorage.setItem('subfinder',JSON.stringify(S));}
+// ---------- card-field visibility: manual toggles + auto density-driven reduction ----------
+const FIELDROWS=[['rank','#'],['color','Color badge'],['status','Status'],['vend','Vendor'],['title','Title'],['hex','Hex'],['specs','Specs'],['price','Price'],['sku','Model #'],['score','Match bar'],['link','Link'],['urls','URL editor']];
+const FIELDS=Object.assign(Object.fromEntries(FIELDROWS.map(([k])=>[k,true])),JSON.parse(localStorage.getItem('subfinder_fields')||'{}'));
+function applyFieldClasses(){const g=el('grid');FIELDROWS.forEach(([k])=>g.classList.toggle('h-'+k,!FIELDS[k]));}
+function updFieldsN(){const n=FIELDROWS.filter(([k])=>!FIELDS[k]).length;const b=el('nFields');if(b){b.textContent=n||'';b.style.display=n?'':'none';}}
+function renderFields(){const box=el('fieldsRow');if(!box)return;box.innerHTML='';
+ FIELDROWS.forEach(([k,lbl])=>{const on=FIELDS[k];const d=document.createElement('div');d.className='fitem'+(on?' on':'');
+ d.innerHTML=`<span class="box">${on?'✓':''}</span><span class="lbl">${lbl}</span>`;
+ d.onclick=()=>{FIELDS[k]=!FIELDS[k];localStorage.setItem('subfinder_fields',JSON.stringify(FIELDS));applyFieldClasses();renderFields();};box.appendChild(d);});
+ updFieldsN();}
+// as columns increase, remove text in order (hex→score→name→vendor→specs→price→status→color); model # + image always kept
+const DENSORDER=['hex','score','title','vend','specs','price','status','color'];
+function applyDensityFields(cols){const g=el('grid');const hide=cols<=4?0:Math.min(DENSORDER.length,cols-4);
+ DENSORDER.forEach((k,i)=>g.classList.toggle('x-'+k,i<hide));}
function draw(){
document.documentElement.style.setProperty('--cols',S.den);
+ applyDensityFields(S.den);applyFieldClasses();
el('vDen').textContent=S.den;el('vShow').textContent=S.show;
renderFacets();
RANKED=SUBS.filter(passes).map(c=>({c,s:score(c)})).sort((a,b)=>b.s-a.s).slice(0,S.show);
@@ -344,7 +368,12 @@ function draw(){
<button class="editimg" title="Override image / product URL">✎ URLs</button>
</div>
<div class="body"><div class="vend">${c.vendor}</div><div class="title">${c.title}</div>
- <div class="meta">${c.material} · ${c.style} · Hue ${c.hue}°${c.price?' · $'+c.price:''} · ${c.sku}</div>
+ <div class="meta">
+ <span class="m-hex"><i class="hx" style="background:${c.hex}"></i>${c.hex}</span>
+ <span class="m-specs">${c.material} · ${c.style}</span>
+ ${c.price?`<span class="m-price">$${c.price}</span>`:''}
+ <span class="m-sku">${c.sku}</span>
+ </div>
<div class="scorebar"><i style="width:${Math.round(r.s*100)}%"></i></div>
<a class="rowlink" href="${prodUrl(c)}" target="_blank" rel="noopener">View product ↗</a>
<div class="imgedit">
@@ -401,13 +430,14 @@ el('mMail').addEventListener('click',()=>{const sub=`Substitute recommendations
{const want=['color','style','material'];if(!Array.isArray(S.prio))S.prio=want.slice();
S.prio=S.prio.filter(x=>want.includes(x));want.forEach(x=>{if(!S.prio.includes(x))S.prio.push(x);});S.prio=S.prio.slice(0,3);}
loadFacets();
-drawOrig();renderPrio();
+drawOrig();renderPrio();renderFields();
function bind(id,key){const e=el(id);e.value=S[key];e.addEventListener('input',()=>{S[key]=+e.value;saveS();draw();});}
bind('show','show');bind('den','den');
{let qt;el('q').addEventListener('input',e=>{clearTimeout(qt);qt=setTimeout(()=>{QTEXT=e.target.value.trim().toLowerCase();draw();},220);});}
el('reset').addEventListener('click',()=>{S.prio=D.prio.slice();S.den=D.den;S.show=D.show;saveS();
for(const k in FSETS)FSETS[k].clear();saveFacets();QTEXT='';el('q').value='';
- el('show').value=S.show;el('den').value=S.den;renderPrio();draw();});
+ FIELDROWS.forEach(([k])=>FIELDS[k]=true);localStorage.setItem('subfinder_fields',JSON.stringify(FIELDS));
+ el('show').value=S.show;el('den').value=S.den;renderPrio();renderFields();draw();});
draw();syncBar();
probeExtract().then(ok=>{if(ok){drawOrig();renderPrio();draw();}});
</script></body></html>
\ No newline at end of file
← 813b540 left rail: all.dw-style facet filters (vendor/material/style
·
back to Dw Substitute Viewer
·
matching: CLIP image-similarity as default top priority (Vis c41f36f →