[object Object]

← back to Wallco Ai

drunk-curator = canonical Hot-or-Not agent: add πŸ›’ Etsy bucket action button (keybind: S) next to HOT/Fix/NOT/Extract. Posts to /api/etsy-bucket/add (queues for digital-download listing at 300 DPI / 7200px). Ineligibility (seam FAIL etc) surfaces an amber toast with the reason; eligibility passes auto-advance the queue + tag the design 'etsy-bucket'

e1ba3238045f621b70f69fe1a0ee8e33cd316494 Β· 2026-05-28 09:19:07 -0700 Β· Steve Abrams

Files touched

Diff

commit e1ba3238045f621b70f69fe1a0ee8e33cd316494
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu May 28 09:19:07 2026 -0700

    drunk-curator = canonical Hot-or-Not agent: add πŸ›’ Etsy bucket action button (keybind: S) next to HOT/Fix/NOT/Extract. Posts to /api/etsy-bucket/add (queues for digital-download listing at 300 DPI / 7200px). Ineligibility (seam FAIL etc) surfaces an amber toast with the reason; eligibility passes auto-advance the queue + tag the design 'etsy-bucket'
---
 public/admin/drunk-curator.html | 38 ++++++++++++++++++++++++++++++++++++--
 1 file changed, 36 insertions(+), 2 deletions(-)

diff --git a/public/admin/drunk-curator.html b/public/admin/drunk-curator.html
index 504b5b3..c40d91d 100644
--- a/public/admin/drunk-curator.html
+++ b/public/admin/drunk-curator.html
@@ -567,8 +567,8 @@ function render(){
     <div class="meta"><span class="cues">${buildCues(d, i)}</span><span title="${d.created_at||''}">πŸ•“ ${fmtDate(d.created_at)}</span></div>
     <div class="prompt" onclick="this.classList.toggle('open')">${(d.prompt||'').replace(/</g,'&lt;').slice(0,240)}</div>
     <div class="nav"><button class="navb" onclick="go(-1)" ${i<=0?'disabled':''}>β€Ή Prev</button><span class="pos">${i+1} / ${QUEUE.length}${decided[d.id]?' Β· '+decided[d.id]:''}</span><button class="navb" onclick="go(1)" ${i>=QUEUE.length-1?'disabled':''}>Next β€Ί</button></div>
-    <div class="btns"><button class="hot" onclick="decide('hot')">πŸ”₯ HOT</button><button class="fix" onclick="decide('fix')">πŸ”§ Fix</button><button class="not" onclick="decide('not')">πŸ—‘ Trash</button><button class="extract" onclick="extract()">βœ‚οΈ Extract</button></div>
-    <div class="hint">πŸ”₯/β†’ hot Β· πŸ”§/F fix Β· πŸ—‘/← trash Β· βœ‚οΈ/E extract Β· β€Ή Prev , / [ Β· Next β€Ί . / ] (skip without deciding) Β· dotted = auto-detected errors</div>`;
+    <div class="btns"><button class="hot" onclick="decide('hot')">πŸ”₯ HOT</button><button class="fix" onclick="decide('fix')">πŸ”§ Fix</button><button class="not" onclick="decide('not')">πŸ—‘ NOT</button><button class="etsy" onclick="sendToEtsy()" style="background:#d4634e;color:#fff">πŸ›’ Etsy</button><button class="extract" onclick="extract()">βœ‚οΈ Extract</button></div>
+    <div class="hint">πŸ”₯/β†’ HOT Β· πŸ”§/F Fix Β· πŸ—‘/← NOT Β· πŸ›’/S Etsy bucket Β· βœ‚οΈ/E Extract Β· β€Ή Prev , / [ Β· Next β€Ί . / ] (skip) Β· dotted = auto-detected errors</div>`;
   applyScale();                   // size tile to the chosen print width + draw the inch ruler
   if(autoIdOn) autoIdCurrent();   // opt-in: only scan when the Auto-ID toggle is on
 }
