← back to Crezana Internal
Add sort control to crezana internal viewer (Steve hard rule)
7a000b764f1ffdb3c99db2342daedd97e4e350c4 · 2026-07-09 09:52:42 -0700 · Steve
Grid had density but no sort <select>. Added Newest/Title/Color/Style sort
wired to render, persisted to localStorage (crz_sort), restored on load —
matches the fromental/gracie pattern.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
Diff
commit 7a000b764f1ffdb3c99db2342daedd97e4e350c4
Author: Steve <steve@designerwallcoverings.com>
Date: Thu Jul 9 09:52:42 2026 -0700
Add sort control to crezana internal viewer (Steve hard rule)
Grid had density but no sort <select>. Added Newest/Title/Color/Style sort
wired to render, persisted to localStorage (crz_sort), restored on load —
matches the fromental/gracie pattern.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
public/index.html | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/public/index.html b/public/index.html
index a881e3f..684e1cd 100644
--- a/public/index.html
+++ b/public/index.html
@@ -47,6 +47,14 @@
<aside>
<input type="search" id="q" placeholder="Search pattern / sku / collection…">
<div class="controls">
+ <label>Sort
+ <select id="sort">
+ <option value="newest">Newest</option>
+ <option value="title">Title A→Z</option>
+ <option value="color">Color</option>
+ <option value="style">Style</option>
+ </select>
+ </label>
<label>Density <input type="range" id="density" min="120" max="340" value="190"></label>
<label>Text size <input type="range" id="tscale" min="80" max="150" value="100"></label>
<label><input type="checkbox" id="imgonly"> Image-only wall</label>
@@ -61,7 +69,18 @@
<script>
const API = location.origin;
let ALL=[], activeFacets={};
+let SORT = localStorage.getItem('crz_sort') || 'newest';
const fmtDate = d => d ? new Date(d).toLocaleString(undefined,{year:'numeric',month:'short',day:'numeric',hour:'numeric',minute:'2-digit'}) : '';
+function hue(hex){ if(!hex) return 999; const m=/^#?([0-9a-f]{6})$/i.exec(hex); if(!m) return 999; const n=parseInt(m[1],16), r=(n>>16&255)/255, g=(n>>8&255)/255, b=(n&255)/255; const mx=Math.max(r,g,b), mn=Math.min(r,g,b), d=mx-mn; if(!d) return -1; let h; if(mx===r) h=((g-b)/d)%6; else if(mx===g) h=(b-r)/d+2; else h=(r-g)/d+4; return (h*60+360)%360; }
+function sortRows(rows){
+ const by={
+ newest:(a,b)=> new Date(b.crawled_at||0)-new Date(a.crawled_at||0),
+ title:(a,b)=> (a.pattern_name||a.mfr_sku||'').localeCompare(b.pattern_name||b.mfr_sku||''),
+ color:(a,b)=> hue(a.dominant_color_hex||a.color_hex)-hue(b.dominant_color_hex||b.color_hex),
+ style:(a,b)=> ((a.ai_styles&&a.ai_styles[0])||'zz').localeCompare((b.ai_styles&&b.ai_styles[0])||'zz'),
+ };
+ return rows.slice().sort(by[SORT]||by.newest);
+}
async function load(){
const [p,f] = await Promise.all([
@@ -100,7 +119,7 @@ function matches(p){
return true;
}
function render(){
- const grid=document.getElementById('grid'); const rows=ALL.filter(matches);
+ const grid=document.getElementById('grid'); const rows=sortRows(ALL.filter(matches));
document.getElementById('empty').style.display=rows.length?'none':'block';
grid.innerHTML = rows.map(p=>{
const hex=p.dominant_color_hex||p.color_hex;
@@ -116,6 +135,8 @@ function render(){
}).join('');
}
document.getElementById('q').oninput=render;
+const sortEl=document.getElementById('sort'); sortEl.value=SORT;
+sortEl.onchange=e=>{ SORT=e.target.value; localStorage.setItem('crz_sort',SORT); render(); };
document.getElementById('density').oninput=e=>document.documentElement.style.setProperty('--card',e.target.value+'px');
document.getElementById('tscale').oninput=e=>document.documentElement.style.setProperty('--scale',(e.target.value/100));
document.getElementById('imgonly').onchange=e=>document.body.classList.toggle('imgonly',e.target.checked);
← fd39508 initial scaffold: crezana-internal (Crezana onboard)
·
back to Crezana Internal
·
jsonl-backed data layer: self-contained bundle for Kamatera d05fb36 →