[object Object]

← back to Wallco Ai

drunk-curator: add sort (density/character/newest) + generator filter (persists in localStorage)

2ca51dc466bbc3a9b6cef3e0f9a3dead24dc8766 · 2026-05-28 06:26:14 -0700 · Steve Abrams

Files touched

Diff

commit 2ca51dc466bbc3a9b6cef3e0f9a3dead24dc8766
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu May 28 06:26:14 2026 -0700

    drunk-curator: add sort (density/character/newest) + generator filter (persists in localStorage)
---
 public/admin/drunk-curator.html | 73 +++++++++++++++++++++++++++++++++++++++--
 1 file changed, 71 insertions(+), 2 deletions(-)

diff --git a/public/admin/drunk-curator.html b/public/admin/drunk-curator.html
index 6a6b3f8..8975cc6 100644
--- a/public/admin/drunk-curator.html
+++ b/public/admin/drunk-curator.html
@@ -117,6 +117,19 @@
   <select id="coll" title="pick a line" style="background:var(--panel);color:var(--ink);border:1px solid var(--line);border-radius:7px;padding:4px 9px;font-size:13px;max-width:300px;">
     <option value="">drunk samples (default)</option>
   </select>
+  <select id="sort" title="sort mode (persists)" style="background:var(--panel);color:var(--ink);border:1px solid var(--line);border-radius:7px;padding:4px 9px;font-size:13px;">
+    <option value="density">↓ density (n=64 first)</option>
+    <option value="character">🎨 character first (raffia/textile)</option>
+    <option value="newest">✨ newest first</option>
+    <option value="oldest">🕓 oldest first</option>
+  </select>
+  <select id="filterGen" title="filter by generator (persists)" style="background:var(--panel);color:var(--ink);border:1px solid var(--line);border-radius:7px;padding:4px 9px;font-size:13px;">
+    <option value="">all generators</option>
+    <option value="bgswap">bgswap (raffia/textile)</option>
+    <option value="comfy">comfy (fresh dense)</option>
+    <option value="replicate">replicate</option>
+    <option value="gemini">gemini</option>
+  </select>
   <label style="display:flex;align-items:center;gap:6px;font-size:12px;color:var(--mut);cursor:pointer;user-select:none;">
     <input type="checkbox" id="autotoggle"> 🔍 Auto-ID <span style="color:#6a737d">(Gemini ~$.003/item)</span>
   </label>
@@ -150,7 +163,54 @@ const $=s=>document.querySelector(s);
 const ADMIN=new URLSearchParams(location.search).get('admin');
 const CATEGORY=new URLSearchParams(location.search).get('category')||'';
 const q=s=>ADMIN?(s+(s.includes('?')?'&':'?')+'admin='+encodeURIComponent(ADMIN)):s;
-let QUEUE=[], i=0, hot=0, not=0, streak=0, decided={};
+let QUEUE=[], RAW_ITEMS=[], i=0, hot=0, not=0, streak=0, decided={};
+// Sort + filter — client-side over RAW_ITEMS so changes are instant. Persist in localStorage.
+let SORT_MODE = localStorage.getItem('drunk_sort') || 'density';
+let FILTER_GEN = localStorage.getItem('drunk_filter_gen') || '';
+// "Character first" demotes strict-grid drunk-zoo-36 toiles and surfaces bgswap raffia/textile-bg
+// designs (Steve's original-drunk-monkey aesthetic). Within each tier, falls back to density.
+function characterScore(d){
+  const g = String(d.generator||'').toLowerCase();
+  const c = String(d.category||'').toLowerCase();
+  let s = 0;
+  if (g.includes('bgswap')) s += 100;             // raffia/textile-bg family wins
+  if (g.includes('gemini')) s += 70;              // gemini edits (high-aesthetic)
+  if (g.includes('comfy')) s += 40;               // fresh comfy dense
+  if (g.includes('replicate-fast')) s += 30;
+  if (g.includes('replicate')) s += 20;
+  if (g.includes('smartfix')||g.includes('midheal')||g.includes('edgeheal')) s -= 30;
+  if (c.includes('drunk-zoo-36')) s -= 25;        // strict-grid toiles demoted
+  if (c.includes('drunk-animals-sample')) s += 10; // fresh samples slight boost
+  return s;
+}
+function rebuildQueue(){
+  let items = RAW_ITEMS.slice();
+  if (FILTER_GEN){
+    items = items.filter(d => String(d.generator||'').toLowerCase().includes(FILTER_GEN));
+  }
+  if (SORT_MODE === 'density'){
+    items.sort((a,b)=>(b.auto_hot_n??-1)-(a.auto_hot_n??-1) || b.id-a.id);
+  } else if (SORT_MODE === 'character'){
+    items.sort((a,b)=>characterScore(b)-characterScore(a) || (b.auto_hot_n??-1)-(a.auto_hot_n??-1) || b.id-a.id);
+  } else if (SORT_MODE === 'newest'){
+    items.sort((a,b)=>{
+      const ta = a.created_at?new Date(a.created_at).getTime():0;
+      const tb = b.created_at?new Date(b.created_at).getTime():0;
+      return tb-ta || b.id-a.id;
+    });
+  } else if (SORT_MODE === 'oldest'){
+    items.sort((a,b)=>{
+      const ta = a.created_at?new Date(a.created_at).getTime():0;
+      const tb = b.created_at?new Date(b.created_at).getTime():0;
+      return ta-tb || a.id-b.id;
+    });
+  }
+  QUEUE = items;
+  // Reset cursor to top whenever the filter/sort shifts the queue out from under it
+  i = 0;
+  const leftEl = document.getElementById('left'); if (leftEl) leftEl.textContent = QUEUE.length;
+  render();
+}
 // Repeat-scale preview: show the flat tile stretched to a real print width with an
 // inch ruler. 24/36/54″ are the standard wallpaper/panel widths. Persists.
 const SCALE_WIDTHS = [24, 36, 54];   // only these print widths — no slider
@@ -603,7 +663,16 @@ _autoTog.addEventListener('change',()=>{
 });
 { const gl=document.getElementById('gridlink'); if(gl) gl.href=q('/admin/cactus-curator'+(CATEGORY?'?category='+encodeURIComponent(CATEGORY):'')); }
 loadCollections();
-(async function(){ const r=await fetch(q('/api/admin/drunk/list'+(CATEGORY?'?category='+encodeURIComponent(CATEGORY):''))); QUEUE=(await r.json()).items||[]; render(); })();
+(async function(){
+  const r=await fetch(q('/api/admin/drunk/list'+(CATEGORY?'?category='+encodeURIComponent(CATEGORY):'')));
+  RAW_ITEMS = (await r.json()).items || [];
+  // Wire sort + filter dropdowns to current state and rebuild on change
+  const sortEl = document.getElementById('sort');
+  const filtEl = document.getElementById('filterGen');
+  if (sortEl){ sortEl.value = SORT_MODE; sortEl.onchange = () => { SORT_MODE = sortEl.value; localStorage.setItem('drunk_sort', SORT_MODE); rebuildQueue(); }; }
+  if (filtEl){ filtEl.value = FILTER_GEN; filtEl.onchange = () => { FILTER_GEN = filtEl.value; localStorage.setItem('drunk_filter_gen', FILTER_GEN); rebuildQueue(); }; }
+  rebuildQueue();
+})();
 </script>
 </body>
 </html>

← d083387 admin/bg-tester: .when chip on recent-design tiles (created  ·  back to Wallco Ai  ·  etsy export: scripts/export-etsy-digital.js writes per-desig 581d6a2 →