← back to Wallco Ai
elements: add Sort (newest/oldest/category/color-by-hue/id↑↓ + Shuffled) coexisting with the Shuffle button; persists, restored sort overrides cold-start shuffle [yolo tick6/N8, local-only]
82cf88b834aa4732309bb1c9d8b44ad7e7a226f7 · 2026-06-02 13:38:07 -0700 · Steve Abrams
Files touched
M public/admin/elements.html
Diff
commit 82cf88b834aa4732309bb1c9d8b44ad7e7a226f7
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Jun 2 13:38:07 2026 -0700
elements: add Sort (newest/oldest/category/color-by-hue/id↑↓ + Shuffled) coexisting with the Shuffle button; persists, restored sort overrides cold-start shuffle [yolo tick6/N8, local-only]
---
public/admin/elements.html | 49 ++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 47 insertions(+), 2 deletions(-)
diff --git a/public/admin/elements.html b/public/admin/elements.html
index 255cfee..7bd9b04 100644
--- a/public/admin/elements.html
+++ b/public/admin/elements.html
@@ -242,6 +242,17 @@
<label>Search</label>
<input type="text" id="f-search" placeholder="id, category, or hex…" style="min-width:200px">
+ <label>Sort</label>
+ <select id="f-sort">
+ <option value="shuffle">Shuffled</option>
+ <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 style="margin-left:auto">Density</label>
<input id="density-slider" type="range" min="140" max="380" step="20" value="220">
<span class="density-val" id="density-val">220</span>
@@ -278,8 +289,31 @@
filter: { category:'', colorway:'', density:'', quality:true, search:'' },
cardMin: parseInt(localStorage.getItem('el-card-min') || '220', 10),
shuffleSeed: 0,
+ sort: localStorage.getItem('el-sort') || 'shuffle',
};
+ // Sort the view (Steve standing rule). dominant_hex exists → color-by-hue is available.
+ 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 sortView(arr) {
+ const r = arr.slice(), m = state.sort;
+ 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) => categoryOf(a).localeCompare(categoryOf(b)) || (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 applyCardMin(px) {
document.documentElement.style.setProperty('--card-min', px + 'px');
document.getElementById('density-val').textContent = px;
@@ -364,6 +398,7 @@
const grid = document.getElementById('grid');
let view = state.filtered;
if (state.shuffleSeed) view = shuffleList(view, state.shuffleSeed);
+ else if (state.sort !== 'shuffle') view = sortView(view);
view = view.slice(0, PER);
if (view.length === 0) {
@@ -508,7 +543,16 @@
localStorage.setItem('el-card-min', String(state.cardMin));
applyCardMin(state.cardMin);
});
+ document.getElementById('f-sort').addEventListener('change', e => {
+ state.sort = e.target.value;
+ localStorage.setItem('el-sort', state.sort);
+ state.shuffleSeed = 0; // an explicit sort overrides the random shuffle
+ render();
+ });
document.getElementById('shuffle-btn').addEventListener('click', () => {
+ state.sort = 'shuffle';
+ document.getElementById('f-sort').value = 'shuffle';
+ localStorage.setItem('el-sort', 'shuffle');
state.shuffleSeed = Date.now() & 0xffffffff;
render();
});
@@ -553,6 +597,7 @@
// Boot
applyCardMin(state.cardMin);
document.getElementById('density-slider').value = state.cardMin;
+ document.getElementById('f-sort').value = state.sort;
fetch('/api/admin/elements/items')
.then(r => r.json())
@@ -566,8 +611,8 @@
document.getElementById('stat-baked').textContent = state.items.filter(i => i.element_motif_path).length;
document.getElementById('stat-clean').textContent = state.items.length;
populateFilterOptions();
- // Cold-start: Shuffle 12.
- state.shuffleSeed = Date.now() & 0xffffffff;
+ // Cold-start shuffle only when no explicit sort is persisted (default 'shuffle').
+ if (state.sort === 'shuffle') state.shuffleSeed = Date.now() & 0xffffffff;
applyFilters();
})
.catch(err => {
← a375967 etsy-bucket: add Sort (newest/oldest/id↑↓/dpi) + persist; fo
·
back to Wallco Ai
·
yolo-backlog: T6 — N7 etsy-bucket + N8 elements done, LOOP C 9b4f2b9 →