[object Object]

← back to Wallco Ai

universal search: match designs by color name (blue/navy/teal/cream/sage…) derived from dominant_hex via lib/color _hueBucket+hueName — was title/category/motif only, so color queries returned 0 (blue 0→271, green 0→435, teal 0→253). Cached per hex; text searches unchanged

e25598a6b68ecf3253e80cec849f47cd3dfcc716 · 2026-06-03 14:34:35 -0700 · Steve Abrams

Files touched

Diff

commit e25598a6b68ecf3253e80cec849f47cd3dfcc716
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Jun 3 14:34:35 2026 -0700

    universal search: match designs by color name (blue/navy/teal/cream/sage…) derived from dominant_hex via lib/color _hueBucket+hueName — was title/category/motif only, so color queries returned 0 (blue 0→271, green 0→435, teal 0→253). Cached per hex; text searches unchanged
---
 server.js | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/server.js b/server.js
index 77e9c38..bf409a0 100644
--- a/server.js
+++ b/server.js
@@ -776,6 +776,46 @@ const UNIVERSAL_PAGES = [
 // and navigable pages (matched by title + keyword aliases). Pure read-only,
 // in-memory — no DB. Falls back to a did-you-mean over the motif/category
 // vocabulary when nothing matched, and to a browse payload when q is empty.
+// Map a design's dominant_hex → the color WORDS a shopper would type ("blue",
+// "navy", "cream"…), so the universal search can match by color, not just by
+// title/category/motif. Reuses lib/color helpers (pure math, no API). Cached by
+// hex since the same colors recur across the catalog. (2026-06-03 — closes the
+// "blue / teal / green → 0 results" search gap.)
+const _colorTokCache = new Map();
+function colorTokensForHex(hex) {
+  if (!hex) return [];
+  const key = String(hex).toLowerCase();
+  const hit = _colorTokCache.get(key);
+  if (hit) return hit;
+  const toks = new Set();
+  const bucket = _hueBucket(hex);                       // 'blue','green','neutral',…
+  const name = (hueName(hex) || '').toLowerCase();      // 'sapphire','sage',…
+  if (name) toks.add(name);
+  const hsl = _hexToHsl(hex) || { h: 0, s: 0, l: 50 };
+  if (bucket === 'neutral') {
+    toks.add('neutral');
+    if (hsl.l >= 78)      ['cream', 'ivory', 'white', 'beige', 'bone'].forEach(t => toks.add(t));
+    else if (hsl.l <= 22) ['charcoal', 'black'].forEach(t => toks.add(t));
+    else                  ['gray', 'grey', 'taupe', 'greige'].forEach(t => toks.add(t));
+  } else {
+    toks.add(bucket);
+    const SYN = {
+      red: ['red', 'crimson', 'scarlet'], orange: ['orange', 'terracotta', 'rust'],
+      yellow: ['yellow', 'gold', 'honey', 'amber'], chartreuse: ['chartreuse', 'lime', 'green'],
+      green: ['green', 'emerald', 'olive', 'sage'], teal: ['teal', 'turquoise', 'aqua'],
+      cyan: ['cyan', 'aqua', 'turquoise', 'blue'], blue: ['blue', 'navy', 'azure', 'cobalt'],
+      indigo: ['indigo', 'blue', 'violet'], violet: ['violet', 'purple', 'plum', 'mauve', 'lavender'],
+      magenta: ['magenta', 'pink', 'fuchsia', 'rose'],
+    };
+    (SYN[bucket] || []).forEach(t => toks.add(t));
+    if (hsl.l <= 28) toks.add('dark');
+    if (hsl.l >= 72) toks.add('light');
+  }
+  const arr = Array.from(toks);
+  _colorTokCache.set(key, arr);
+  return arr;
+}
+
 // Used by both GET /api/search (JSON) and GET /search (server-rendered page).
 function universalSearch(qRaw, limit) {
   qRaw = String(qRaw || '').toLowerCase().trim();
@@ -842,6 +882,7 @@ function universalSearch(qRaw, limit) {
       const category = (d.category || '').toLowerCase();
       const handle = (d.handle || '').toLowerCase();
       const motifs = (d.motifs || []).map(m => String(m).toLowerCase());
+      const ctoks = colorTokensForHex(d.dominant_hex);   // color WORDS for this design
       let score = 0;
       for (const t of terms) {
         if (motifs.some(m => m === t))            score += 5;
@@ -850,6 +891,7 @@ function universalSearch(qRaw, limit) {
         if (category === t)       score += 2;
         else if (category.includes(t)) score += 1;
         if (handle.includes(t))   score += 1;
+        if (ctoks.includes(t))    score += 2;   // color-name match (blue/navy/cream…)
       }
       if (score > 0) {
         designs.push({

← 4cb93d5 admin: /admin/mural-masters local review gallery for room-si  ·  back to Wallco Ai  ·  YOLO ledger: 2026-06-03 EVE run — DTD verdict B + T1 color-n 3c40e9c →