[object Object]

← back to Wallco Ai

stage-la-toile-nb: emit gallery from REAL inserted IDs, not predicted ones

93e7589aed3500cd73af468f50b255acb5e81c5f · 2026-06-03 08:48:49 -0700 · Steve Abrams

Fixes the off-by-N 404 bug (2026-06-03): gallery HTML built inline with predicted IDs
(57280-57309) before the insert assigned real ones (57313-57342), so links 404'd while
designs were live. Now captures {id,title} from each RETURNING id, writes the gallery
after insert/publish, labeled LIVE only when --publish flipped is_published.

Files touched

Diff

commit 93e7589aed3500cd73af468f50b255acb5e81c5f
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Jun 3 08:48:49 2026 -0700

    stage-la-toile-nb: emit gallery from REAL inserted IDs, not predicted ones
    
    Fixes the off-by-N 404 bug (2026-06-03): gallery HTML built inline with predicted IDs
    (57280-57309) before the insert assigned real ones (57313-57342), so links 404'd while
    designs were live. Now captures {id,title} from each RETURNING id, writes the gallery
    after insert/publish, labeled LIVE only when --publish flipped is_published.
---
 scripts/stage-la-toile-nb.js | 39 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)

diff --git a/scripts/stage-la-toile-nb.js b/scripts/stage-la-toile-nb.js
index 9e21291..3a8a4bd 100644
--- a/scripts/stage-la-toile-nb.js
+++ b/scripts/stage-la-toile-nb.js
@@ -44,6 +44,8 @@ print(json.dumps([{'hex':'#{:02x}{:02x}{:02x}'.format(*c),'pct':round(n/tot*100,
 }
 
 const ids = [];
+const meta = [];   // {id,title} captured from the REAL inserted rows — the gallery is built
+                   // from these, never from predicted IDs (the off-by-N 404 bug, 2026-06-03)
 for (let i = 0; i < 30; i++) {
   const src = `/tmp/nb-toile-${String(i + 1).padStart(2, '0')}.png`;
   if (!fs.existsSync(src)) { console.log(`scene ${i + 1}: MISSING ${src} — skip`); continue; }
@@ -61,6 +63,7 @@ RETURNING id;`;
   const id = parseInt(psql(sql), 10);
   psql(`UPDATE all_designs SET image_url='/designs/img/by-id/${id}' WHERE id=${id};`);
   ids.push(id);
+  meta.push({ id, title });
   console.log(`scene ${i + 1} "${title}" -> #${id} (dom ${dom})`);
 }
 fs.writeFileSync('/tmp/nb-toile-staged-ids.json', JSON.stringify(ids));
@@ -75,3 +78,39 @@ COMMIT;`;
   const n = psql(`SELECT count(*) FROM all_designs WHERE id IN (${ids.join(',')}) AND is_published=TRUE;`);
   console.log(`PUBLISHED (DB): ${n}/${ids.length} now is_published=TRUE`);
 }
+
+// Build the gallery from the REAL inserted IDs (meta), AFTER insert/publish. The old flow
+// wrote the gallery with predicted/sequential IDs before the insert assigned real ones, so
+// every link 404'd (the 2026-06-03 off-by-33 bug). Label LIVE only when actually published.
+function htmlEsc(s){ return String(s).replace(/[&<>"]/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c])); }
+function writeGallery(rows, published) {
+  if (!rows.length) { console.log('GALLERY skipped — no rows'); return; }
+  const status = published ? 'LIVE on wallco.ai' : 'STAGED (unpublished) — run with --publish to go live';
+  const first = rows[0].id, last = rows[rows.length - 1].id;
+  const cards = rows.map(m => `    <a class="card" href="https://wallco.ai/design/${m.id}" target="_blank" rel="noopener">
+      <img loading="lazy" src="https://wallco.ai/designs/img/by-id/${m.id}" alt="${htmlEsc(m.title)}">
+      <div class="cap">#${m.id} · ${htmlEsc(m.title)}</div>
+    </a>`).join('\n');
+  const html = `<!doctype html><html lang="en"><head><meta charset="utf-8">
+<meta name="viewport" content="width=device-width,initial-scale=1">
+<title>LA Toile de Jouy — ${status}</title>
+<style>
+  body{margin:0;background:#0e0e10;color:#eee;font:14px/1.45 system-ui,-apple-system,sans-serif}
+  h1{font-weight:500;padding:22px 24px 0;margin:0}
+  p.sub{padding:4px 24px 0;margin:0;color:#9aa2ad}
+  .grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:14px;padding:20px 24px}
+  .card{display:block;background:#17171b;border:1px solid #26262c;border-radius:10px;overflow:hidden;text-decoration:none;color:inherit;transition:border-color .12s}
+  .card:hover{border-color:#5b8def}
+  .card img{width:100%;aspect-ratio:1;object-fit:cover;object-position:center 12%;display:block;background:#222}
+  .cap{padding:8px 10px;font-size:12px;color:#cfcfd6}
+</style></head><body>
+  <h1>LA Toile de Jouy — ${status}</h1>
+  <p class="sub">${rows.length} designs · IDs ${first}–${last} · built ${new Date().toISOString().slice(0,16).replace('T',' ')} UTC</p>
+  <div class="grid">
+${cards}
+  </div>
+</body></html>`;
+  fs.writeFileSync('/tmp/nb-toile-gallery.html', html);
+  console.log(`GALLERY -> /tmp/nb-toile-gallery.html (${rows.length} cards, real IDs ${first}-${last}, ${status})`);
+}
+writeGallery(meta, PUBLISH && ids.length > 0);

← e131ed2 wallco: flip 137 reviewed misclassified mural_panel->seamles  ·  back to Wallco Ai  ·  yolo loop 2026-06-03: ledger + tick 1 (route sweep clean, /c 1f7667f →