← 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
M scripts/generate_designs.jsM server.js
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 →