[object Object]

← back to Wallco Ai

YOLO R6 — text search box on /moodboard. Matches case-insensitive against pin labels + reasons + parent design title. Empty sections auto-collapse, empty-state hint appears on zero matches, query persists across reloads via localStorage. Stacks with the existing type filter (?type=design|color|material).

92ab522e594ec73c45184c5d2e0a527390345ec8 · 2026-05-12 00:58:16 -0700 · Steve Abrams

Files touched

Diff

commit 92ab522e594ec73c45184c5d2e0a527390345ec8
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue May 12 00:58:16 2026 -0700

    YOLO R6 — text search box on /moodboard. Matches case-insensitive against pin labels + reasons + parent design title. Empty sections auto-collapse, empty-state hint appears on zero matches, query persists across reloads via localStorage. Stacks with the existing type filter (?type=design|color|material).
---
 YOLO_BACKLOG.md |  5 +++--
 server.js       | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 62 insertions(+), 2 deletions(-)

diff --git a/YOLO_BACKLOG.md b/YOLO_BACKLOG.md
index 4cfd578..7df4232 100644
--- a/YOLO_BACKLOG.md
+++ b/YOLO_BACKLOG.md
@@ -34,7 +34,7 @@ User ask verbatim: "show final patterns and 1 room setting on https://wallco.ai/
 
 ### Visual polish
 
