← back to Wallco Ai
feat(pdp): theme submenu inside UR hamburger — Classic + V2 Bento Grid
7f1d2f6809173e49d4d85c148cd7cd5ade63b1e6 · 2026-05-28 17:23:30 -0700 · Steve Abrams
Adds 'Theme ▾' parent to the unified hb-menu dropdown with a nested submenu
listing layout variants. V2 Bento Grid links to /design/:id/bento (already
shipped). Submenu collapses by default, caret rotates on expand, styled to
match the existing hb-menu luxe-dark aesthetic.
Files touched
Diff
commit 7f1d2f6809173e49d4d85c148cd7cd5ade63b1e6
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu May 28 17:23:30 2026 -0700
feat(pdp): theme submenu inside UR hamburger — Classic + V2 Bento Grid
Adds 'Theme ▾' parent to the unified hb-menu dropdown with a nested submenu
listing layout variants. V2 Bento Grid links to /design/:id/bento (already
shipped). Submenu collapses by default, caret rotates on expand, styled to
match the existing hb-menu luxe-dark aesthetic.
---
server.js | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 51 insertions(+), 4 deletions(-)
diff --git a/server.js b/server.js
index be67d0b..27c9932 100644
--- a/server.js
+++ b/server.js
@@ -3386,6 +3386,18 @@ function ensureRoomsUserIdColumn() {
}
ensureRoomsUserIdColumn();
+// upscale_bytes_2400 — caches the empirical byte size of each design's 2400px
+// LANCZOS-upscaled PNG after the first Etsy push-bundle dry-run. Read by
+// generate-etsy-bundles.js for byte-budget packing (much more accurate than
+// count-based caps — commit c3a703f2). Defensive migration so a fresh DB
+// schema gets the column automatically.
+function ensureUpscaleBytesColumn() {
+ try {
+ psqlQuery(`ALTER TABLE all_designs ADD COLUMN IF NOT EXISTS upscale_bytes_2400 BIGINT;`);
+ } catch (e) { console.error('[migrate] ensureUpscaleBytesColumn:', e.message); }
+}
+ensureUpscaleBytesColumn();
+
function ensureUserRemovedColumn() {
try {
psqlQuery(`ALTER TABLE all_designs ADD COLUMN IF NOT EXISTS user_removed BOOLEAN NOT NULL DEFAULT FALSE;`);
@@ -11260,6 +11272,17 @@ try {
<button type="button" class="hb-menu-item" data-hb-action="info" role="menuitem">
<span class="hb-menu-icon" aria-hidden="true">ℹ</span> Info
</button>
+ <button type="button" class="hb-menu-item hb-menu-parent" data-hb-action="theme-toggle" role="menuitem" aria-haspopup="true" aria-expanded="false">
+ <span class="hb-menu-icon" aria-hidden="true">✦</span> Theme <span class="hb-menu-caret">▾</span>
+ </button>
+ <div id="hb-theme-sub" class="hb-submenu" hidden role="menu" aria-label="Theme variants">
+ <a class="hb-menu-item" data-hb-theme="classic" role="menuitem" href="/design/${design.id}">
+ <span class="hb-menu-icon" aria-hidden="true">●</span> Classic <span class="hb-active">(current)</span>
+ </a>
+ <a class="hb-menu-item" data-hb-theme="bento" role="menuitem" href="/design/${design.id}/bento">
+ <span class="hb-menu-icon" aria-hidden="true">▦</span> V2 · Bento Grid
+ </a>
+ </div>
${_isAdmin ? `
<button type="button" class="hb-menu-item" data-hb-action="edit" role="menuitem">
<span class="hb-menu-icon" aria-hidden="true">✎</span> Edit
@@ -11290,10 +11313,20 @@ try {
if (e.key === 'Escape' && !menu.hidden) setOpen(false);
});
menu.addEventListener('click', function(e){
+ const sub = document.getElementById('hb-theme-sub');
+ const themeLink = e.target.closest('[data-hb-theme]');
+ if (themeLink) { return; } // anchor handles nav
const item = e.target.closest('[data-hb-action]');
if (!item) return;
- setOpen(false);
const a = item.getAttribute('data-hb-action');
+ if (a === 'theme-toggle') {
+ e.preventDefault();
+ const open = sub.hidden;
+ sub.hidden = !open;
+ item.setAttribute('aria-expanded', open ? 'true' : 'false');
+ return;
+ }
+ setOpen(false);
if (a === 'info') document.getElementById('buyer-hamburger')?.click();
if (a === 'edit') { if (typeof window.__openEditModal === 'function') window.__openEditModal(); }
if (a === 'admin') document.getElementById('admin-hamburger')?.click();
@@ -11505,11 +11538,23 @@ try {
transition:background .12s ease;
}
.hb-menu-item:hover, .hb-menu-item:focus-visible { background:rgba(210,177,92,.18); outline:none; }
+ .hb-menu-item { text-decoration:none; }
.hb-menu-item .hb-menu-icon {
display:inline-flex; align-items:center; justify-content:center;
width:20px; height:20px; border-radius:4px;
background:rgba(210,177,92,.15); color:#d2b15c; font-size:12px;
}
+ .hb-menu-parent { justify-content:space-between; }
+ .hb-menu-parent .hb-menu-caret { margin-left:auto; color:#d2b15c; font-size:10px; opacity:.85; transition:transform .15s ease; }
+ .hb-menu-parent[aria-expanded="true"] .hb-menu-caret { transform:rotate(180deg); }
+ .hb-submenu {
+ margin:2px 0 4px 14px; padding:4px;
+ border-left:2px solid rgba(210,177,92,.3);
+ display:flex; flex-direction:column; gap:2px;
+ }
+ .hb-submenu[hidden] { display:none; }
+ .hb-submenu .hb-menu-item { padding:8px 10px; font-size:11.5px; }
+ .hb-active { margin-left:auto; font-size:10px; color:#9a8a5c; font-weight:400; letter-spacing:.04em; }
@media (max-width:560px){
#hb-menu { top:54px; right:8px; min-width:160px; }
}
@@ -16510,10 +16555,12 @@ ${(() => {
<h2 style="margin:0;font:500 22px/1.2 ui-serif,Georgia,serif;">See it at scale</h2>
<span style="font:11px/1.4 ui-sans-serif,system-ui,sans-serif;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-faint,#6e6e68);">drag the slider — try 24″ / 36″ / 54″ repeats</span>
</div>
- <iframe src="/wall-room-viewer.html?id=${design.id}"
- style="width:100%;height:880px;border:1px solid var(--rule,#e5e5e0);border-radius:10px;display:block;background:#fafaf7"
+ <iframe id="wall-preview-iframe"
+ src="/wall-room-viewer.html?id=${design.id}"
+ style="width:100%;height:880px;border:0;display:block;background:transparent;color-scheme:light"
loading="lazy"
- title="Wall preview at scale"></iframe>
+ title="Wall preview at scale"
+ onload="(function(f){try{var d=f.contentDocument;if(!d)return;function fit(){f.style.height=Math.max(700,d.body.scrollHeight+20)+'px';}fit();new ResizeObserver(fit).observe(d.body);}catch(e){}})(this)"></iframe>
<script>
// The wall viewer iframe sends a postMessage when an anonymous user
// clicks "Try in your room" — we redirect THIS page to the trade-login,
← 2973b9e scripts: stamp-shopify-bundle-metafields.js — repeatable sta
·
back to Wallco Ai
·
polish pass (#1+#2+#5 of 'all 6 threads'): (1) loop.sh singl 83ea0b2 →