@@ -591,6 +591,39 @@ async function autoIdCurrent(){
 
 // Extract the OPAQUE motif only (alpha-trimmed solid silhouette) β†’ elements library.
 // Non-terminal: saves the element + confirms; you still HOT/Fix/Trash the source.
+// πŸ›’ Send the current item to the Etsy bucket β€” gets queued for digital-download
+// listing (state='queued', target_dpi=300 β†’ 24" tile upscale). Non-destructive:
+// the design stays in PG with an 'etsy-bucket' tag; the etsy push happens via
+// the activation wizard. Advances the queue on success like HOT/Fix/NOT.
+async function sendToEtsy(){
+  if(busy || i>=QUEUE.length) return; const d=QUEUE[i];
+  busy=true;
+  const btn=document.querySelector('.etsy'); const orig=btn?btn.textContent:'';
+  if(btn){btn.disabled=true; btn.textContent='πŸ›’ sending…';}
+  try{
+    const r=await fetch(q('/api/etsy-bucket/add'),{method:'POST',headers:{'Content-Type':'application/json'},
+      body:JSON.stringify({ids:[d.id], target_dpi:300})});
+    const j=await r.json();
+    if(!r.ok || !j.ok) throw new Error(j.error || ('HTTP '+r.status));
+    const result = (j.results||[])[0] || {};
+    if(result.ok === false){
+      // eligibility rejected (e.g., seam FAIL) β€” surface the reason, don't advance
+      busy=false;
+      $('#stage').insertAdjacentHTML('afterbegin',`<div id="saveerr" style="position:fixed;top:62px;left:50%;transform:translateX(-50%);background:#8a5b1f;color:#fff;padding:8px 16px;border-radius:8px;z-index:99;font-size:13px;max-width:90vw">πŸ›’ #${d.id} not eligible: ${(result.reason||'unknown')}</div>`);
+      setTimeout(()=>document.getElementById('saveerr')?.remove(),5000);
+      if(btn){btn.disabled=false; btn.textContent=orig;}
+      return;
+    }
+    flash('πŸ›’'); decided[d.id]='etsy';
+    i++; busy=false; render();
+  }catch(e){
+    busy=false;
+    flash('⚠'); console.error('etsy send failed',d.id,e);
+    $('#stage').insertAdjacentHTML('afterbegin',`<div id="saveerr" style="position:fixed;top:62px;left:50%;transform:translateX(-50%);background:#a13030;color:#fff;padding:8px 16px;border-radius:8px;z-index:99;font-size:13px">πŸ›’ send failed for #${d.id}: ${e.message}</div>`);
+    setTimeout(()=>document.getElementById('saveerr')?.remove(),4000);
+    if(btn){btn.disabled=false; btn.textContent=orig;}
+  }
+}
 async function extract(){
   if(i>=QUEUE.length) return; const d=QUEUE[i];
   const btn=document.querySelector('.extract'); if(btn){btn.disabled=true;btn.textContent='βœ‚οΈ …';}
@@ -657,6 +690,7 @@ document.addEventListener('keydown',e=>{ const k=e.key.toLowerCase();
   if(k===','||e.key==='[')return go(-1);          // prev β€” skip back without deciding
   if(k==='.'||e.key===']')return go(1);           // next β€” skip forward without deciding
   if(e.key==='ArrowRight'||k==='h')decide('hot'); else if(e.key==='ArrowLeft'||k==='n')decide('not');
+  else if(k==='s')sendToEtsy();
   else if(k==='f')decide('fix'); else if(k==='e')extract(); });
 
 // collection picker β€” switch the hot-or-not queue to any wallco.ai line

← 8581f87 night-builder: land UNPUBLISHED β€” every new comfy roll requi  Β·  back to Wallco Ai  Β·  drunk-curator HOT = always publish (Steve directive 'dont st d7db846 β†’