← back to Wallco Ai
new.html: add Sort (6 opts incl color-by-hue) + Density slider per standing rule — both persist to localStorage [yolo tick1, local-only]
7aa964558e40a6b9feaf950f500ceac4cc239d4e · 2026-06-02 10:52:25 -0700 · Steve Abrams
Files touched
Diff
commit 7aa964558e40a6b9feaf950f500ceac4cc239d4e
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Jun 2 10:52:25 2026 -0700
new.html: add Sort (6 opts incl color-by-hue) + Density slider per standing rule — both persist to localStorage [yolo tick1, local-only]
---
public/new.html | 109 +++++++++++++++++++++++++++++++++++++++++---------------
1 file changed, 81 insertions(+), 28 deletions(-)
diff --git a/public/new.html b/public/new.html
index fb3c8ea..a1deeb9 100644
--- a/public/new.html
+++ b/public/new.html
@@ -8,6 +8,7 @@
<style>
:root {
--ink:#1f1808; --line:#d8d0c0; --gold:#c9a14b;
+ --card-min:220px;
--bg:#fbf8f1; --card:#fff; --faint:#7a6e5a;
--green:#1f6a2c; --blue:#3b78d8; --purple:#7a4ab8; --orange:#c0660b; --red:#b3261e;
--sans:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
@@ -49,8 +50,9 @@
}
.grid {
- display:grid; grid-template-columns:repeat(auto-fill,minmax(220px,1fr)); gap:14px;
+ display:grid; grid-template-columns:repeat(auto-fill,minmax(var(--card-min),1fr)); gap:14px;
}
+ .controls input[type=range] { accent-color:var(--gold); vertical-align:middle; }
.card {
background:var(--card); border:1px solid var(--line); border-radius:8px;
overflow:hidden; cursor:pointer; transition:transform .12s, box-shadow .12s;
@@ -118,6 +120,17 @@
<option value="48">last 48h</option>
<option value="168">last 7d</option>
</select>
+ <label>Sort</label>
+ <select id="sort">
+ <option value="newest">Newest</option>
+ <option value="oldest">Oldest</option>
+ <option value="category">Category A→Z</option>
+ <option value="color">Color (by hue)</option>
+ <option value="id">ID ↑</option>
+ <option value="id_desc">ID ↓</option>
+ </select>
+ <label>Density</label>
+ <input type="range" id="density" min="140" max="340" step="10" title="card size">
<span class="right" id="meta">—</span>
<a href="/admin/ghost-review">🔍 Ghost Review →</a>
</div>
@@ -134,8 +147,43 @@
const $ = (id) => document.getElementById(id);
let HOURS = 24;
let LAST_TOTAL = 0;
+ let ALL = [];
$('hours').onchange = (e) => { HOURS = parseInt(e.target.value, 10); refresh(); };
+ // sort + density (Steve standing rule: every product grid; both persist)
+ $('sort').value = localStorage.getItem('new_sort') || 'newest';
+ $('density').value = localStorage.getItem('new_density') || '220';
+ document.documentElement.style.setProperty('--card-min', $('density').value + 'px');
+ $('sort').onchange = () => { localStorage.setItem('new_sort', $('sort').value); render(); };
+ $('density').oninput = () => {
+ localStorage.setItem('new_density', $('density').value);
+ document.documentElement.style.setProperty('--card-min', $('density').value + 'px');
+ };
+
+ function hexHue(hex) {
+ const m = /^#?([0-9a-fA-F]{6})$/.exec(hex || ''); if (!m) return 1e6;
+ const n = parseInt(m[1], 16), r = (n >> 16 & 255) / 255, g = (n >> 8 & 255) / 255, b = (n & 255) / 255;
+ const max = Math.max(r, g, b), min = Math.min(r, g, b), d = max - min; let h = 0;
+ if (d === 0) h = 0;
+ else if (max === r) h = ((g - b) / d) % 6;
+ else if (max === g) h = (b - r) / d + 2;
+ else h = (r - g) / d + 4;
+ h *= 60; if (h < 0) h += 360;
+ const s = max === 0 ? 0 : d / max;
+ if (s < 0.12) return 1e5 + (1 - max);
+ return h;
+ }
+ function sortItems(rows) {
+ const r = rows.slice(), m = $('sort').value;
+ if (m === 'newest') r.sort((a, b) => String(b.created_at || '').localeCompare(String(a.created_at || '')));
+ else if (m === 'oldest') r.sort((a, b) => String(a.created_at || '').localeCompare(String(b.created_at || '')));
+ else if (m === 'category') r.sort((a, b) => String(a.category || '').localeCompare(String(b.category || '')) || (b.id - a.id));
+ else if (m === 'color') r.sort((a, b) => hexHue(a.dominant_hex) - hexHue(b.dominant_hex) || (b.id - a.id));
+ else if (m === 'id') r.sort((a, b) => a.id - b.id);
+ else if (m === 'id_desc') r.sort((a, b) => b.id - a.id);
+ return r;
+ }
+
function genKind(gen) {
const g = (gen || '').toLowerCase();
if (g.includes('smart-fix') || g.includes('smart_fix')) return 'smart-fix';
@@ -158,6 +206,7 @@
setTimeout(() => { document.title = "What's New — wallco.ai"; }, 4000);
}
LAST_TOTAL = items.length;
+ ALL = items;
const counts = { 'smart-fix':0, 'crop-fix':0, 'regen':0, 'wand':0, 'other':0 };
items.forEach(it => { counts[genKind(it.generator)]++; });
@@ -168,38 +217,42 @@
$('stat-regen').textContent = counts['regen'];
$('meta').textContent = `${items.length} shown · window=${HOURS}h · auto-refresh 10s`;
- const grid = $('grid');
- grid.innerHTML = '';
- if (!items.length) { $('empty').style.display = ''; return; }
- $('empty').style.display = 'none';
- const frag = document.createDocumentFragment();
- for (const it of items) {
- const k = genKind(it.generator);
- const card = document.createElement('a');
- card.className = 'card';
- card.href = `/design/${it.id}`;
- card.target = '_blank';
- const ts = new Date(it.created_at);
- const ago = humanAgo(ts);
- card.innerHTML = `
- <div class="thumb">
- <img loading="lazy" src="/designs/img/by-id/${it.id}" alt="${it.id}">
- <div class="id">#${it.id}</div>
- <div class="gen-pill gen-${k}">${pillLabel(k)}</div>
- </div>
- <div class="meta-row">
- <span class="cat">${it.category || '—'}</span>
- <span class="ts">${ago} · ${it.dominant_hex || ''}</span>
- </div>
- `;
- frag.appendChild(card);
- }
- grid.appendChild(frag);
+ render();
} catch (e) {
console.warn('refresh failed:', e.message);
}
}
+ function render() {
+ const grid = $('grid');
+ grid.innerHTML = '';
+ if (!ALL.length) { $('empty').style.display = ''; return; }
+ $('empty').style.display = 'none';
+ const frag = document.createDocumentFragment();
+ for (const it of sortItems(ALL)) {
+ const k = genKind(it.generator);
+ const card = document.createElement('a');
+ card.className = 'card';
+ card.href = `/design/${it.id}`;
+ card.target = '_blank';
+ const ts = new Date(it.created_at);
+ const ago = humanAgo(ts);
+ card.innerHTML = `
+ <div class="thumb">
+ <img loading="lazy" src="/designs/img/by-id/${it.id}" alt="${it.id}">
+ <div class="id">#${it.id}</div>
+ <div class="gen-pill gen-${k}">${pillLabel(k)}</div>
+ </div>
+ <div class="meta-row">
+ <span class="cat">${it.category || '—'}</span>
+ <span class="ts">${ago} · ${it.dominant_hex || ''}</span>
+ </div>
+ `;
+ frag.appendChild(card);
+ }
+ grid.appendChild(frag);
+ }
+
function humanAgo(ts) {
const sec = Math.max(0, Math.round((Date.now() - ts.getTime()) / 1000));
if (sec < 60) return `${sec}s ago`;
← 4eebff7 Add settlement-gate-batch.js: gate ungated designs by catego
·
back to Wallco Ai
·
yolo-backlog: open 2026-06-02 refinement loop (local-only pe 6452ad3 →