← back to Wallco Ai
pdp: room-size mural preview block (web previews + API field + PDP section)
b4013ff3b617e0983c7b0d275d83eb4dee312954 · 2026-06-03 21:16:15 -0700 · Steve Abrams
Surfaces built 12x11ft scenic-mural masters as a panelized wall product on the
design PDP. mural-web-preview.py downscales each Henry master to a ~234KB web JPG
+ a sidecar JSON (dims/panels) — both small, in-repo, ship to prod (the 500MB TIF
stays Mac2-local). server.js: static route /designs/mural-preview, a 'mural' field
on /api/design/:id, and a conditional 'Room-size mural' block on the bare PDP
(preview image + 12x11ft + 6in install-bleed copy + 24/36/54in panel chips). Renders
only for designs with a master; hidden otherwise. node -c clean; real-browser
validated (block + 1400px preview render on #27151, absent on #54099). 28 previews
generated. NOT deployed — prod push is Steve-gated.
Files touched
A data/mural-previews/10557.jpgA data/mural-previews/10557.jsonA data/mural-previews/11308.jpgA data/mural-previews/11308.jsonA data/mural-previews/12599.jpgA data/mural-previews/12599.jsonA data/mural-previews/12635.jpgA data/mural-previews/12635.jsonA data/mural-previews/14136.jpgA data/mural-previews/14136.jsonA data/mural-previews/14450.jpgA data/mural-previews/14450.jsonA data/mural-previews/14681.jpgA data/mural-previews/14681.jsonA data/mural-previews/16204.jpgA data/mural-previews/16204.jsonA data/mural-previews/20213.jpgA data/mural-previews/20213.jsonA data/mural-previews/20774.jpgA data/mural-previews/20774.jsonA data/mural-previews/21079.jpgA data/mural-previews/21079.jsonA data/mural-previews/21976.jpgA data/mural-previews/21976.jsonA data/mural-previews/25045.jpgA data/mural-previews/25045.jsonA data/mural-previews/25046.jpgA data/mural-previews/25046.jsonA data/mural-previews/25051.jpgA data/mural-previews/25051.jsonA data/mural-previews/25052.jpgA data/mural-previews/25052.jsonA data/mural-previews/25059.jpgA data/mural-previews/25059.jsonA data/mural-previews/27151.jpgA data/mural-previews/27151.jsonA data/mural-previews/31169.jpgA data/mural-previews/31169.jsonA data/mural-previews/31866.jpgA data/mural-previews/31866.jsonA data/mural-previews/33427.jpgA data/mural-previews/33427.jsonA data/mural-previews/37412.jpgA data/mural-previews/37412.jsonA data/mural-previews/37417.jpgA data/mural-previews/37417.jsonA data/mural-previews/37613.jpgA data/mural-previews/37613.jsonA data/mural-previews/37709.jpgA data/mural-previews/37709.jsonA data/mural-previews/37715.jpgA data/mural-previews/37715.jsonA data/mural-previews/37769.jpgA data/mural-previews/37769.jsonA data/mural-previews/37893.jpgA data/mural-previews/37893.jsonA scripts/mural-web-preview.pyM server.js
Diff
commit b4013ff3b617e0983c7b0d275d83eb4dee312954
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Wed Jun 3 21:16:15 2026 -0700
pdp: room-size mural preview block (web previews + API field + PDP section)
Surfaces built 12x11ft scenic-mural masters as a panelized wall product on the
design PDP. mural-web-preview.py downscales each Henry master to a ~234KB web JPG
+ a sidecar JSON (dims/panels) — both small, in-repo, ship to prod (the 500MB TIF
stays Mac2-local). server.js: static route /designs/mural-preview, a 'mural' field
on /api/design/:id, and a conditional 'Room-size mural' block on the bare PDP
(preview image + 12x11ft + 6in install-bleed copy + 24/36/54in panel chips). Renders
only for designs with a master; hidden otherwise. node -c clean; real-browser
validated (block + 1400px preview render on #27151, absent on #54099). 28 previews
generated. NOT deployed — prod push is Steve-gated.
---
data/mural-previews/10557.jpg | Bin 0 -> 134918 bytes
data/mural-previews/10557.json | 1 +
data/mural-previews/11308.jpg | Bin 0 -> 153496 bytes
data/mural-previews/11308.json | 1 +
data/mural-previews/12599.jpg | Bin 0 -> 147574 bytes
data/mural-previews/12599.json | 1 +
data/mural-previews/12635.jpg | Bin 0 -> 94163 bytes
data/mural-previews/12635.json | 1 +
data/mural-previews/14136.jpg | Bin 0 -> 249138 bytes
data/mural-previews/14136.json | 1 +
data/mural-previews/14450.jpg | Bin 0 -> 218515 bytes
data/mural-previews/14450.json | 1 +
data/mural-previews/14681.jpg | Bin 0 -> 161301 bytes
data/mural-previews/14681.json | 1 +
data/mural-previews/16204.jpg | Bin 0 -> 149231 bytes
data/mural-previews/16204.json | 1 +
data/mural-previews/20213.jpg | Bin 0 -> 135372 bytes
data/mural-previews/20213.json | 1 +
data/mural-previews/20774.jpg | Bin 0 -> 291787 bytes
data/mural-previews/20774.json | 1 +
data/mural-previews/21079.jpg | Bin 0 -> 339407 bytes
data/mural-previews/21079.json | 1 +
data/mural-previews/21976.jpg | Bin 0 -> 173908 bytes
data/mural-previews/21976.json | 1 +
data/mural-previews/25045.jpg | Bin 0 -> 245088 bytes
data/mural-previews/25045.json | 1 +
data/mural-previews/25046.jpg | Bin 0 -> 252890 bytes
data/mural-previews/25046.json | 1 +
data/mural-previews/25051.jpg | Bin 0 -> 252118 bytes
data/mural-previews/25051.json | 1 +
data/mural-previews/25052.jpg | Bin 0 -> 240079 bytes
data/mural-previews/25052.json | 1 +
data/mural-previews/25059.jpg | Bin 0 -> 247987 bytes
data/mural-previews/25059.json | 1 +
data/mural-previews/27151.jpg | Bin 0 -> 309820 bytes
data/mural-previews/27151.json | 1 +
data/mural-previews/31169.jpg | Bin 0 -> 240018 bytes
data/mural-previews/31169.json | 1 +
data/mural-previews/31866.jpg | Bin 0 -> 294073 bytes
data/mural-previews/31866.json | 1 +
data/mural-previews/33427.jpg | Bin 0 -> 239797 bytes
data/mural-previews/33427.json | 1 +
data/mural-previews/37412.jpg | Bin 0 -> 323624 bytes
data/mural-previews/37412.json | 1 +
data/mural-previews/37417.jpg | Bin 0 -> 176375 bytes
data/mural-previews/37417.json | 1 +
data/mural-previews/37613.jpg | Bin 0 -> 229971 bytes
data/mural-previews/37613.json | 1 +
data/mural-previews/37709.jpg | Bin 0 -> 273883 bytes
data/mural-previews/37709.json | 1 +
data/mural-previews/37715.jpg | Bin 0 -> 288429 bytes
data/mural-previews/37715.json | 1 +
data/mural-previews/37769.jpg | Bin 0 -> 232858 bytes
data/mural-previews/37769.json | 1 +
data/mural-previews/37893.jpg | Bin 0 -> 289023 bytes
data/mural-previews/37893.json | 1 +
scripts/mural-web-preview.py | 79 +++++++++++++++++++++++++++++++++++++++++
server.js | 49 +++++++++++++++++++++++++
58 files changed, 156 insertions(+)
diff --git a/data/mural-previews/10557.jpg b/data/mural-previews/10557.jpg
new file mode 100644
index 0000000..547c3ab
Binary files /dev/null and b/data/mural-previews/10557.jpg differ
diff --git a/data/mural-previews/10557.json b/data/mural-previews/10557.json
new file mode 100644
index 0000000..63b729c
--- /dev/null
+++ b/data/mural-previews/10557.json
@@ -0,0 +1 @@
+{"print_ft": 4.0, "height_ft": 4.0, "master_px": [7200, 7200], "dpi": 150, "bleed_in": 6.0, "panels": {"24in": 2, "36in": 2, "54in": 1}}
\ No newline at end of file
diff --git a/data/mural-previews/11308.jpg b/data/mural-previews/11308.jpg
new file mode 100644
index 0000000..c5713b5
Binary files /dev/null and b/data/mural-previews/11308.jpg differ
diff --git a/data/mural-previews/11308.json b/data/mural-previews/11308.json
new file mode 100644
index 0000000..db41901
--- /dev/null
+++ b/data/mural-previews/11308.json
@@ -0,0 +1 @@
+{"print_ft": 12.0, "height_ft": 11.0, "master_px": [21600, 19800], "dpi": 150, "bleed_in": 6.0, "panels": {"24in": 6, "36in": 4, "54in": 3}}
\ No newline at end of file
diff --git a/data/mural-previews/12599.jpg b/data/mural-previews/12599.jpg
new file mode 100644
index 0000000..290fab6
Binary files /dev/null and b/data/mural-previews/12599.jpg differ
diff --git a/data/mural-previews/12599.json b/data/mural-previews/12599.json
new file mode 100644
index 0000000..db41901
--- /dev/null
+++ b/data/mural-previews/12599.json
@@ -0,0 +1 @@
+{"print_ft": 12.0, "height_ft": 11.0, "master_px": [21600, 19800], "dpi": 150, "bleed_in": 6.0, "panels": {"24in": 6, "36in": 4, "54in": 3}}
\ No newline at end of file
diff --git a/data/mural-previews/12635.jpg b/data/mural-previews/12635.jpg
new file mode 100644
index 0000000..03a1aa6
Binary files /dev/null and b/data/mural-previews/12635.jpg differ
diff --git a/data/mural-previews/12635.json b/data/mural-previews/12635.json
new file mode 100644
index 0000000..db41901
--- /dev/null
+++ b/data/mural-previews/12635.json
@@ -0,0 +1 @@
+{"print_ft": 12.0, "height_ft": 11.0, "master_px": [21600, 19800], "dpi": 150, "bleed_in": 6.0, "panels": {"24in": 6, "36in": 4, "54in": 3}}
\ No newline at end of file
diff --git a/data/mural-previews/14136.jpg b/data/mural-previews/14136.jpg
new file mode 100644
index 0000000..51a0857
Binary files /dev/null and b/data/mural-previews/14136.jpg differ
diff --git a/data/mural-previews/14136.json b/data/mural-previews/14136.json
new file mode 100644
index 0000000..db41901
--- /dev/null
+++ b/data/mural-previews/14136.json
@@ -0,0 +1 @@
+{"print_ft": 12.0, "height_ft": 11.0, "master_px": [21600, 19800], "dpi": 150, "bleed_in": 6.0, "panels": {"24in": 6, "36in": 4, "54in": 3}}
\ No newline at end of file
diff --git a/data/mural-previews/14450.jpg b/data/mural-previews/14450.jpg
new file mode 100644
index 0000000..1faed28
Binary files /dev/null and b/data/mural-previews/14450.jpg differ
diff --git a/data/mural-previews/14450.json b/data/mural-previews/14450.json
new file mode 100644
index 0000000..db41901
--- /dev/null
+++ b/data/mural-previews/14450.json
@@ -0,0 +1 @@
+{"print_ft": 12.0, "height_ft": 11.0, "master_px": [21600, 19800], "dpi": 150, "bleed_in": 6.0, "panels": {"24in": 6, "36in": 4, "54in": 3}}
\ No newline at end of file
diff --git a/data/mural-previews/14681.jpg b/data/mural-previews/14681.jpg
new file mode 100644
index 0000000..6ad8990
Binary files /dev/null and b/data/mural-previews/14681.jpg differ
diff --git a/data/mural-previews/14681.json b/data/mural-previews/14681.json
new file mode 100644
index 0000000..db41901
--- /dev/null
+++ b/data/mural-previews/14681.json
@@ -0,0 +1 @@
+{"print_ft": 12.0, "height_ft": 11.0, "master_px": [21600, 19800], "dpi": 150, "bleed_in": 6.0, "panels": {"24in": 6, "36in": 4, "54in": 3}}
\ No newline at end of file
diff --git a/data/mural-previews/16204.jpg b/data/mural-previews/16204.jpg
new file mode 100644
index 0000000..061c179
Binary files /dev/null and b/data/mural-previews/16204.jpg differ
diff --git a/data/mural-previews/16204.json b/data/mural-previews/16204.json
new file mode 100644
index 0000000..db41901
--- /dev/null
+++ b/data/mural-previews/16204.json
@@ -0,0 +1 @@
+{"print_ft": 12.0, "height_ft": 11.0, "master_px": [21600, 19800], "dpi": 150, "bleed_in": 6.0, "panels": {"24in": 6, "36in": 4, "54in": 3}}
\ No newline at end of file
diff --git a/data/mural-previews/20213.jpg b/data/mural-previews/20213.jpg
new file mode 100644
index 0000000..6c0029c
Binary files /dev/null and b/data/mural-previews/20213.jpg differ
diff --git a/data/mural-previews/20213.json b/data/mural-previews/20213.json
new file mode 100644
index 0000000..db41901
--- /dev/null
+++ b/data/mural-previews/20213.json
@@ -0,0 +1 @@
+{"print_ft": 12.0, "height_ft": 11.0, "master_px": [21600, 19800], "dpi": 150, "bleed_in": 6.0, "panels": {"24in": 6, "36in": 4, "54in": 3}}
\ No newline at end of file
diff --git a/data/mural-previews/20774.jpg b/data/mural-previews/20774.jpg
new file mode 100644
index 0000000..fb3f271
Binary files /dev/null and b/data/mural-previews/20774.jpg differ
diff --git a/data/mural-previews/20774.json b/data/mural-previews/20774.json
new file mode 100644
index 0000000..db41901
--- /dev/null
+++ b/data/mural-previews/20774.json
@@ -0,0 +1 @@
+{"print_ft": 12.0, "height_ft": 11.0, "master_px": [21600, 19800], "dpi": 150, "bleed_in": 6.0, "panels": {"24in": 6, "36in": 4, "54in": 3}}
\ No newline at end of file
diff --git a/data/mural-previews/21079.jpg b/data/mural-previews/21079.jpg
new file mode 100644
index 0000000..0afaa63
Binary files /dev/null and b/data/mural-previews/21079.jpg differ
diff --git a/data/mural-previews/21079.json b/data/mural-previews/21079.json
new file mode 100644
index 0000000..db41901
--- /dev/null
+++ b/data/mural-previews/21079.json
@@ -0,0 +1 @@
+{"print_ft": 12.0, "height_ft": 11.0, "master_px": [21600, 19800], "dpi": 150, "bleed_in": 6.0, "panels": {"24in": 6, "36in": 4, "54in": 3}}
\ No newline at end of file
diff --git a/data/mural-previews/21976.jpg b/data/mural-previews/21976.jpg
new file mode 100644
index 0000000..f6d50b4
Binary files /dev/null and b/data/mural-previews/21976.jpg differ
diff --git a/data/mural-previews/21976.json b/data/mural-previews/21976.json
new file mode 100644
index 0000000..db41901
--- /dev/null
+++ b/data/mural-previews/21976.json
@@ -0,0 +1 @@
+{"print_ft": 12.0, "height_ft": 11.0, "master_px": [21600, 19800], "dpi": 150, "bleed_in": 6.0, "panels": {"24in": 6, "36in": 4, "54in": 3}}
\ No newline at end of file
diff --git a/data/mural-previews/25045.jpg b/data/mural-previews/25045.jpg
new file mode 100644
index 0000000..799e860
Binary files /dev/null and b/data/mural-previews/25045.jpg differ
diff --git a/data/mural-previews/25045.json b/data/mural-previews/25045.json
new file mode 100644
index 0000000..db41901
--- /dev/null
+++ b/data/mural-previews/25045.json
@@ -0,0 +1 @@
+{"print_ft": 12.0, "height_ft": 11.0, "master_px": [21600, 19800], "dpi": 150, "bleed_in": 6.0, "panels": {"24in": 6, "36in": 4, "54in": 3}}
\ No newline at end of file
diff --git a/data/mural-previews/25046.jpg b/data/mural-previews/25046.jpg
new file mode 100644
index 0000000..c8d2aff
Binary files /dev/null and b/data/mural-previews/25046.jpg differ
diff --git a/data/mural-previews/25046.json b/data/mural-previews/25046.json
new file mode 100644
index 0000000..db41901
--- /dev/null
+++ b/data/mural-previews/25046.json
@@ -0,0 +1 @@
+{"print_ft": 12.0, "height_ft": 11.0, "master_px": [21600, 19800], "dpi": 150, "bleed_in": 6.0, "panels": {"24in": 6, "36in": 4, "54in": 3}}
\ No newline at end of file
diff --git a/data/mural-previews/25051.jpg b/data/mural-previews/25051.jpg
new file mode 100644
index 0000000..60f69b8
Binary files /dev/null and b/data/mural-previews/25051.jpg differ
diff --git a/data/mural-previews/25051.json b/data/mural-previews/25051.json
new file mode 100644
index 0000000..db41901
--- /dev/null
+++ b/data/mural-previews/25051.json
@@ -0,0 +1 @@
+{"print_ft": 12.0, "height_ft": 11.0, "master_px": [21600, 19800], "dpi": 150, "bleed_in": 6.0, "panels": {"24in": 6, "36in": 4, "54in": 3}}
\ No newline at end of file
diff --git a/data/mural-previews/25052.jpg b/data/mural-previews/25052.jpg
new file mode 100644
index 0000000..f08802a
Binary files /dev/null and b/data/mural-previews/25052.jpg differ
diff --git a/data/mural-previews/25052.json b/data/mural-previews/25052.json
new file mode 100644
index 0000000..db41901
--- /dev/null
+++ b/data/mural-previews/25052.json
@@ -0,0 +1 @@
+{"print_ft": 12.0, "height_ft": 11.0, "master_px": [21600, 19800], "dpi": 150, "bleed_in": 6.0, "panels": {"24in": 6, "36in": 4, "54in": 3}}
\ No newline at end of file
diff --git a/data/mural-previews/25059.jpg b/data/mural-previews/25059.jpg
new file mode 100644
index 0000000..ba85e2c
Binary files /dev/null and b/data/mural-previews/25059.jpg differ
diff --git a/data/mural-previews/25059.json b/data/mural-previews/25059.json
new file mode 100644
index 0000000..db41901
--- /dev/null
+++ b/data/mural-previews/25059.json
@@ -0,0 +1 @@
+{"print_ft": 12.0, "height_ft": 11.0, "master_px": [21600, 19800], "dpi": 150, "bleed_in": 6.0, "panels": {"24in": 6, "36in": 4, "54in": 3}}
\ No newline at end of file
diff --git a/data/mural-previews/27151.jpg b/data/mural-previews/27151.jpg
new file mode 100644
index 0000000..0da832f
Binary files /dev/null and b/data/mural-previews/27151.jpg differ
diff --git a/data/mural-previews/27151.json b/data/mural-previews/27151.json
new file mode 100644
index 0000000..db41901
--- /dev/null
+++ b/data/mural-previews/27151.json
@@ -0,0 +1 @@
+{"print_ft": 12.0, "height_ft": 11.0, "master_px": [21600, 19800], "dpi": 150, "bleed_in": 6.0, "panels": {"24in": 6, "36in": 4, "54in": 3}}
\ No newline at end of file
diff --git a/data/mural-previews/31169.jpg b/data/mural-previews/31169.jpg
new file mode 100644
index 0000000..4ef3d87
Binary files /dev/null and b/data/mural-previews/31169.jpg differ
diff --git a/data/mural-previews/31169.json b/data/mural-previews/31169.json
new file mode 100644
index 0000000..db41901
--- /dev/null
+++ b/data/mural-previews/31169.json
@@ -0,0 +1 @@
+{"print_ft": 12.0, "height_ft": 11.0, "master_px": [21600, 19800], "dpi": 150, "bleed_in": 6.0, "panels": {"24in": 6, "36in": 4, "54in": 3}}
\ No newline at end of file
diff --git a/data/mural-previews/31866.jpg b/data/mural-previews/31866.jpg
new file mode 100644
index 0000000..0c294d0
Binary files /dev/null and b/data/mural-previews/31866.jpg differ
diff --git a/data/mural-previews/31866.json b/data/mural-previews/31866.json
new file mode 100644
index 0000000..db41901
--- /dev/null
+++ b/data/mural-previews/31866.json
@@ -0,0 +1 @@
+{"print_ft": 12.0, "height_ft": 11.0, "master_px": [21600, 19800], "dpi": 150, "bleed_in": 6.0, "panels": {"24in": 6, "36in": 4, "54in": 3}}
\ No newline at end of file
diff --git a/data/mural-previews/33427.jpg b/data/mural-previews/33427.jpg
new file mode 100644
index 0000000..030a8e7
Binary files /dev/null and b/data/mural-previews/33427.jpg differ
diff --git a/data/mural-previews/33427.json b/data/mural-previews/33427.json
new file mode 100644
index 0000000..db41901
--- /dev/null
+++ b/data/mural-previews/33427.json
@@ -0,0 +1 @@
+{"print_ft": 12.0, "height_ft": 11.0, "master_px": [21600, 19800], "dpi": 150, "bleed_in": 6.0, "panels": {"24in": 6, "36in": 4, "54in": 3}}
\ No newline at end of file
diff --git a/data/mural-previews/37412.jpg b/data/mural-previews/37412.jpg
new file mode 100644
index 0000000..4e010bf
Binary files /dev/null and b/data/mural-previews/37412.jpg differ
diff --git a/data/mural-previews/37412.json b/data/mural-previews/37412.json
new file mode 100644
index 0000000..db41901
--- /dev/null
+++ b/data/mural-previews/37412.json
@@ -0,0 +1 @@
+{"print_ft": 12.0, "height_ft": 11.0, "master_px": [21600, 19800], "dpi": 150, "bleed_in": 6.0, "panels": {"24in": 6, "36in": 4, "54in": 3}}
\ No newline at end of file
diff --git a/data/mural-previews/37417.jpg b/data/mural-previews/37417.jpg
new file mode 100644
index 0000000..33d74f3
Binary files /dev/null and b/data/mural-previews/37417.jpg differ
diff --git a/data/mural-previews/37417.json b/data/mural-previews/37417.json
new file mode 100644
index 0000000..db41901
--- /dev/null
+++ b/data/mural-previews/37417.json
@@ -0,0 +1 @@
+{"print_ft": 12.0, "height_ft": 11.0, "master_px": [21600, 19800], "dpi": 150, "bleed_in": 6.0, "panels": {"24in": 6, "36in": 4, "54in": 3}}
\ No newline at end of file
diff --git a/data/mural-previews/37613.jpg b/data/mural-previews/37613.jpg
new file mode 100644
index 0000000..c71c291
Binary files /dev/null and b/data/mural-previews/37613.jpg differ
diff --git a/data/mural-previews/37613.json b/data/mural-previews/37613.json
new file mode 100644
index 0000000..db41901
--- /dev/null
+++ b/data/mural-previews/37613.json
@@ -0,0 +1 @@
+{"print_ft": 12.0, "height_ft": 11.0, "master_px": [21600, 19800], "dpi": 150, "bleed_in": 6.0, "panels": {"24in": 6, "36in": 4, "54in": 3}}
\ No newline at end of file
diff --git a/data/mural-previews/37709.jpg b/data/mural-previews/37709.jpg
new file mode 100644
index 0000000..1aa3c21
Binary files /dev/null and b/data/mural-previews/37709.jpg differ
diff --git a/data/mural-previews/37709.json b/data/mural-previews/37709.json
new file mode 100644
index 0000000..db41901
--- /dev/null
+++ b/data/mural-previews/37709.json
@@ -0,0 +1 @@
+{"print_ft": 12.0, "height_ft": 11.0, "master_px": [21600, 19800], "dpi": 150, "bleed_in": 6.0, "panels": {"24in": 6, "36in": 4, "54in": 3}}
\ No newline at end of file
diff --git a/data/mural-previews/37715.jpg b/data/mural-previews/37715.jpg
new file mode 100644
index 0000000..9f9f7a1
Binary files /dev/null and b/data/mural-previews/37715.jpg differ
diff --git a/data/mural-previews/37715.json b/data/mural-previews/37715.json
new file mode 100644
index 0000000..db41901
--- /dev/null
+++ b/data/mural-previews/37715.json
@@ -0,0 +1 @@
+{"print_ft": 12.0, "height_ft": 11.0, "master_px": [21600, 19800], "dpi": 150, "bleed_in": 6.0, "panels": {"24in": 6, "36in": 4, "54in": 3}}
\ No newline at end of file
diff --git a/data/mural-previews/37769.jpg b/data/mural-previews/37769.jpg
new file mode 100644
index 0000000..e251c0d
Binary files /dev/null and b/data/mural-previews/37769.jpg differ
diff --git a/data/mural-previews/37769.json b/data/mural-previews/37769.json
new file mode 100644
index 0000000..db41901
--- /dev/null
+++ b/data/mural-previews/37769.json
@@ -0,0 +1 @@
+{"print_ft": 12.0, "height_ft": 11.0, "master_px": [21600, 19800], "dpi": 150, "bleed_in": 6.0, "panels": {"24in": 6, "36in": 4, "54in": 3}}
\ No newline at end of file
diff --git a/data/mural-previews/37893.jpg b/data/mural-previews/37893.jpg
new file mode 100644
index 0000000..99fc08a
Binary files /dev/null and b/data/mural-previews/37893.jpg differ
diff --git a/data/mural-previews/37893.json b/data/mural-previews/37893.json
new file mode 100644
index 0000000..db41901
--- /dev/null
+++ b/data/mural-previews/37893.json
@@ -0,0 +1 @@
+{"print_ft": 12.0, "height_ft": 11.0, "master_px": [21600, 19800], "dpi": 150, "bleed_in": 6.0, "panels": {"24in": 6, "36in": 4, "54in": 3}}
\ No newline at end of file
diff --git a/scripts/mural-web-preview.py b/scripts/mural-web-preview.py
new file mode 100644
index 0000000..91a7c6a
--- /dev/null
+++ b/scripts/mural-web-preview.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python3
+"""
+mural-web-preview.py — web-sized previews of room-size mural masters for the PDP.
+
+The 12x11ft masters are 500MB TIFs on Henry (not servable to shoppers). This
+makes a small JPG (~1400px wide, ~150KB) of each so a design's product page can
+show "available as a 12x11ft panelized mural" with a real preview. Previews land
+in data/mural-previews/<id>.jpg — small, in-repo, ship to prod with the deploy.
+
+NO spend, NO DB. Pure local PIL.
+
+Usage:
+ python3 scripts/mural-web-preview.py # all built masters missing a preview
+ python3 scripts/mural-web-preview.py <id>... # specific ids
+ python3 scripts/mural-web-preview.py --force # rebuild existing previews
+"""
+import json
+import sys
+from pathlib import Path
+from PIL import Image
+Image.MAX_IMAGE_PIXELS = None
+
+ROOT = Path(__file__).resolve().parents[1]
+_HENRY = Path("/Volumes/Henry/wallco-ai-archive/mural-masters")
+MURAL_DIR = _HENRY if _HENRY.parent.is_dir() else ROOT / "data" / "mural-masters"
+OUT_DIR = ROOT / "data" / "mural-previews"
+OUT_DIR.mkdir(parents=True, exist_ok=True)
+PREVIEW_W = 1400
+
+
+def built_ids():
+ ids = []
+ for d in sorted(MURAL_DIR.iterdir()) if MURAL_DIR.is_dir() else []:
+ if (d / "summary.json").exists():
+ ids.append(d.name)
+ return ids
+
+
+def make_preview(design_id, force=False):
+ d = MURAL_DIR / str(design_id)
+ master = next((p for p in d.glob("*.tif")), None)
+ if not master:
+ print(f" {design_id}: no master tif", file=sys.stderr); return False
+ out = OUT_DIR / f"{design_id}.jpg"
+ side = OUT_DIR / f"{design_id}.json"
+ # Sidecar JSON (dims + panel counts) ships to prod alongside the jpg, so the
+ # PDP can show the panel breakdown without the Henry-only summary.json.
+ if not side.exists() or force:
+ try:
+ summ = json.loads((d / "summary.json").read_text())
+ panels = {k: (v or {}).get("count", 0) for k, v in (summ.get("panels") or {}).items()}
+ side.write_text(json.dumps({
+ "print_ft": summ.get("print_ft"), "height_ft": summ.get("height_ft"),
+ "master_px": summ.get("master_px"), "dpi": summ.get("dpi"),
+ "bleed_in": summ.get("bleed_in"), "panels": panels,
+ }))
+ except Exception as e:
+ print(f" {design_id}: sidecar skipped ({e})", file=sys.stderr)
+ if out.exists() and not force:
+ return True
+ im = Image.open(master).convert("RGB")
+ w, h = im.size
+ prev = im.resize((PREVIEW_W, round(PREVIEW_W * h / w)), Image.LANCZOS)
+ prev.save(out, format="JPEG", quality=86, optimize=True)
+ print(f" {design_id}: {w}x{h} -> {prev.size[0]}x{prev.size[1]} jpg ({out.stat().st_size//1024} KB)")
+ return True
+
+
+def main():
+ args = [a for a in sys.argv[1:] if not a.startswith("--")]
+ force = "--force" in sys.argv
+ ids = args or built_ids()
+ print(f"generating web previews for {len(ids)} masters -> {OUT_DIR}")
+ ok = sum(1 for i in ids if make_preview(i, force))
+ print(f"done: {ok}/{len(ids)} previews")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/server.js b/server.js
index 28150c1..2b69442 100644
--- a/server.js
+++ b/server.js
@@ -207,6 +207,13 @@ app.use('/designs/room', express.static(path.join(__dirname, 'data', 'rooms'), {
maxAge: '7d',
setHeaders: (res) => res.setHeader('Cache-Control', 'public, max-age=604800')
}));
+// Web-sized previews of room-size mural masters (data/mural-previews/<id>.jpg +
+// <id>.json sidecar). Small + in-repo so they ship to prod; the 500MB TIF
+// masters stay Mac2-local. Powers the PDP "room-size mural" block.
+app.use('/designs/mural-preview', express.static(path.join(__dirname, 'data', 'mural-previews'), {
+ maxAge: '1d',
+ setHeaders: (res) => res.setHeader('Cache-Control', 'public, max-age=86400')
+}));
// ── Dual-path room URL resolver (2026-05-30) ──────────────────────────────
// A room render lives in ONE of two places: the canonical
@@ -526,6 +533,22 @@ app.get('/api/design/:id', (req, res) => {
const cid = parseInt(c, 10);
if (Number.isFinite(cid) && cid > 0 && cid !== id) merged.smartfix_child = cid;
} catch { /* PG hiccup — omit the link */ }
+ // Room-size mural: if a 12x11ft master was built for this design, surface the
+ // web preview + panel breakdown so the PDP can offer it as a panelized mural.
+ try {
+ const pvJpg = path.join(__dirname, 'data', 'mural-previews', id + '.jpg');
+ if (fs.existsSync(pvJpg)) {
+ let info = {};
+ try { info = JSON.parse(fs.readFileSync(path.join(__dirname, 'data', 'mural-previews', id + '.json'), 'utf-8')); } catch {}
+ merged.mural = {
+ available: true,
+ preview_url: '/designs/mural-preview/' + id + '.jpg',
+ print_ft: info.print_ft || 12, height_ft: info.height_ft || 11,
+ bleed_in: info.bleed_in || 6,
+ panels: info.panels || { '24in': 6, '36in': 4, '54in': 3 },
+ };
+ }
+ } catch { /* non-fatal — PDP just won't show the mural block */ }
res.json({ ok: true, design: merged });
});
@@ -13081,6 +13104,31 @@ app.get('/design/:id', (req, res) => {
const _resolvedRooms = resolveDesignRooms(id);
const roomUrlMap = Object.fromEntries(_resolvedRooms.map(r => [r.room_type, r.url]));
const haveRooms = new Set(_resolvedRooms.map(r => r.room_type));
+
+ // Room-size mural block — shown when a 12x11ft master/preview was built for
+ // this design (data/mural-previews/<id>.jpg + sidecar; both ship to prod, the
+ // 500MB TIF master stays Mac2-local). Surfaces the scenic mural as a panelized
+ // wall product on the PDP.
+ let muralBlock = '';
+ try {
+ if (fs.existsSync(path.join(__dirname, 'data', 'mural-previews', id + '.jpg'))) {
+ let _mi = {};
+ try { _mi = JSON.parse(fs.readFileSync(path.join(__dirname, 'data', 'mural-previews', id + '.json'), 'utf-8')); } catch {}
+ const _pn = _mi.panels || { '24in': 6, '36in': 4, '54in': 3 };
+ const _ft = _mi.print_ft || 12, _ht = _mi.height_ft || 11;
+ const _chips = Object.keys(_pn).map(k =>
+ `<span style="padding:3px 10px;background:#f3efe6;border:1px solid #e3ddd0;border-radius:999px;font-size:12px;color:#3a2f1f"><b style="color:#7a5c2e">${_pn[k]}</b> × ${k.replace('in', '"')}</span>`).join('');
+ muralBlock = `
+ <details class="collapse-pane" open>
+ <summary>Room-size mural <span class="cp-sub">${_ft}ft × ${_ht}ft · panelized</span></summary>
+ <div class="collapse-body">
+ <img src="/designs/mural-preview/${id}.jpg" alt="room-size scenic mural at ${_ft}ft x ${_ht}ft" loading="lazy" style="width:100%;border-radius:8px;display:block;margin:6px 0 10px">
+ <p style="font-size:13px;line-height:1.6;color:#5a4a33;margin:0 0 10px">Available as a full-wall scenic mural — one continuous ${_ft}ft × ${_ht}ft scene at 150 DPI with a 6" install bleed top & bottom, split into vertical panels (pick your width):</p>
+ <div style="display:flex;gap:8px;flex-wrap:wrap">${_chips}</div>
+ </div>
+ </details>` ;
+ }
+ } catch (e) { /* non-fatal — PDP just omits the mural block */ }
const roomCtx = ROOM_CONTEXTS
.map(c => { const room = c.candidates.find(r => haveRooms.has(r)); return room ? { id: c.id, label: c.label, room } : null; })
.filter(Boolean);
@@ -17333,6 +17381,7 @@ ${(() => {
</script>
` : ''}
+ ${muralBlock}
${roomCtx.length ? `
<!-- Room mockup with context toggle (residential / commercial / hospitality) -->
<details class="collapse-pane">
← 2fed713 yolo loop: stop (done) — queue reconciled, clean slate
·
back to Wallco Ai
·
fix: digital-for-sale also unpublishes spoon_all_designs (pr 5dc7d6a →