[object Object]

← back to Wallco Ai

width slider: offer 24/36/52 on every design + samples form, generator picks per-design from weighted pool

525d067478e25722f8bd533eb4502d987aae0ad0 · 2026-05-11 20:28:58 -0700 · SteveStudio2

Files touched

Diff

commit 525d067478e25722f8bd533eb4502d987aae0ad0
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Mon May 11 20:28:58 2026 -0700

    width slider: offer 24/36/52 on every design + samples form, generator picks per-design from weighted pool
---
 scripts/generate_designs.js | 18 +++++++++++++----
 server.js                   | 49 +++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 61 insertions(+), 6 deletions(-)

diff --git a/scripts/generate_designs.js b/scripts/generate_designs.js
index 288e203..d9de08f 100644
--- a/scripts/generate_designs.js
+++ b/scripts/generate_designs.js
@@ -28,9 +28,15 @@ fs.mkdirSync(OUT, { recursive: true });
 
 const BACKEND = process.env.GEN_BACKEND || 'stub';
 
+// Distribution of generation widths when --width is not explicitly passed.
+// Override via env: GENERATE_WIDTH_POOL="24,24,36,36,52" (entries weight pick frequency).
+const WIDTH_POOL = (process.env.GENERATE_WIDTH_POOL || '24,24,36,36,52')
+  .split(',').map(s => parseFloat(s.trim())).filter(n => Number.isFinite(n) && n > 0);
+function pickWidth() { return WIDTH_POOL[Math.floor(Math.random() * WIDTH_POOL.length)]; }
+
 function args() {
   const a = process.argv.slice(2);
-  const o = { n: 10, kind: 'seamless_tile', width: 24, height: 24, panels: null, category: 'mixed', prompts: null };
+  const o = { n: 10, kind: 'seamless_tile', width: null, height: null, panels: null, category: 'mixed', prompts: null };
   for (let i = 0; i < a.length; i++) {
     const v = a[i+1];
     switch (a[i]) {
@@ -318,7 +324,8 @@ VALUES (
 
 function main() {
   const opt = args();
-  console.log(`Generating ${opt.n} × ${opt.kind} (${opt.width}"×${opt.height}", category=${opt.category}, backend=${BACKEND})`);
+  const widthFixed = (opt.width != null);
+  console.log(`Generating ${opt.n} × ${opt.kind} (${widthFixed ? `${opt.width}"×${opt.height||opt.width}"` : `width mixed from [${WIDTH_POOL.join(',')}]`}, category=${opt.category}, backend=${BACKEND})`);
 
   const prompts = opt.prompts || pickPrompts(opt.category, opt.n);
   const created = [];
@@ -327,6 +334,9 @@ function main() {
     const seed = crypto.randomInt(1, 2**31 - 1);
     const filename = `${Date.now()}_${seed}.png`;
     const outPath = path.join(OUT, filename);
+    // Per-design width pick when not overridden via --width.
+    const thisWidth  = widthFixed ? opt.width  : pickWidth();
+    const thisHeight = (opt.height != null) ? opt.height : thisWidth;
     try {
       generate(prompt, seed, outPath);
       // Triple-layer copyright watermark — metadata + LSB(blue) + DCT(green).
@@ -350,10 +360,10 @@ function main() {
       }
       const id = persistDesign({
         kind: opt.kind, prompt, seed, localPath: outPath,
-        width: opt.width, height: opt.height, panels: opt.panels,
+        width: thisWidth, height: thisHeight, panels: opt.panels,
         category: opt.category
       });
-      console.log(`  ✓ #${id} · seed ${seed} · ${path.basename(outPath)} · ©`);
+      console.log(`  ✓ #${id} · ${thisWidth}" · seed ${seed} · ${path.basename(outPath)} · ©`);
       created.push(id);
     } catch (e) {
       console.log(`  ✗ ${e.message}`);
diff --git a/server.js b/server.js
index 65c267f..f97a345 100644
--- a/server.js
+++ b/server.js
@@ -1635,9 +1635,27 @@ ${htmlHeader('/designs')}
 
       <!-- Sample CTA (DW standing rule — non-negotiable) -->
       <div class="cta-group">
-        <a href="/samples?design=${design.id}" class="btn-primary cta-sample">Request Free Sample</a>
+        <a id="cta-sample-link" href="/samples?design=${design.id}" class="btn-primary cta-sample">Request Free Sample</a>
         <a href="/designs" class="btn-outline">Back to Designs</a>
       </div>
+      <script>
+      (function(){
+        function syncCtaWidth(){
+          var saved = JSON.parse(localStorage.getItem('wallco.material') || 'null');
+          if (!saved || !saved.w) return;
+          var link = document.getElementById('cta-sample-link');
+          if (!link) return;
+          var base = link.getAttribute('href').split('?')[0];
+          var q = new URLSearchParams(link.getAttribute('href').split('?')[1] || '');
+          q.set('width', saved.w);
+          link.setAttribute('href', base + '?' + q.toString());
+        }
+        syncCtaWidth();
+        document.querySelectorAll('.w-tab').forEach(function(t){
+          t.addEventListener('click', function(){ setTimeout(syncCtaWidth, 0); });
+        });
+      })();
+      </script>
 
       <!-- Pair-with sibling palette neighbors -->
       <div class="pair-with-block" style="margin-top:32px">
@@ -2673,6 +2691,9 @@ document.querySelectorAll('.mb-unpin').forEach(btn => {
 app.get('/samples', (req, res) => {
   const designId = req.query.design || '';
   const design   = designId ? DESIGNS.find(d => d.id === parseInt(designId,10)) : null;
+  // Roll width chosen on the design page (query param). Constrained to {24,36,52}.
+  const reqW = parseInt(req.query.width, 10);
+  const selectedWidth = [24, 36, 52].includes(reqW) ? reqW : (design && [24,36,52].includes(Math.round(design.width_in||0)) ? Math.round(design.width_in) : 24);
 
   res.type('html').send(`${htmlHead({
     title: 'Request a Sample — wallco.ai',
@@ -2695,6 +2716,13 @@ ${htmlHeader('/samples')}
       <input type="hidden" name="source" value="samples-page">
       ${design ? `<input type="hidden" name="design_id" value="${design.id}">` : ''}
 
+      <label>Roll width
+        <div id="sample-width-tabs" style="display:flex;gap:8px;margin-top:6px">
+          ${[24,36,52].map(w => `<button type="button" class="sw-tab${w===selectedWidth?' is-on':''}" data-w="${w}" style="flex:1;padding:9px 12px;border:1px solid ${w===selectedWidth?'var(--accent,#222)':'var(--line,#ddd)'};background:${w===selectedWidth?'var(--accent,#222)':'transparent'};color:${w===selectedWidth?'var(--bg,#fff)':'var(--ink,#222)'};font:13px var(--sans,system-ui);font-weight:500;border-radius:6px;cursor:pointer">${w}"</button>`).join('')}
+        </div>
+        <input type="hidden" name="width" id="sample-width-input" value="${selectedWidth}">
+      </label>
+
       <label>Design
         <select name="design_id" ${design ? 'disabled' : ''}>
           ${design
@@ -2715,6 +2743,23 @@ ${htmlHeader('/samples')}
 ${FOOTER}
 
 <script>
+// Roll-width tab wiring (3-way toggle 24/36/52)
+(function(){
+  var tabs = document.querySelectorAll('#sample-width-tabs .sw-tab');
+  var hidden = document.getElementById('sample-width-input');
+  tabs.forEach(function(t){
+    t.addEventListener('click', function(){
+      tabs.forEach(function(x){
+        var on = x === t;
+        x.classList.toggle('is-on', on);
+        x.style.background = on ? 'var(--accent,#222)' : 'transparent';
+        x.style.color      = on ? 'var(--bg,#fff)' : 'var(--ink,#222)';
+        x.style.borderColor= on ? 'var(--accent,#222)' : 'var(--line,#ddd)';
+      });
+      hidden.value = t.dataset.w;
+    });
+  });
+})();
 document.getElementById('sample-form').addEventListener('submit', async function(e) {
   e.preventDefault();
   var form = e.target;
@@ -2778,7 +2823,7 @@ app.post('/api/leads', async (req, res) => {
         ${source  ? `'${source.replace(/'/g,"''")}'`     : 'NULL'}
       );
     `;
-    execSync(`psql ${process.env.DATABASE_URL || 'dw_unified'} -c "${sql.replace(/\n/g,' ')}"`, { encoding: 'utf8' });
+    execSync(`${PSQL_CMD} -c "${sql.replace(/\n/g,' ')}"`, { encoding: 'utf8' });
   } catch (e) {
     console.error('Lead insert error:', e.message);
     // Still return ok — don't block the user on a DB error

← 12f88ca design/:id: replace left side-panel with hamburger → tabbed  ·  back to Wallco Ai  ·  tick 11: process 8 new wikimedia PD images (palette+tileabil 4504906 →