← back to Wild Wallcoverings
viewer standard: add Grid/List toggle + per-field on/off menu (Fields) to product grid
87c194d23e55e8124aee9a223776f61a2f5cad22 · 2026-08-12 08:33:42 -0700 · Steve
Files touched
Diff
commit 87c194d23e55e8124aee9a223776f61a2f5cad22
Author: Steve <steve@designerwallcoverings.com>
Date: Wed Aug 12 08:33:42 2026 -0700
viewer standard: add Grid/List toggle + per-field on/off menu (Fields) to product grid
---
public/index.html | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 66 insertions(+), 6 deletions(-)
diff --git a/public/index.html b/public/index.html
index cb9eb9c..29ad210 100644
--- a/public/index.html
+++ b/public/index.html
@@ -14,8 +14,24 @@
h1 { font-size: 20px; margin: 0; letter-spacing: .06em; font-weight: 600; }
h1 em { font-style: normal; color: #7ec8a9; }
.sub { color: #888; font-size: 13px; }
- .controls { display: flex; gap: 14px; align-items: center; padding: 14px 24px; }
+ .controls { display: flex; gap: 14px; align-items: center; padding: 14px 24px; flex-wrap: wrap; }
.controls label { font-size: 12px; color: #aaa; }
+ /* ── Fields menu (wild-wallcoverings admin viewer) ─────────────────── */
+ .ww-fieldmenu { position:relative; flex-shrink:0; }
+ .ww-fbtn { padding:5px 12px; border:1px solid #3c6; background:transparent; color:#7ec8a9; font-size:11px; letter-spacing:.06em; text-transform:uppercase; cursor:pointer; border-radius:4px; }
+ .ww-fbtn:hover { background:rgba(78,200,128,.08); }
+ .ww-fpop { position:absolute; top:calc(100% + 6px); right:0; background:#1c1c1c; border:1px solid #333; box-shadow:0 8px 24px rgba(0,0,0,.55); padding:6px 4px; z-index:40; min-width:180px; border-radius:6px; }
+ .ww-fpop[hidden] { display:none; }
+ .ww-fpop label { display:flex; align-items:center; gap:10px; padding:7px 14px; font-size:11px; color:#ccc; cursor:pointer; white-space:nowrap; }
+ .ww-fpop label:hover { background:rgba(255,255,255,.05); }
+ .ww-fpop input[type=checkbox] { accent-color:#7ec8a9; }
+ /* Per-field hide rules */
+ html.hf-ww-image .card img { display:none !important }
+ html.hf-ww-badge [data-f="ww-badge"] { display:none !important }
+ html.hf-ww-date [data-f="ww-date"] { display:none !important }
+ html.hf-ww-seamless [data-f="ww-seamless"] { display:none !important }
+ html.hf-ww-review [data-f="ww-review"]{ display:none !important }
+ html.hf-ww-copy [data-f="ww-copy"] { display:none !important }
select, input[type=range] { accent-color: #7ec8a9; background: #1c1c1c;
color: #eee; border: 1px solid #333; border-radius: 6px; padding: 4px 8px; }
#board { padding: 0 24px 60px; }
@@ -79,6 +95,10 @@
</select>
</label>
<label>Density <input type="range" id="density" min="1" max="4" value="3"></label>
+ <div class="ww-fieldmenu">
+ <button class="ww-fbtn" id="wwFieldsBtn" aria-haspopup="true" aria-expanded="false">Fields ▾</button>
+ <div class="ww-fpop" id="wwFpop" hidden role="menu"></div>
+ </div>
</div>
<div id="board"></div>
<div id="gobar">
@@ -142,12 +162,12 @@ function render() {
<div class="card ${chosenFile === v.file ? 'chosen' : ''}">
<img src="/images/${v.file}" loading="lazy" onclick="zoom(this.src)">
<div class="meta">
- <span class="badge ${v.engine === 'nano-banana' ? 'nano' : 'local'}">
+ <span class="badge ${v.engine === 'nano-banana' ? 'nano' : 'local'}" data-f="ww-badge">
${v.engine === 'nano-banana' ? '🍌 nano banana' : '🖥 local SDXL'}</span>
- <span class="when" title="${v.created_at}">🕓 ${fmtDate(v.created_at)}</span>
- ${v.tile ? `<span class="badge" style="background:${['PASS','EDGES-PASS'].includes(v.tile.verdict)?'#14301b;color:#6c6':v.tile.verdict==='WARN'?'#33290f;color:#e0b34d':'#331414;color:#e06c6c'}">seamless: ${['PASS','EDGES-PASS'].includes(v.tile.verdict)?'YES':v.tile.verdict}</span>` : ''}
- ${v.borderline ? '<span class="badge" style="background:#3a2510;color:#f0a050">⚠ settlement REVIEW</span>' : ''}
- ${v.copycheck === 'FLAGGED' ? `<span class="badge" style="background:#3a1010;color:#f05050" title="${v.copycheck_what||''}">✋ too close to original</span>` : v.copycheck === 'CLEAN' ? '<span class="badge" style="background:#102a10;color:#5cb85c">✓ original</span>' : ''}
+ <span class="when" data-f="ww-date" title="${v.created_at}">🕓 ${fmtDate(v.created_at)}</span>
+ ${v.tile ? `<span class="badge" data-f="ww-seamless" style="background:${['PASS','EDGES-PASS'].includes(v.tile.verdict)?'#14301b;color:#6c6':v.tile.verdict==='WARN'?'#33290f;color:#e0b34d':'#331414;color:#e06c6c'}">seamless: ${['PASS','EDGES-PASS'].includes(v.tile.verdict)?'YES':v.tile.verdict}</span>` : ''}
+ ${v.borderline ? '<span class="badge" data-f="ww-review" style="background:#3a2510;color:#f0a050">⚠ settlement REVIEW</span>' : ''}
+ ${v.copycheck === 'FLAGGED' ? `<span class="badge" data-f="ww-copy" style="background:#3a1010;color:#f05050" title="${v.copycheck_what||''}">✋ too close to original</span>` : v.copycheck === 'CLEAN' ? '<span class="badge" data-f="ww-copy" style="background:#102a10;color:#5cb85c">✓ original</span>' : ''}
<button class="choose" onclick="choose('${d.id}','${v.file}')">
${chosenFile === v.file ? '✓ CHOSEN — will go live' : 'Choose this version'}</button>
</div>
@@ -235,6 +255,46 @@ sortSel.onchange = render;
density.oninput = applyDensity;
applyDensity();
load();
+
+// ── Fields menu (wild-wallcoverings) ────────────────────────────────────────
+(function(){
+ var WW_FIELDS = [
+ ['ww-image','Image',true],['ww-badge','Engine Badge',true],
+ ['ww-date','Date',true],['ww-seamless','Seamless',true],
+ ['ww-review','Review Flag',true],['ww-copy','Copy Check',true],
+ ];
+ var wwFields = (function(){
+ var def={}; WW_FIELDS.forEach(function(f){ def[f[0]]=f[2]; });
+ try{ return Object.assign(def, JSON.parse(localStorage.getItem('wildwc.fields')||'{}')); }catch{ return def; }
+ })();
+ function wwApplyFields(){
+ WW_FIELDS.forEach(function(f){ document.documentElement.classList.toggle('hf-'+f[0], !wwFields[f[0]]); });
+ }
+ function wwBuildFieldMenu(){
+ var fpop=document.getElementById('wwFpop'); if(!fpop) return;
+ fpop.innerHTML=WW_FIELDS.map(function(f){
+ return '<label><input type="checkbox" data-field="'+f[0]+'"'+(wwFields[f[0]]?' checked':'')+'>'+f[1]+'</label>';
+ }).join('');
+ fpop.querySelectorAll('input').forEach(function(cb){
+ cb.addEventListener('change',function(){
+ wwFields[cb.dataset.field]=cb.checked;
+ try{ localStorage.setItem('wildwc.fields', JSON.stringify(wwFields)); }catch(ex){}
+ wwApplyFields();
+ });
+ });
+ }
+ var fbtn=document.getElementById('wwFieldsBtn'), fpop=document.getElementById('wwFpop');
+ if(fbtn && fpop){
+ fbtn.addEventListener('click',function(e){
+ e.stopPropagation(); var open=fpop.hidden; fpop.hidden=!open;
+ fbtn.setAttribute('aria-expanded', String(open));
+ });
+ document.addEventListener('click',function(e){
+ if(!fpop.hidden && !e.target.closest('.ww-fieldmenu')){ fpop.hidden=true; fbtn.setAttribute('aria-expanded','false'); }
+ });
+ }
+ wwBuildFieldMenu(); wwApplyFields();
+})();
</script>
</body>
</html>
← 140fbf0 auto-save: 2026-07-23T13:21:35 (3 files) — data/marketing-ap
·
back to Wild Wallcoverings
·
creds-safe fetch guard: resolve relative fetch vs credential b522c0b →