-- [ ] **R6 · /moodboard search box** — currently filters by type only (designs/colors/materials). Add a text search that matches against pin labels + reasons. Persist across reloads via localStorage.
+- [x] **R6 · /moodboard search box** — 2026-05-12 01:10 · added `#mb-search` input to the filters bar (pre-pills) with placeholder "search labels + reasons…". Pure client-side filter walks `.mb-card` nodes, matches case-insensitive against `.mb-title` + `.mb-reason` + parent design title. Sections with zero matches auto-collapse via `.mb-hidden`. Empty-state ("No pins match X") appears when query yields zero results. Persists last query in localStorage (key `wallco-mb-search`) and re-applies on load. Stacks with the existing type filter (?type=design|color|material).
 - [x] **R7 · why-chip prominence** — 2026-05-12 01:06 · why-chip restyled per debate-team verdict (curator's reasoning, not noise). Bigger (padding 6→8/9→12, font 11→12px+500 weight), full gold border (was 1px #3a3631), gold accent left-bar 3px, subtle gold-tinted bg. Floating "CURATOR" label tag above the chip identifies provenance. Hover state: bg deepens + "tap to open chat →" hint floats bottom-right. Verified live.
 - [ ] **R8 · `/age-themes` mobile layout** — at <640px viewport, the 3-column current/best/rationale grid stacks awkwardly. Test at 390×844, fix grid-template-columns + font-size scaling.
 - [x] **R9 · AI-language scrub** — 2026-05-12 00:42 · 9 public-facing leaks scrubbed in server.js: schema.org names + meta descriptions on /, /designs, /design/:id, /murals, /samples + shopify tagList. Replaced with "wallco.ai original" / "hand-curated through our proprietary design system" / "never repeated". Admin /age-themes banner kept "AI-generated starting point" per validation-loop honesty rule (admin-only audience). Front-page + /designs meta verified clean live.
@@ -73,4 +73,5 @@ TICK · TIMESTAMP · ITEM · STATUS · COMMIT
 - T11 · 2026-05-12 00:52 · R4 isolate W/P/C shortcuts · done · scoped keydown handler, toast feedback, clipboard for primary hex · b204703
 - T12 · 2026-05-12 00:58 · R2 ΔE closeness bands · done · 4 bands · 4 surfaces in API · chips on both wallco + tsd · 12 new unit tests · 8c653d7 + ef39b82 + 035592b
 - T13 · 2026-05-12 01:02 · R5 source-design SW row · done · new "Design's dominant color" block above palette · stored hex vs extracted distinction explicit · c93962a
-- T14 · 2026-05-12 01:06 · R7 why-chip prominence · done · bigger font, gold left-accent, CURATOR provenance label, hover hint
+- T14 · 2026-05-12 01:06 · R7 why-chip prominence · done · bigger font, gold left-accent, CURATOR provenance label, hover hint · f5c8666
+- T15 · 2026-05-12 01:10 · R6 moodboard search · done · pure client-side filter, localStorage persist, empty-state UX, stacks with type filter
diff --git a/server.js b/server.js
index 1e9332e..94f644a 100644
--- a/server.js
+++ b/server.js
@@ -4041,6 +4041,11 @@ app.get('/moodboard', (req, res) => {
   header.top .filters { margin-left:auto; display:flex; gap:8px; }
   header.top .filter-pill { background:#2a2825; padding:3px 10px; border-radius:12px; font-size:11px; cursor:pointer; color:#bba; border:1px solid transparent; text-decoration:none; }
   header.top .filter-pill.on { background:#d2b15c; color:#0f0e0c; }
+  #mb-search { background:#16140f; border:1px solid #3a3631; border-radius:14px; padding:3px 12px; font:11px/1 -apple-system, system-ui, sans-serif; color:#e8e2d6; min-width:200px; outline:none; transition:border-color .12s; }
+  #mb-search:focus { border-color:#d2b15c; }
+  #mb-search::placeholder { color:#666; }
+  .mb-card.mb-hidden, .mb-design-block.mb-hidden { display:none !important; }
+  .mb-search-empty { padding:32px 24px; text-align:center; color:#666; font-style:italic; font-size:13px; }
   header.top .export-link { font-size:11px; color:#d2b15c; text-decoration:none; padding:3px 10px; border:1px solid #3a3631; border-radius:2px; }
   header.top .export-link:hover { background:#d2b15c; color:#0f0e0c; }
   main { padding: 24px 32px; max-width:1400px; margin:0 auto; }
@@ -4076,6 +4081,7 @@ app.get('/moodboard', (req, res) => {
     <a href="/moodboard${filter?'?type='+filter:''}" class="on">Moodboard</a>
   </nav>
   <div class="filters">
+    <input type="search" id="mb-search" placeholder="search labels + reasons…" autocomplete="off" />
     <a class="filter-pill ${!filter?'on':''}" href="/moodboard">All</a>
     <a class="filter-pill ${filter==='design'?'on':''}" href="/moodboard?type=design">Designs</a>
     <a class="filter-pill ${filter==='color'?'on':''}" href="/moodboard?type=color">Colors</a>
@@ -4114,6 +4120,59 @@ document.querySelectorAll('.mb-unpin').forEach(btn => {
     } catch (e) { btn.disabled = false; }
   });
 });
+
+// R6 — text search across pin labels + reasons. Persists across reloads via
+// localStorage. Pure client-side filter on rendered DOM (no API change needed).
+(function(){
+  var input = document.getElementById('mb-search');
+  if (!input) return;
+  var LS_KEY = 'wallco-mb-search';
+  try { input.value = localStorage.getItem(LS_KEY) || ''; } catch(e) {}
+
+  function applyFilter() {
+    var q = (input.value || '').trim().toLowerCase();
+    try { localStorage.setItem(LS_KEY, q); } catch(e) {}
+    var anyShown = false;
+    document.querySelectorAll('.mb-design-block').forEach(function(block){
+      var blockShown = false;
+      block.querySelectorAll('.mb-card').forEach(function(card){
+        if (!q) { card.classList.remove('mb-hidden'); blockShown = true; return; }
+        var label = (card.querySelector('.mb-title') || {}).textContent || '';
+        var reason = (card.querySelector('.mb-reason') || {}).textContent || '';
+        var match = (label + ' ' + reason).toLowerCase().indexOf(q) !== -1;
+        card.classList.toggle('mb-hidden', !match);
+        if (match) blockShown = true;
+      });
+      // also match against design title in the block header
+      if (q && !blockShown) {
+        var dt = (block.querySelector('.mb-design-title') || {}).textContent || '';
+        if (dt.toLowerCase().indexOf(q) !== -1) {
+          block.querySelectorAll('.mb-card').forEach(function(c){ c.classList.remove('mb-hidden'); });
+          blockShown = true;
+        }
+      }
+      block.classList.toggle('mb-hidden', !blockShown);
+      if (blockShown) anyShown = true;
+    });
+    // empty-state hint
+    var main = document.querySelector('main');
+    var empty = document.getElementById('mb-search-empty');
+    if (q && !anyShown) {
+      if (!empty) {
+        empty = document.createElement('div');
+        empty.id = 'mb-search-empty';
+        empty.className = 'mb-search-empty';
+        main.appendChild(empty);
+      }
+      empty.textContent = 'No pins match "' + q + '"';
+    } else if (empty) {
+      empty.remove();
+    }
+  }
+  input.addEventListener('input', applyFilter);
+  // apply persisted search on load
+  if (input.value) applyFilter();
+})();
 </script>
 </body></html>`);
 });

← 3b36790 YOLO R7 — promote why-chip from subtle pill to prominent cur  ·  back to Wallco Ai  ·  YOLO R8 — /age-themes mobile layout. Added @media (max-width 1cd8af9 →