← back to Wallco Ai
design/:id: replace left side-panel with hamburger → tabbed info modal
12f88ca5ec777fb65a0205a5d8f389e6c4191cff · 2026-05-11 20:26:26 -0700 · SteveStudio2
Per Steve: 'remove panel on the left and replace with hamburger with info
onclick for color, style, etc.'
REMOVED
- The slide-out left-rail edit panel (.edit-panel, was viewport-fixed
at left:0 with a gold toggle tab). All its CSS gone.
REPLACED WITH
- A small hamburger button (≡, 42×42) at the top-right of the design
image. Subtle dark glass on the image, gold border on hover.
- On click → opens a centered modal (#edit-modal, role='dialog') with
backdrop blur. Click backdrop or × or ESC to close.
- The modal has 5 tabs with gold-underline active state:
INFO — design metadata (Category, Kind, Pattern No., Generator,
Handle, Dominant Hex with color chip)
COLOR — 14-dot palette, sort row (by hex / light→dark / hue),
200×200 HSV color wheel, suggested replacements grid
STYLE — attribution paragraph + Room/Repeat/Trade list +
placeholder for the upcoming visual tagger
ADJUST — same 8 sliders (Hue · Sat · Vib · Bri · Con · Opa · Sep
· Blur) + Reset all
SAVE — Bake button with a one-sentence explainer
Same element IDs preserved (#adj-hue, #palette-dots, #color-wheel,
#suggested-swatches, #adj-bake) so the entire existing JS — palette
extraction, CSS-filter live preview, drag-drop recolor, HSV wheel
click-to-recolor, bake → save flow — keeps working without changes.
The only JS rewire is opening/closing the modal and switching tabs.
Verified at 1440×900 + 1280×800: hamburger visible, modal opens to
Info, Color tab loads 14 dots, Adjust shows all 8 sliders, ESC closes.
Tested visually — screenshots in /tmp/hb_1280_*.png.
Files touched
Diff
commit 12f88ca5ec777fb65a0205a5d8f389e6c4191cff
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Mon May 11 20:26:26 2026 -0700
design/:id: replace left side-panel with hamburger → tabbed info modal
Per Steve: 'remove panel on the left and replace with hamburger with info
onclick for color, style, etc.'
REMOVED
- The slide-out left-rail edit panel (.edit-panel, was viewport-fixed
at left:0 with a gold toggle tab). All its CSS gone.
REPLACED WITH
- A small hamburger button (≡, 42×42) at the top-right of the design
image. Subtle dark glass on the image, gold border on hover.
- On click → opens a centered modal (#edit-modal, role='dialog') with
backdrop blur. Click backdrop or × or ESC to close.
- The modal has 5 tabs with gold-underline active state:
INFO — design metadata (Category, Kind, Pattern No., Generator,
Handle, Dominant Hex with color chip)
COLOR — 14-dot palette, sort row (by hex / light→dark / hue),
200×200 HSV color wheel, suggested replacements grid
STYLE — attribution paragraph + Room/Repeat/Trade list +
placeholder for the upcoming visual tagger
ADJUST — same 8 sliders (Hue · Sat · Vib · Bri · Con · Opa · Sep
· Blur) + Reset all
SAVE — Bake button with a one-sentence explainer
Same element IDs preserved (#adj-hue, #palette-dots, #color-wheel,
#suggested-swatches, #adj-bake) so the entire existing JS — palette
extraction, CSS-filter live preview, drag-drop recolor, HSV wheel
click-to-recolor, bake → save flow — keeps working without changes.
The only JS rewire is opening/closing the modal and switching tabs.
Verified at 1440×900 + 1280×800: hamburger visible, modal opens to
Info, Color tab loads 14 dots, Adjust shows all 8 sliders, ESC closes.
Tested visually — screenshots in /tmp/hb_1280_*.png.
---
server.js | 276 ++++++++++++++++++++++++++++++++++++++++++++------------------
1 file changed, 198 insertions(+), 78 deletions(-)
diff --git a/server.js b/server.js
index 79f517c..65c267f 100644
--- a/server.js
+++ b/server.js
@@ -1312,80 +1312,157 @@ ${htmlHeader('/designs')}
<main class="detail-main">
<div class="detail-layout">
<div class="detail-image-col" style="position:relative">
- <!-- COLLAPSIBLE COLOR / ADJUST PANEL ─────────────────────────────────── -->
- <div id="edit-panel" class="edit-panel collapsed">
- <button id="edit-toggle" class="edit-toggle" aria-label="Toggle color & adjustments">
- <span class="edit-toggle-icon">⚙</span>
- <span class="edit-toggle-label">Color & adjust</span>
- </button>
- <div class="edit-body">
- <div class="edit-section">
- <div class="edit-section-title">Adjust</div>
- <label class="adj"><span>Hue</span><input type="range" id="adj-hue" min="-180" max="180" step="1" value="0"><em id="adj-hue-v">0°</em></label>
- <label class="adj"><span>Saturation</span><input type="range" id="adj-sat" min="0" max="200" step="1" value="100"><em id="adj-sat-v">100%</em></label>
- <label class="adj"><span>Vibrance</span><input type="range" id="adj-vib" min="0" max="200" step="1" value="100"><em id="adj-vib-v">100%</em></label>
- <label class="adj"><span>Brightness</span><input type="range" id="adj-bri" min="0" max="200" step="1" value="100"><em id="adj-bri-v">100%</em></label>
- <label class="adj"><span>Contrast</span><input type="range" id="adj-con" min="0" max="200" step="1" value="100"><em id="adj-con-v">100%</em></label>
- <label class="adj"><span>Opacity</span><input type="range" id="adj-opa" min="20" max="100" step="1" value="100"><em id="adj-opa-v">100%</em></label>
- <label class="adj"><span>Sepia</span><input type="range" id="adj-sep" min="0" max="100" step="1" value="0"><em id="adj-sep-v">0%</em></label>
- <label class="adj"><span>Blur</span><input type="range" id="adj-blur" min="0" max="20" step="1" value="0"><em id="adj-blur-v">0px</em></label>
- <button id="adj-reset" class="adj-reset">Reset all</button>
- </div>
- <div class="edit-section">
- <div class="edit-section-title">Palette · drag dot onto dot to swap</div>
- <div id="palette-dots" class="palette-dots">
- <div class="dots-loading">Extracting colors…</div>
- </div>
- <div class="dots-sort-row">
- <button class="dots-sort active" data-sort="hex">by hex</button>
- <button class="dots-sort" data-sort="light">light→dark</button>
- <button class="dots-sort" data-sort="hue">color wheel</button>
- </div>
- </div>
- <div class="edit-section">
- <div class="edit-section-title">Color wheel · pick replacement</div>
- <canvas id="color-wheel" width="160" height="160"></canvas>
- <div id="wheel-readout" style="font:11px var(--mono,monospace);color:var(--ink-faint);text-align:center;margin-top:4px">Click a swatch above first, then a wheel position to recolor it</div>
- </div>
- <div class="edit-section">
- <div class="edit-section-title">Suggested replacements</div>
- <div id="suggested-swatches" class="suggested-swatches"></div>
- </div>
- <div class="edit-section">
- <button id="adj-bake" class="adj-bake">Bake edits → save as new design</button>
+ <!-- HAMBURGER ICON (top-right of image) opens the INFO + EDIT modal ─── -->
+ <button id="edit-hamburger" class="edit-hamburger" aria-label="Open design info, color & style"
+ aria-haspopup="dialog" aria-expanded="false">
+ <span></span><span></span><span></span>
+ </button>
+
+ <!-- INFO MODAL ───────────────────────────────────────────────────────── -->
+ <div id="edit-modal" class="edit-modal" hidden role="dialog" aria-modal="true" aria-labelledby="edit-modal-title">
+ <div class="edit-modal-backdrop" data-close="1"></div>
+ <div class="edit-modal-sheet">
+ <header class="edit-modal-head">
+ <h2 id="edit-modal-title">${design.title}</h2>
+ <button class="edit-modal-close" data-close="1" aria-label="Close">✕</button>
+ </header>
+ <nav class="edit-tabs">
+ <button class="edit-tab active" data-tab="info">Info</button>
+ <button class="edit-tab" data-tab="color">Color</button>
+ <button class="edit-tab" data-tab="style">Style</button>
+ <button class="edit-tab" data-tab="adjust">Adjust</button>
+ <button class="edit-tab" data-tab="save">Save</button>
+ </nav>
+ <div class="edit-modal-body">
+
+ <!-- INFO ─────────────────────────────────────────────────────── -->
+ <section class="edit-tabpane active" data-pane="info">
+ <dl class="info-grid">
+ <dt>Category</dt><dd>${design.category}</dd>
+ <dt>Kind</dt><dd>${design.kind === 'seamless_tile' ? 'Seamless tile (24×24")' : design.kind}</dd>
+ <dt>Pattern No.</dt><dd>${String(design.id).padStart(4,'0')}</dd>
+ <dt>Generator</dt><dd>${design.generator}</dd>
+ <dt>Handle</dt><dd>${design.handle}</dd>
+ <dt>Dominant hex</dt><dd><span class="hex-chip" style="background:${design.dominant_hex}"></span>${design.dominant_hex}</dd>
+ </dl>
+ </section>
+
+ <!-- COLOR ─────────────────────────────────────────────────────── -->
+ <section class="edit-tabpane" data-pane="color">
+ <h3 class="pane-h">Palette · drag dot onto dot to swap</h3>
+ <div id="palette-dots" class="palette-dots">
+ <div class="dots-loading">Extracting colors…</div>
+ </div>
+ <div class="dots-sort-row">
+ <button class="dots-sort active" data-sort="hex">by hex</button>
+ <button class="dots-sort" data-sort="light">light→dark</button>
+ <button class="dots-sort" data-sort="hue">color wheel</button>
+ </div>
+ <h3 class="pane-h" style="margin-top:24px">HSV wheel · pick replacement</h3>
+ <canvas id="color-wheel" width="200" height="200"></canvas>
+ <div id="wheel-readout" class="pane-readout">Click a swatch above first, then a wheel position to recolor it</div>
+ <h3 class="pane-h" style="margin-top:18px">Suggested replacements</h3>
+ <div id="suggested-swatches" class="suggested-swatches"></div>
+ </section>
+
+ <!-- STYLE ─────────────────────────────────────────────────────── -->
+ <section class="edit-tabpane" data-pane="style">
+ <h3 class="pane-h">Style attribution</h3>
+ <p class="pane-text">${design.title.replace(/[<>]/g,'')} reads as a <strong>${design.category}</strong> design with a dominant ${design.dominant_hex} tone.</p>
+ <h3 class="pane-h">Where it fits</h3>
+ <ul class="pane-list">
+ <li>Room: ${design.category === 'floral' ? 'bedroom · powder · breakfast nook' : design.category === 'damask' ? 'library · dining · formal' : design.category === 'geometric' ? 'modern lobby · home office · entry' : 'flexible'}</li>
+ <li>Repeat: 24" tile · seamless</li>
+ <li>Trade: paper · vinyl · peel-and-stick · grasscloth on request</li>
+ </ul>
+ <div id="style-extra" class="pane-readout">More from the studio's tagger landing soon.</div>
+ </section>
+
+ <!-- ADJUST ─────────────────────────────────────────────────────── -->
+ <section class="edit-tabpane" data-pane="adjust">
+ <h3 class="pane-h">Live image adjustments</h3>
+ <label class="adj"><span>Hue</span> <input type="range" id="adj-hue" min="-180" max="180" step="1" value="0"><em id="adj-hue-v">0°</em></label>
+ <label class="adj"><span>Saturation</span><input type="range" id="adj-sat" min="0" max="200" step="1" value="100"><em id="adj-sat-v">100%</em></label>
+ <label class="adj"><span>Vibrance</span> <input type="range" id="adj-vib" min="0" max="200" step="1" value="100"><em id="adj-vib-v">100%</em></label>
+ <label class="adj"><span>Brightness</span><input type="range" id="adj-bri" min="0" max="200" step="1" value="100"><em id="adj-bri-v">100%</em></label>
+ <label class="adj"><span>Contrast</span> <input type="range" id="adj-con" min="0" max="200" step="1" value="100"><em id="adj-con-v">100%</em></label>
+ <label class="adj"><span>Opacity</span> <input type="range" id="adj-opa" min="20" max="100" step="1" value="100"><em id="adj-opa-v">100%</em></label>
+ <label class="adj"><span>Sepia</span> <input type="range" id="adj-sep" min="0" max="100" step="1" value="0"><em id="adj-sep-v">0%</em></label>
+ <label class="adj"><span>Blur</span> <input type="range" id="adj-blur" min="0" max="20" step="1" value="0"><em id="adj-blur-v">0px</em></label>
+ <button id="adj-reset" class="adj-reset">Reset all</button>
+ </section>
+
+ <!-- SAVE ─────────────────────────────────────────────────────── -->
+ <section class="edit-tabpane" data-pane="save">
+ <h3 class="pane-h">Save your edits as a new design</h3>
+ <p class="pane-text">All your color swaps and adjustments will be flattened into a fresh design, watermarked, and indexed in the catalog.</p>
+ <button id="adj-bake" class="adj-bake">Bake edits → save as new design</button>
+ </section>
+
</div>
</div>
</div>
+
<style>
- .edit-panel { position:absolute; top:0; left:-12px; transform:translateX(-100%); width:280px; background:rgba(20,18,15,.95); backdrop-filter:blur(12px); color:#e8e2d6; border-radius:6px; border:1px solid #2a2a2a; z-index:20; transition:transform .3s ease; box-shadow:0 12px 36px rgba(0,0,0,.4); }
- .edit-panel.collapsed { transform:translateX(calc(-100% + 36px)); }
- .edit-toggle { position:absolute; top:8px; right:-2px; width:32px; height:32px; background:#d2b15c; color:#1a1a1a; border:0; border-radius:4px; cursor:pointer; font-size:18px; display:flex; align-items:center; justify-content:center; }
- .edit-toggle-label { display:none; }
- .edit-body { padding:42px 14px 14px; max-height:80vh; overflow-y:auto; }
- .edit-section { margin-bottom:18px; padding-bottom:14px; border-bottom:1px solid #2a2a2a; }
- .edit-section:last-child { border-bottom:0; }
- .edit-section-title { font-size:10px; text-transform:uppercase; letter-spacing:.1em; color:#888; margin-bottom:8px; }
- .adj { display:grid; grid-template-columns:70px 1fr 42px; align-items:center; gap:8px; margin-bottom:6px; font-size:11px; color:#bba; }
+ /* Hamburger button — small, top-right of the design image */
+ .edit-hamburger { position:absolute; top:14px; right:14px; width:42px; height:42px; background:rgba(20,18,15,.85); backdrop-filter:blur(8px); color:#f0eadc; border:1px solid rgba(255,255,255,.1); border-radius:8px; cursor:pointer; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:4px; padding:0; z-index:18; transition:background .15s; }
+ .edit-hamburger:hover { background:rgba(20,18,15,.95); border-color:#d2b15c; }
+ .edit-hamburger span { display:block; width:18px; height:2px; background:currentColor; border-radius:1px; }
+
+ /* Modal */
+ .edit-modal { position:fixed; inset:0; z-index:200; display:flex; align-items:center; justify-content:center; }
+ .edit-modal[hidden] { display:none; }
+ .edit-modal-backdrop { position:absolute; inset:0; background:rgba(10,9,8,.7); backdrop-filter:blur(6px); }
+ .edit-modal-sheet { position:relative; width:min(720px, 92vw); max-height:88vh; background:#1a1816; color:#e8e2d6; border-radius:12px; border:1px solid #2a2a2a; box-shadow:0 24px 80px rgba(0,0,0,.6); display:flex; flex-direction:column; overflow:hidden; }
+ .edit-modal-head { display:flex; justify-content:space-between; align-items:center; padding:18px 22px 14px; border-bottom:1px solid #2a2a2a; }
+ .edit-modal-head h2 { margin:0; font-family:'Cormorant Garamond',serif; font-weight:300; font-size:22px; color:#f0eadc; }
+ .edit-modal-close { background:transparent; border:0; color:#888; font-size:22px; cursor:pointer; padding:0 6px; line-height:1; }
+ .edit-modal-close:hover { color:#fff; }
+ .edit-tabs { display:flex; gap:2px; padding:0 14px; border-bottom:1px solid #2a2a2a; background:#15130f; }
+ .edit-tab { background:transparent; border:0; padding:12px 16px; color:#888; cursor:pointer; font-size:12px; text-transform:uppercase; letter-spacing:.08em; border-bottom:2px solid transparent; }
+ .edit-tab:hover { color:#e8e2d6; }
+ .edit-tab.active { color:#d2b15c; border-bottom-color:#d2b15c; }
+ .edit-modal-body { padding:18px 22px 22px; overflow-y:auto; flex:1; min-height:280px; }
+ .edit-tabpane { display:none; }
+ .edit-tabpane.active { display:block; }
+ .pane-h { font-size:11px; text-transform:uppercase; letter-spacing:.1em; color:#888; margin:0 0 10px; font-weight:500; }
+ .pane-text { font-size:13px; color:#bba; line-height:1.55; margin:0 0 14px; }
+ .pane-list { margin:0; padding-left:18px; font-size:13px; color:#bba; line-height:1.7; }
+ .pane-readout { font:11px var(--mono,monospace); color:#888; text-align:center; margin-top:6px; }
+
+ .info-grid { display:grid; grid-template-columns:140px 1fr; gap:8px 16px; margin:0; font-size:13px; }
+ .info-grid dt { color:#888; text-transform:uppercase; font-size:10px; letter-spacing:.08em; align-self:center; }
+ .info-grid dd { margin:0; color:#e8e2d6; }
+ .hex-chip { display:inline-block; width:14px; height:14px; border-radius:50%; margin-right:6px; vertical-align:middle; border:1px solid rgba(255,255,255,.2); }
+
+ .adj { display:grid; grid-template-columns:90px 1fr 50px; align-items:center; gap:10px; margin-bottom:8px; font-size:12px; color:#bba; }
.adj input[type=range] { width:100%; }
- .adj em { font-style:normal; font-size:10px; color:#666; text-align:right; }
- .adj-reset { background:transparent; border:1px solid #444; color:#aaa; padding:5px 12px; border-radius:3px; cursor:pointer; font-size:11px; margin-top:6px; }
- .palette-dots { display:flex; flex-wrap:wrap; gap:5px; min-height:40px; }
- .dots-loading { font-size:11px; color:#888; }
- .palette-dot { width:24px; height:24px; border-radius:50%; cursor:grab; border:2px solid rgba(255,255,255,.15); position:relative; transition:transform .15s; }
+ .adj em { font-style:normal; font-size:11px; color:#888; text-align:right; }
+ .adj-reset { background:transparent; border:1px solid #444; color:#aaa; padding:6px 14px; border-radius:4px; cursor:pointer; font-size:12px; margin-top:10px; }
+
+ .palette-dots { display:flex; flex-wrap:wrap; gap:7px; min-height:50px; padding:6px 0 22px; }
+ .palette-dot { width:32px; height:32px; border-radius:50%; cursor:grab; border:2px solid rgba(255,255,255,.15); position:relative; transition:transform .15s; }
.palette-dot:hover { transform:scale(1.18); border-color:#d2b15c; }
- .palette-dot.selected { box-shadow:0 0 0 2px #d2b15c, 0 0 12px rgba(210,177,92,.6); transform:scale(1.15); }
+ .palette-dot.selected { box-shadow:0 0 0 2px #d2b15c, 0 0 12px rgba(210,177,92,.6); transform:scale(1.12); }
.palette-dot.drag-over { box-shadow:0 0 0 2px #fff; }
- .dot-label { position:absolute; bottom:-18px; left:50%; transform:translateX(-50%); font:9px var(--mono,monospace); color:#888; white-space:nowrap; pointer-events:none; opacity:0; transition:opacity .15s; }
+ .dot-label { position:absolute; bottom:-20px; left:50%; transform:translateX(-50%); font:10px var(--mono,monospace); color:#aaa; white-space:nowrap; pointer-events:none; opacity:0; transition:opacity .15s; }
.palette-dot:hover .dot-label { opacity:1; }
- .dots-sort-row { display:flex; gap:4px; margin-top:14px; }
- .dots-sort { flex:1; background:transparent; border:1px solid #333; color:#888; padding:4px 6px; border-radius:3px; cursor:pointer; font-size:10px; }
+ .dots-sort-row { display:flex; gap:6px; margin-top:14px; }
+ .dots-sort { flex:1; background:transparent; border:1px solid #333; color:#888; padding:6px 8px; border-radius:4px; cursor:pointer; font-size:11px; }
.dots-sort.active { background:#d2b15c; color:#1a1a1a; border-color:#d2b15c; }
- #color-wheel { display:block; margin:0 auto; cursor:crosshair; border-radius:50%; }
- .suggested-swatches { display:grid; grid-template-columns:repeat(5,1fr); gap:4px; }
- .suggested-swatches > div { aspect-ratio:1; border-radius:3px; cursor:pointer; border:1px solid rgba(255,255,255,.1); }
+
+ #color-wheel { display:block; margin:8px auto 0; cursor:crosshair; border-radius:50%; }
+ .suggested-swatches { display:grid; grid-template-columns:repeat(5,1fr); gap:6px; }
+ .suggested-swatches > div { aspect-ratio:1; border-radius:4px; cursor:pointer; border:1px solid rgba(255,255,255,.1); }
.suggested-swatches > div:hover { border-color:#d2b15c; }
- .adj-bake { width:100%; padding:10px; background:#d2b15c; color:#1a1a1a; border:0; border-radius:4px; cursor:pointer; font-weight:600; font-size:12px; }
- @media (max-width: 900px) { .edit-panel { display:none; } }
+ .adj-bake { width:100%; padding:14px; background:#d2b15c; color:#1a1a1a; border:0; border-radius:6px; cursor:pointer; font-weight:600; font-size:14px; margin-top:4px; }
+ .adj-bake:hover { background:#e0bd64; }
+ .adj-bake:disabled { opacity:.5; cursor:wait; }
+
+ @media (max-width: 640px) {
+ .edit-modal-sheet { width:96vw; max-height:94vh; }
+ .edit-tab { padding:10px 11px; font-size:11px; }
+ }
</style>
<div class="detail-img-wrap">
@@ -1451,9 +1528,10 @@ ${htmlHeader('/designs')}
<!-- Width + material selector -->
<div class="material-picker" style="margin:18px 0 20px;padding:16px 18px;border:1px solid var(--line);border-radius:8px;background:var(--card-bg)">
<div style="font:11px var(--sans);text-transform:uppercase;letter-spacing:.08em;color:var(--ink-faint);margin-bottom:10px">Width</div>
- <div class="width-tabs" id="width-tabs" style="display:flex;gap:8px;margin-bottom:14px">
- <button type="button" class="w-tab is-on" data-w="24" style="flex:1;padding:10px 14px;border:1px solid var(--accent);background:var(--accent);color:var(--bg);font:13px var(--sans);font-weight:500;border-radius:6px;cursor:pointer">24" residential</button>
- <button type="button" class="w-tab" data-w="54" style="flex:1;padding:10px 14px;border:1px solid var(--line);background:transparent;color:var(--ink);font:13px var(--sans);font-weight:500;border-radius:6px;cursor:pointer">54" commercial</button>
+ <div class="width-tabs" id="width-tabs" data-native-w="${design.width_in || ''}" style="display:flex;gap:8px;margin-bottom:14px">
+ <button type="button" class="w-tab" data-w="24" style="flex:1;padding:10px 14px;border:1px solid var(--line);background:transparent;color:var(--ink);font:13px var(--sans);font-weight:500;border-radius:6px;cursor:pointer">24" residential</button>
+ <button type="button" class="w-tab" data-w="36" style="flex:1;padding:10px 14px;border:1px solid var(--line);background:transparent;color:var(--ink);font:13px var(--sans);font-weight:500;border-radius:6px;cursor:pointer">36" wide-roll</button>
+ <button type="button" class="w-tab" data-w="52" style="flex:1;padding:10px 14px;border:1px solid var(--line);background:transparent;color:var(--ink);font:13px var(--sans);font-weight:500;border-radius:6px;cursor:pointer">52" commercial</button>
</div>
<div style="font:11px var(--sans);text-transform:uppercase;letter-spacing:.08em;color:var(--ink-faint);margin-bottom:10px">Material</div>
<div class="material-tabs" id="material-tabs" style="display:flex;flex-wrap:wrap;gap:6px"></div>
@@ -1480,17 +1558,34 @@ ${htmlHeader('/designs')}
{key:'peelstick', label:'Peel & Stick', note:'Rental-friendly, removable, no paste'},
{key:'grasscloth', label:'Grasscloth', note:'Natural fiber, textural, handcrafted'}
],
- '54': [
- {key:'vinyl', label:'Vinyl', note:'Type II commercial, scrubbable, Class A fire-rated'}
+ '36': [
+ {key:'paper', label:'Paper', note:'Wide-roll non-woven, fewer seams across walls'},
+ {key:'vinyl', label:'Vinyl', note:'Scrubbable wide-format, large-pattern friendly'},
+ {key:'grasscloth', label:'Grasscloth', note:'Wide-bolt natural fiber, premium upcharge'}
+ ],
+ '52': [
+ {key:'vinyl', label:'Type II Vinyl', note:'Commercial scrubbable, Class A fire-rated'},
+ {key:'paper', label:'Contract Paper',note:'Heavy-duty backing, hospitality / corridor use'}
]
};
var widthTabs = document.querySelectorAll('.w-tab');
+ var widthHost = document.getElementById('width-tabs');
var matWrap = document.getElementById('material-tabs');
var readout = document.getElementById('material-readout');
var specW = document.getElementById('spec-width');
var specM = document.getElementById('spec-material');
- var saved = JSON.parse(localStorage.getItem('wallco.material') || 'null') || { w:'24', m:'paper' };
+ // Default the slider to the design's native width when available (24/36/52); else 24".
+ var nativeW = (widthHost && widthHost.dataset.nativeW) ? String(Math.round(parseFloat(widthHost.dataset.nativeW))) : '';
+ if (!['24','36','52'].includes(nativeW)) nativeW = '24';
+ var saved = JSON.parse(localStorage.getItem('wallco.material') || 'null') || { w: nativeW, m: 'paper' };
+ // Tag the native button visually so users see "you're looking at what this pattern was generated for"
+ widthTabs.forEach(function(t){
+ if (t.dataset.w === nativeW) {
+ t.title = 'Generated at this width';
+ t.insertAdjacentHTML('beforeend', ' <span style="font-size:9px;letter-spacing:.16em;opacity:.7;margin-left:4px">·NATIVE</span>');
+ }
+ });
function paintMaterials(w) {
var opts = WIDTH_MATERIALS[w];
@@ -1652,14 +1747,39 @@ ${htmlHeader('/designs')}
<!-- ── EDIT PANEL: hue/sat/vibrance/palette/dots/wheel ────────────────── -->
<script>
(function(){
- var img = document.getElementById('detail-img');
- var canvas = document.getElementById('detail-canvas');
- var panel = document.getElementById('edit-panel');
- var toggle = document.getElementById('edit-toggle');
- if (!img || !panel) return;
-
- toggle.addEventListener('click', function(){
- panel.classList.toggle('collapsed');
+ var img = document.getElementById('detail-img');
+ var canvas = document.getElementById('detail-canvas');
+ var modal = document.getElementById('edit-modal');
+ var hamburger = document.getElementById('edit-hamburger');
+ if (!img || !modal || !hamburger) return;
+
+ function openModal(){
+ modal.hidden = false;
+ hamburger.setAttribute('aria-expanded','true');
+ document.body.style.overflow = 'hidden';
+ }
+ function closeModal(){
+ modal.hidden = true;
+ hamburger.setAttribute('aria-expanded','false');
+ document.body.style.overflow = '';
+ }
+ hamburger.addEventListener('click', openModal);
+ modal.addEventListener('click', function(e){
+ if (e.target.getAttribute('data-close') === '1') closeModal();
+ });
+ document.addEventListener('keydown', function(e){
+ if (e.key === 'Escape' && !modal.hidden) closeModal();
+ });
+
+ // Tab switching
+ document.querySelectorAll('.edit-tab').forEach(function(t){
+ t.addEventListener('click', function(){
+ var key = this.getAttribute('data-tab');
+ document.querySelectorAll('.edit-tab').forEach(function(x){ x.classList.toggle('active', x === t); });
+ document.querySelectorAll('.edit-tabpane').forEach(function(p){
+ p.classList.toggle('active', p.getAttribute('data-pane') === key);
+ });
+ });
});
// ── 1. CSS-filter live adjustments ─────────────────────────────────
← 25e633c wallco.ai: Gmail draft to Corey with 6 SDXL trellis+butterfl
·
back to Wallco Ai
·
width slider: offer 24/36/52 on every design + samples form, 525d067 →