β 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
M public/admin/drunk-curator.html
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,'<').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 β