← back to Wallco Ai
ui(designs): move chat pop-out to upper-right; strip ALL sliders + admin clutter
f7a54b8fd0db197a88cb68fdfa8424d8a977c325 · 2026-05-20 15:55:37 -0700 · Steve Abrams
Steve directive 2026-05-20 from screenshot review:
1. Move 'Chat with our designer' pop-out — was bottom-right (bottom:22px;
right:22px). Now upper-right (top:22px; right:22px). Panel anchored
under the FAB instead of above it — slides DOWN on open.
2. Remove ALL sliders globally on /designs —
- density-slider (1-12 range) — top toolbar
- spacing-slider (0-40 admin range) — top toolbar
- scrub-hue + scrub-style (color/style filter sliders block) — below toolbar
- Per-card admin Design/Color/Style sliders (0-10) — review-flyout
3. Strip admin review-flyout per-card markup (sliders, Keep/Reject/Pair
buttons, why-chip, chips). Admin cards now match the public-card UX:
hover reveals just View + Sample. The bulk-select + favorites paths
still run via wireCard.
4. Null-guard 4 JS handlers that referenced now-removed DOM elements:
density-slider boot, wireCard K/R/Pair/why-chip handlers,
loadChipsForVisibleCards .chips innerHTML. All return early when the
element is absent, so script.js works whether the page renders the
admin review controls or not.
Verified locally — 0 JS errors, 0 input[type=range] elements in rendered
HTML, 60 cards with 60 Sample+View action pairs, FAB at (y=22, right=22).
Files touched
M public/css/chat.cssM server.js
Diff
commit f7a54b8fd0db197a88cb68fdfa8424d8a977c325
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Wed May 20 15:55:37 2026 -0700
ui(designs): move chat pop-out to upper-right; strip ALL sliders + admin clutter
Steve directive 2026-05-20 from screenshot review:
1. Move 'Chat with our designer' pop-out — was bottom-right (bottom:22px;
right:22px). Now upper-right (top:22px; right:22px). Panel anchored
under the FAB instead of above it — slides DOWN on open.
2. Remove ALL sliders globally on /designs —
- density-slider (1-12 range) — top toolbar
- spacing-slider (0-40 admin range) — top toolbar
- scrub-hue + scrub-style (color/style filter sliders block) — below toolbar
- Per-card admin Design/Color/Style sliders (0-10) — review-flyout
3. Strip admin review-flyout per-card markup (sliders, Keep/Reject/Pair
buttons, why-chip, chips). Admin cards now match the public-card UX:
hover reveals just View + Sample. The bulk-select + favorites paths
still run via wireCard.
4. Null-guard 4 JS handlers that referenced now-removed DOM elements:
density-slider boot, wireCard K/R/Pair/why-chip handlers,
loadChipsForVisibleCards .chips innerHTML. All return early when the
element is absent, so script.js works whether the page renders the
admin review controls or not.
Verified locally — 0 JS errors, 0 input[type=range] elements in rendered
HTML, 60 cards with 60 Sample+View action pairs, FAB at (y=22, right=22).
---
public/css/chat.css | 9 ++++--
server.js | 87 +++++++++++++++++------------------------------------
2 files changed, 33 insertions(+), 63 deletions(-)
diff --git a/public/css/chat.css b/public/css/chat.css
index d1abc14..6818152 100644
--- a/public/css/chat.css
+++ b/public/css/chat.css
@@ -2,9 +2,10 @@
Dark-mode aware. Inherits the page's theme. */
/* ── floating button (FAB) ───────────────────────────────────────── */
+/* Pinned upper-right per Steve directive 2026-05-20 (was bottom-right). */
#wallco-chat-fab {
position: fixed;
- bottom: 22px;
+ top: 22px;
right: 22px;
z-index: 9998;
background: var(--accent, #0d0d0d);
@@ -25,9 +26,10 @@
#wallco-chat-fab .fab-spark { font-family: var(--serif); font-size: 17px; color: var(--gold, #c9a14b); line-height: 1; }
/* ── panel ───────────────────────────────────────────────────────── */
+/* Anchored under the FAB (now upper-right) — drops down instead of up. */
#wallco-chat-panel {
position: fixed;
- bottom: 22px;
+ top: 78px;
right: 22px;
width: 400px; max-width: calc(100vw - 32px);
height: 600px; max-height: calc(100vh - 44px);
@@ -41,7 +43,8 @@
flex-direction: column;
overflow: hidden;
font-family: var(--sans);
- transform: translateY(8px); opacity: 0;
+ /* Drops DOWN from the upper-right FAB now (was sliding up from bottom). */
+ transform: translateY(-8px); opacity: 0;
transition: transform .22s ease, opacity .22s ease;
}
#wallco-chat-panel.is-open { display: flex; transform: translateY(0); opacity: 1; }
diff --git a/server.js b/server.js
index fb387b8..e8846e5 100644
--- a/server.js
+++ b/server.js
@@ -3793,27 +3793,11 @@ app.get('/designs', (req, res) => {
</div>
</a>
<span class="pattern-num" aria-hidden="true">${_digAdm}</span>
- <div class="review-flyout">
- <div class="card-meta">
- <span class="card-title">${d.title}</span>
- <span class="card-cat">${d.category}</span>
- <span class="color-dot" style="background:${d.dominant_hex}"></span>
- </div>
- <div class="review-controls">
- <div class="sliders">
- <label>Design <input type="range" min="0" max="10" value="5" data-axis="design"><span>5</span></label>
- <label>Color <input type="range" min="0" max="10" value="5" data-axis="color"><span>5</span></label>
- <label>Style <input type="range" min="0" max="10" value="5" data-axis="style"><span>5</span></label>
- </div>
- <div class="decision">
- <button class="btn-keep" type="button">Keep</button>
- <button class="btn-reject" type="button">Reject</button>
- <button class="btn-pair" type="button" title="Suggest combinations">Pair</button>
- </div>
- <div class="why-chip" title="Click to chat about this verdict"></div>
- <div class="chips"></div>
- </div>
- </div>
+ <!-- 2026-05-20 Steve directive — stripped admin review-flyout (sliders +
+ Keep/Reject/Pair buttons + why-chip + chips). Admin cards now match
+ the public-card UX: hover reveals just View + Sample. Title/category
+ meta also dropped from the admin card since the hover overlay already
+ shows the DIG and the title is in the modal on click. -->
</div>`;
}).join('');
@@ -4088,14 +4072,8 @@ ${(req.query.source === 'all') ? `
</optgroup>
</select>
</div>
- <label class="density-label" style="display:inline-flex;align-items:center;gap:8px;font:11px var(--sans);text-transform:uppercase;letter-spacing:.1em;color:var(--ink-faint)">Density
- <input type="range" id="density-slider" min="1" max="12" step="1" value="6" class="density-range" style="flex:0 0 140px" aria-valuemin="1" aria-valuemax="12" aria-valuenow="6">
- <span id="density-value" aria-live="polite" style="font:600 11px var(--sans);color:var(--ink-soft);min-width:18px;text-align:right;font-variant-numeric:tabular-nums">6</span>
- </label>${_isAdmin ? `
- <label class="spacing-label" style="display:inline-flex;align-items:center;gap:8px;font:11px var(--sans);text-transform:uppercase;letter-spacing:.1em;color:var(--ink-faint)" title="Admin-only: visual gap between design cards">Spacing
- <input type="range" id="spacing-slider" min="0" max="40" step="2" value="2" class="density-range" style="flex:0 0 140px" aria-valuemin="0" aria-valuemax="40" aria-valuenow="2">
- <span id="spacing-value" aria-live="polite" style="font:600 11px var(--sans);color:var(--ink-soft);min-width:22px;text-align:right;font-variant-numeric:tabular-nums">2px</span>
- </label>` : ''}
+ <!-- 2026-05-20 Steve directive — density + spacing sliders stripped. -->
+ ${''}
<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>
@@ -4104,30 +4082,8 @@ ${(req.query.source === 'all') ? `
<input type="hidden" name="hue" id="hue-hidden" value="${hueQ}">
</form>
- <!-- ─── Color + Style sliders (client-side filter on data-hue / data-style-rank) ─── -->
- <div id="scrub-sliders" style="margin:14px 0 6px;background:var(--card-bg,#faf8f3);border:1px solid var(--line,#ebe5d6);border-radius:10px;padding:14px 18px;display:grid;gap:12px">
- <div class="scrub-row" style="display:grid;grid-template-columns:90px 1fr 80px;gap:14px;align-items:center">
- <label style="font:11px var(--sans);text-transform:uppercase;letter-spacing:.10em;color:var(--ink-faint)">Color</label>
- <div style="position:relative">
- <input type="range" id="scrub-hue" min="0" max="360" step="1" value="180"
- style="width:100%;height:24px;-webkit-appearance:none;appearance:none;background:linear-gradient(90deg,#ff0000 0%,#ffff00 16%,#00ff00 33%,#00ffff 50%,#0000ff 66%,#ff00ff 83%,#ff0000 100%);border-radius:12px;outline:none;cursor:pointer">
- <span id="scrub-hue-label" style="position:absolute;right:0;top:-18px;font:10px var(--sans);color:var(--ink-faint)">all colors</span>
- </div>
- <button type="button" id="scrub-hue-clear" style="font:11px var(--sans);padding:4px 10px;border:1px solid var(--line);background:transparent;color:var(--ink-soft);border-radius:6px;cursor:pointer">Clear</button>
- </div>
- <div class="scrub-row" style="display:grid;grid-template-columns:90px 1fr 80px;gap:14px;align-items:center">
- <label style="font:11px var(--sans);text-transform:uppercase;letter-spacing:.10em;color:var(--ink-faint)">Style</label>
- <div style="position:relative">
- <input type="range" id="scrub-style" min="0" max="100" step="1" value="50"
- style="width:100%;height:8px;-webkit-appearance:none;appearance:none;background:linear-gradient(90deg,#7a5a3a 0%,#a08070 25%,#b8a890 50%,#9aa8b0 75%,#1a1816 100%);border-radius:6px;outline:none;cursor:pointer">
- <span style="position:absolute;left:0;top:14px;font:10px var(--sans);color:var(--ink-faint)">Traditional</span>
- <span style="position:absolute;right:0;top:14px;font:10px var(--sans);color:var(--ink-faint)">Modern</span>
- <span id="scrub-style-label" style="position:absolute;left:50%;transform:translateX(-50%);top:-18px;font:10px var(--sans);color:var(--ink-faint)">all styles</span>
- </div>
- <button type="button" id="scrub-style-clear" style="font:11px var(--sans);padding:4px 10px;border:1px solid var(--line);background:transparent;color:var(--ink-soft);border-radius:6px;cursor:pointer">Clear</button>
- </div>
- <div id="scrub-count" style="font:11px var(--sans);color:var(--ink-faint);text-align:right">Showing all designs</div>
- </div>
+ <!-- 2026-05-20 Steve directive — Color + Style scrub sliders stripped. -->
+ ${''}
<!-- Color-wheel hue swatches -->
<div class="hue-swatches" style="display:flex;flex-wrap:wrap;gap:6px;margin:18px 0 4px;align-items:center">
@@ -4817,6 +4773,9 @@ ${FOOTER}
var slider = document.getElementById('density-slider');
var grid = document.getElementById('catalog-grid');
var label = document.getElementById('density-value');
+ // 2026-05-20 Steve directive — density slider removed. Bail if the element
+ // is missing so the rest of the page JS isn't blocked by the TypeError.
+ if (!slider || !grid) return;
var saved = localStorage.getItem('wallco-density');
if (saved) slider.value = saved;
// Clamp inline --cols for narrow viewports so a desktop "density=8" pref
@@ -5435,18 +5394,24 @@ ${_isAdmin ? `
function wireCard(card) {
const id = card.dataset.id;
+ // 2026-05-20 Steve directive — admin review-flyout stripped (sliders +
+ // Keep/Reject/Pair). All wireCard work is now no-ops when those elements
+ // don't exist. Each querySelector below is null-guarded so the bulk-select
+ // + favorites code paths still run on the slimmed-down admin card.
// sliders
card.querySelectorAll('.sliders input[type=range]').forEach(slider => {
slider.addEventListener('input', () => { slider.nextElementSibling.textContent = slider.value; });
slider.addEventListener('change', () => saveReview(card));
});
// K/R
- card.querySelector('.btn-keep').addEventListener('click', e => {
+ const _keepBtn = card.querySelector('.btn-keep');
+ if (_keepBtn) _keepBtn.addEventListener('click', e => {
e.preventDefault(); e.stopPropagation();
card.dataset.decision = card.dataset.decision === 'keep' ? '' : 'keep';
saveReview(card);
});
- card.querySelector('.btn-reject').addEventListener('click', e => {
+ const _rejectBtn = card.querySelector('.btn-reject');
+ if (_rejectBtn) _rejectBtn.addEventListener('click', e => {
e.preventDefault(); e.stopPropagation();
card.dataset.decision = card.dataset.decision === 'reject' ? '' : 'reject';
saveReview(card);
@@ -5458,10 +5423,10 @@ ${_isAdmin ? `
openPairings(id, card.querySelector('.card-title').textContent || '#'+id);
});
// why-chip → chat
- card.querySelector('.why-chip').addEventListener('click', () => {
- const why = card.querySelector('.why-chip');
- if (!why.classList.contains('visible')) return;
- openChat(id, '_verdict', '"' + why.textContent + '"');
+ const _whyChip = card.querySelector('.why-chip');
+ if (_whyChip) _whyChip.addEventListener('click', () => {
+ if (!_whyChip.classList.contains('visible')) return;
+ openChat(id, '_verdict', '"' + _whyChip.textContent + '"');
});
}
@@ -5501,7 +5466,9 @@ ${_isAdmin ? `
cards.forEach(card => {
const id = card.dataset.id;
const chipsHTML = (CHIPS[id]||[]).map(c => '<span class="chip" data-chip="' + c.key + '">' + c.label + '</span>').join('');
- card.querySelector('.chips').innerHTML = chipsHTML;
+ const _chips = card.querySelector('.chips');
+ if (!_chips) return; // 2026-05-20 admin review-flyout (with .chips) stripped — no-op now
+ _chips.innerHTML = chipsHTML;
card.querySelectorAll('.chip').forEach(chip => {
chip.addEventListener('click', () => openChat(id, chip.dataset.chip, chip.textContent));
});
← bb09c26 deploy-kamatera.sh: probe https://wallco.ai/health (CF) inst
·
back to Wallco Ai
·
chat: kill-switch on mobile (≤768px) — bail before DOM injec cd80337 →