← back to Wallco Ai
fix(api/room): pass full mural width to upstream renderer for kind=mural
8584a8ad2c0601b2bac6117e31c72a69c9e86aa5 · 2026-05-24 01:14:03 -0700 · Steve Abrams
The /api/room handler hard-defaulted patternWidth=27, patternHeight=27 for
EVERY design — meaning when a mural (kind='mural' or 'mural_panel') was
sent to the upstream room renderer at 45.61.58.125:3075, it tile-repeated
the mural as if it were a 27" repeat motif. Result: chopped scenes,
visible seams, half-mural fragments stacked across the wall.
For murals the renderer must treat the pattern as a single image at full
wall-width, not a tileable repeat. Fix: look up the design's actual
width_in / height_in alongside local_path on the PG query, and when
designKind is mural/mural_panel AND the caller did NOT explicitly pass
patternWidth/patternHeight in the request body, override the defaults
with the design's stored dims.
Backward-compat: explicit patternWidth from the client still wins (admin
testing knob preserved). Non-mural designs (seamless_tile, mural_panel-
adjacent-but-categorized-as-tile like designer-scenic) are unchanged —
only kind='mural' and kind='mural_panel' trip the override.
This is the missing piece for "Generate Room with This Mural" to render
correctly — the existing UI button now hits the right scale.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Files touched
Diff
commit 8584a8ad2c0601b2bac6117e31c72a69c9e86aa5
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sun May 24 01:14:03 2026 -0700
fix(api/room): pass full mural width to upstream renderer for kind=mural
The /api/room handler hard-defaulted patternWidth=27, patternHeight=27 for
EVERY design — meaning when a mural (kind='mural' or 'mural_panel') was
sent to the upstream room renderer at 45.61.58.125:3075, it tile-repeated
the mural as if it were a 27" repeat motif. Result: chopped scenes,
visible seams, half-mural fragments stacked across the wall.
For murals the renderer must treat the pattern as a single image at full
wall-width, not a tileable repeat. Fix: look up the design's actual
width_in / height_in alongside local_path on the PG query, and when
designKind is mural/mural_panel AND the caller did NOT explicitly pass
patternWidth/patternHeight in the request body, override the defaults
with the design's stored dims.
Backward-compat: explicit patternWidth from the client still wins (admin
testing knob preserved). Non-mural designs (seamless_tile, mural_panel-
adjacent-but-categorized-as-tile like designer-scenic) are unchanged —
only kind='mural' and kind='mural_panel' trip the override.
This is the missing piece for "Generate Room with This Mural" to render
correctly — the existing UI button now hits the right scale.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---
server.js | 31 ++++++++++++++++++++++++++++---
1 file changed, 28 insertions(+), 3 deletions(-)
diff --git a/server.js b/server.js
index 2f558f6..5f73acf 100644
--- a/server.js
+++ b/server.js
@@ -13571,24 +13571,49 @@ function localPathForDesign(d) {
// ── POST /api/room — generate room visualization for a design
app.post('/api/room', async (req, res) => {
const { design_id, roomType = 'bedroom', angle = 'straight_on',
- cameraDistance = 5, patternWidth = 27, patternHeight = 27 } = req.body || {};
+ cameraDistance = 5 } = req.body || {};
+ let { patternWidth = 27, patternHeight = 27 } = req.body || {};
// FRESH-PATH FIX (2026-05-12): the in-memory DESIGNS snapshot can be stale
// for designs that were composed/baked after server start — leading to the
// upstream renderer receiving the OLD pattern file. Always read local_path
// live from spoon_all_designs to guarantee we send the CURRENT pattern.
let file = null;
+ let designKind = null, designWidthIn = null, designHeightIn = null;
try {
- const lp = psqlQuery(`SELECT local_path FROM spoon_all_designs WHERE id=${pgEsc(parseInt(design_id, 10))} LIMIT 1;`);
- if (lp && lp.trim()) file = lp.trim();
+ const row = psqlQuery(`SELECT row_to_json(t) FROM (SELECT local_path, kind,
+ width_in, height_in FROM spoon_all_designs WHERE id=${pgEsc(parseInt(design_id, 10))} LIMIT 1) t;`);
+ if (row && row.trim()) {
+ const r = JSON.parse(row);
+ if (r.local_path) file = r.local_path;
+ designKind = r.kind || null;
+ designWidthIn = Number(r.width_in) || null;
+ designHeightIn = Number(r.height_in) || null;
+ }
} catch (e) { /* fall through to in-memory */ }
if (!file) {
const d = findDesign(design_id);
if (!d) return res.status(404).json({ ok: false, error: 'design not found' });
file = localPathForDesign(d);
+ designKind = designKind || d.kind || null;
+ designWidthIn = designWidthIn || Number(d.width_in) || null;
+ designHeightIn = designHeightIn || Number(d.height_in) || null;
}
if (!file || !fs.existsSync(file)) {
return res.status(404).json({ ok: false, error: 'design file missing on disk', file });
}
+ // Mural-aware pattern scale (2026-05-24): when the design is a mural or
+ // mural_panel, the upstream renderer must use the FULL mural width as the
+ // pattern width — otherwise it tile-repeats the mural as if it were a
+ // small motif, which is structurally wrong and produces visible seams /
+ // chopped scenes across the wall. Override default 27" with the design's
+ // own width_in / height_in if the caller didn't pass explicit dims.
+ const isMural = designKind === 'mural' || designKind === 'mural_panel';
+ if (isMural && designWidthIn && req.body && req.body.patternWidth == null) {
+ patternWidth = designWidthIn;
+ }
+ if (isMural && designHeightIn && req.body && req.body.patternHeight == null) {
+ patternHeight = designHeightIn;
+ }
try {
const b64 = fs.readFileSync(file).toString('base64');
// Upstream renderer (45.61.58.125:3075) drops ~5-15% of requests with
← 153962b fix(make_seamless): hard-cut mask threshold to eliminate gho
·
back to Wallco Ai
·
feat(design page): mural pan UI + on-screen arrow controls ea1d852 →