← back to Wallco Ai
tick 11: process 8 new wikimedia PD images (palette+tileability+classification — 4 new AI-input candidates) · 'Surprise me' button on /designs fires random-palette/random-motif chat → 1 fresh SDXL design, ~30s//bin/zsh.014
45049061c17a017d7d1ed3199a3457c75a7933f9 · 2026-05-11 20:31:08 -0700 · Steve
Files touched
M server.jsM src/admin.js
Diff
commit 45049061c17a017d7d1ed3199a3457c75a7933f9
Author: Steve <steve@designerwallcoverings.com>
Date: Mon May 11 20:31:08 2026 -0700
tick 11: process 8 new wikimedia PD images (palette+tileability+classification — 4 new AI-input candidates) · 'Surprise me' button on /designs fires random-palette/random-motif chat → 1 fresh SDXL design, ~30s//bin/zsh.014
---
server.js | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
src/admin.js | 6 +++++-
2 files changed, 54 insertions(+), 1 deletion(-)
diff --git a/server.js b/server.js
index f97a345..5209302 100644
--- a/server.js
+++ b/server.js
@@ -729,6 +729,9 @@ ${htmlHeader('/designs')}
<label class="density-label">Density
<input type="range" id="density-slider" min="2" max="8" step="1" value="4" class="density-range">
</label>
+ <button type="button" id="surprise-btn" class="filter-select"
+ style="background:var(--gold,#c9a14b);color:var(--accent,#0d0d0d);border:0;font-weight:500;padding:8px 14px;cursor:pointer;border-radius:6px;letter-spacing:.04em"
+ title="Generate one fresh design in a random palette · ~30s">✦ Surprise me</button>
</div>
<input type="hidden" name="motif" id="motif-hidden" value="${motifQ}">
</form>
@@ -741,6 +744,52 @@ ${htmlHeader('/designs')}
</div>
<p class="result-count">${total} design${total===1?'':'s'}${cat?' in '+cat:''}${motifQ?' with '+motifQ:''}${q?' matching "'+q+'"':''}</p>
+
+ <div id="surprise-result" style="display:none;margin-bottom:18px;padding:16px;border:1px solid var(--gold);border-radius:8px;background:var(--card-bg)"></div>
+ <script>
+ (function(){
+ var btn = document.getElementById('surprise-btn');
+ if (!btn) return;
+ var palettes = [
+ ['amber and emerald','damask'],
+ ['blush and indigo','floral'],
+ ['oyster and bone','damask'],
+ ['terracotta and sage','mixed'],
+ ['plum and gold','geometric'],
+ ['marine and coral','floral'],
+ ['charcoal and brass','geometric'],
+ ['ivory and persimmon','floral'],
+ ['sapphire and pearl','damask'],
+ ['olive and rose','floral']
+ ];
+ var motifsCue = ['scrollwork','peony','lattice','vine','arabesque','asanoha','fan','blossom','toile','trellis'];
+ btn.addEventListener('click', async function(){
+ var pick = palettes[Math.floor(Math.random()*palettes.length)];
+ var motif = motifsCue[Math.floor(Math.random()*motifsCue.length)];
+ var msg = 'make me a new wallpaper in ' + pick[0] + ' with a ' + motif + ' motif — surprising me';
+ var box = document.getElementById('surprise-result');
+ box.style.display = 'block';
+ box.innerHTML = '<div style="font:13px/1.5 -apple-system,sans-serif;color:var(--ink-soft)">Generating <em>' + pick[0] + ' · ' + motif + '</em>… (~30s)</div>';
+ btn.disabled = true; var orig = btn.textContent; btn.textContent = '… generating';
+ try {
+ var r = await fetch('/api/chat/catalog', {
+ method: 'POST', headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify({ message: msg })
+ });
+ var j = await r.json();
+ var d = (j.actions || []).find(function(a){ return a.type === 'new_design'; });
+ if (d && d.design) {
+ box.innerHTML = '<div style="display:flex;gap:14px;align-items:flex-start"><a href="/design/' + d.design.id + '" style="text-decoration:none"><img src="' + d.design.image_url + '" style="width:140px;height:140px;object-fit:cover;border-radius:6px;border:1px solid var(--line)"></a><div style="font:14px/1.6 var(--sans);color:var(--ink)"><div style="font-family:var(--serif);font-size:18px;margin-bottom:2px">Just generated — #' + d.design.id + '</div><div style="color:var(--ink-soft);font-size:12px"><span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:' + d.design.dominant_hex + ';vertical-align:middle;margin-right:4px"></span>' + d.design.dominant_hex + ' · ' + pick[0] + ' · ' + motif + '</div><a href="/design/' + d.design.id + '" style="display:inline-block;margin-top:10px;color:var(--ink);text-decoration:none;border-bottom:1px solid var(--gold)">View design →</a></div></div>';
+ } else {
+ box.innerHTML = '<div style="color:#b03a2e">Generation failed: ' + (j.error || JSON.stringify(j).slice(0,200)) + '</div>';
+ }
+ } catch (e) {
+ box.innerHTML = '<div style="color:#b03a2e">Error: ' + e.message + '</div>';
+ }
+ btn.disabled = false; btn.textContent = orig;
+ });
+ })();
+ </script>
${isAdmin ? `<div class="export-links">
<a href="/moodboard">Moodboard →</a>
<span style="color:#444">·</span>
diff --git a/src/admin.js b/src/admin.js
index d6ca583..58bc01e 100644
--- a/src/admin.js
+++ b/src/admin.js
@@ -19,8 +19,12 @@
'use strict';
const { execSync } = require('child_process');
+const PSQL_CMD = (process.platform === 'linux')
+ ? `sudo -n -u postgres psql dw_unified -At -q`
+ : `psql dw_unified -At -q`;
+
function psql(sql) {
- return execSync(`psql dw_unified -At -q`, { input: sql, encoding: 'utf8' }).trim();
+ return execSync(PSQL_CMD, { input: sql, encoding: 'utf8' }).trim();
}
function esc(s) { if (s == null) return 'NULL'; return "'" + String(s).replace(/'/g, "''") + "'"; }
← 525d067 width slider: offer 24/36/52 on every design + samples form,
·
back to Wallco Ai
·
fix(kamatera): wrap psql with sudo -u postgres on Linux 4bb293e →