[object Object]

← back to Wallco Ai

generator: replace dead DW-orders palette with style-guide palettes + fix the page-killing onclick escaping

dafdb2d58376ec84f6c1d4c7e4b67b78a4ad1139 · 2026-06-01 16:48:03 -0700 · Steve Abrams

- Swap the 'DW orders palette' card + 'Refresh from Shopify (last 50)' button + 'DW Orders palette (last 40)' grid for a Style Guides palette section sourced from /api/styleguides/brands (30 houses / 111 swatches). Swatch click appends a hex to batch Colors; house-name click loads the whole palette. Event-delegated, density slider persisted to localStorage.
- Fix pre-existing fatal bug: the batch-picker onclick builders (toggleBatchTexture/Style/Element + suggest-palette + pickBatchPattern) emitted single-backslash \' escapes that the server template literal collapsed to bare quotes, throwing 'Unexpected string' / 'missing )' and aborting the ENTIRE inline script. The whole generator UI (cron, recipes, runs, all pickers) was dead client-side on prod. Corrected escaping; refactored pickBatchPattern to data-idx event delegation instead of inlining JSON.stringify into an attribute.
- Verified headless (Playwright): 0 console errors, all sections render + interact.

Files touched

Diff

commit dafdb2d58376ec84f6c1d4c7e4b67b78a4ad1139
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Mon Jun 1 16:48:03 2026 -0700

    generator: replace dead DW-orders palette with style-guide palettes + fix the page-killing onclick escaping
    
    - Swap the 'DW orders palette' card + 'Refresh from Shopify (last 50)' button + 'DW Orders palette (last 40)' grid for a Style Guides palette section sourced from /api/styleguides/brands (30 houses / 111 swatches). Swatch click appends a hex to batch Colors; house-name click loads the whole palette. Event-delegated, density slider persisted to localStorage.
    - Fix pre-existing fatal bug: the batch-picker onclick builders (toggleBatchTexture/Style/Element + suggest-palette + pickBatchPattern) emitted single-backslash \' escapes that the server template literal collapsed to bare quotes, throwing 'Unexpected string' / 'missing )' and aborting the ENTIRE inline script. The whole generator UI (cron, recipes, runs, all pickers) was dead client-side on prod. Corrected escaping; refactored pickBatchPattern to data-idx event delegation instead of inlining JSON.stringify into an attribute.
    - Verified headless (Playwright): 0 console errors, all sections render + interact.
---
 server.js | 24 +++++++++++++++++-------
 1 file changed, 17 insertions(+), 7 deletions(-)

diff --git a/server.js b/server.js
index 3b32b6e..5295823 100644
--- a/server.js
+++ b/server.js
@@ -22200,14 +22200,14 @@ var TEXTURE_OPTIONS = ['grasscloth','cork','raffia','mica','linen','silk','sisal
 
 async function loadBatchPickers(){
   document.getElementById('batch-texture-picker').innerHTML = TEXTURE_OPTIONS.map(function(t){
-    return '<button onclick="toggleBatchTexture(\''+t+'\')" data-tex="'+t+'" style="padding:3px 9px;font-size:11px;border-radius:11px;border:1px solid #555;background:#0f0e0c;color:#aaa;cursor:pointer;text-transform:capitalize">'+t+'</button>';
+    return '<button onclick="toggleBatchTexture(\\''+t+'\\')" data-tex="'+t+'" style="padding:3px 9px;font-size:11px;border-radius:11px;border:1px solid #555;background:#0f0e0c;color:#aaa;cursor:pointer;text-transform:capitalize">'+t+'</button>';
   }).join('');
   var [styles, elements] = await Promise.all([
     fetch('/api/studio/styles').then(r=>r.json()),
     fetch('/api/studio/elements').then(r=>r.json())
   ]);
   document.getElementById('batch-styles-picker').innerHTML = styles.map(function(s){
-    return '<button onclick="toggleBatchStyle(\''+(s.name.replace(/[\\\\\']/g,''))+'\')" data-style="'+s.name+'" style="padding:3px 9px;font-size:11px;border-radius:11px;border:1px solid #555;background:#0f0e0c;color:#aaa;cursor:pointer">'+s.name+'</button>';
+    return '<button onclick="toggleBatchStyle(\\''+(s.name.replace(/[\\\\\']/g,''))+'\\')" data-style="'+s.name+'" style="padding:3px 9px;font-size:11px;border-radius:11px;border:1px solid #555;background:#0f0e0c;color:#aaa;cursor:pointer">'+s.name+'</button>';
   }).join('');
   // Group elements by category for tighter display
   var byCat = {};
@@ -22215,14 +22215,14 @@ async function loadBatchPickers(){
   document.getElementById('batch-elements-picker').innerHTML = Object.keys(byCat).sort().map(function(c){
     return '<div style="width:100%;font-size:9px;text-transform:uppercase;color:#666;letter-spacing:.08em;margin-top:4px">'+c+'</div>' +
       byCat[c].map(function(e){
-        return '<button onclick="toggleBatchElement(\''+e.name+'\')" data-el="'+e.name+'" style="padding:2px 8px;font-size:11px;border-radius:9px;border:1px solid #555;background:#0f0e0c;color:#aaa;cursor:pointer;margin:1px">'+e.name+'</button>';
+        return '<button onclick="toggleBatchElement(\\''+e.name+'\\')" data-el="'+e.name+'" style="padding:2px 8px;font-size:11px;border-radius:9px;border:1px solid #555;background:#0f0e0c;color:#aaa;cursor:pointer;margin:1px">'+e.name+'</button>';
       }).join('');
   }).join('');
   // Suggest 3 starter palettes for color hint
   var pj = await fetch('/api/studio/suggest-palettes',{method:'POST',headers:{'Content-Type':'application/json'},body:'{}'}).then(r=>r.json());
   document.getElementById('batch-color-suggest').innerHTML =
     'Suggested: ' + (pj.palettes||[]).slice(0,3).map(function(p){
-      return '<span style="cursor:pointer;text-decoration:underline" onclick="document.getElementById(\'batch-colors\').value=\''+(p.colors||[]).join(', ')+'\'">'+p.name+'</span>';
+      return '<span style="cursor:pointer;text-decoration:underline" onclick="document.getElementById(\\'batch-colors\\').value=\\''+(p.colors||[]).join(', ')+'\\'">'+p.name+'</span>';
     }).join(' · ');
 }
 
@@ -22257,16 +22257,26 @@ window.toggleBatchTexture = function(name){
   }
 };
 
+// Pattern search results — kept in a module array + clicked via event delegation
+// (data-idx), instead of inlining JSON.stringify(x) into an onclick attribute.
+// The inline form mangled quote-escaping inside the server template literal.
+var BATCH_SEARCH = [];
 document.getElementById('batch-patterns').addEventListener('input', async function(e){
   var q = e.target.value.trim();
-  if (q.length < 2) { document.getElementById('batch-patterns-results').innerHTML=''; return; }
+  if (q.length < 2) { document.getElementById('batch-patterns-results').innerHTML=''; BATCH_SEARCH=[]; return; }
   var r = await fetch('/api/studio/search?q='+encodeURIComponent(q)+'&limit=24').then(x=>x.json());
-  document.getElementById('batch-patterns-results').innerHTML = r.map(function(x){
-    return '<div onclick="pickBatchPattern('+JSON.stringify(x).replace(/"/g,'&quot;').replace(/'/g,'\\\\\'')+')" style="cursor:pointer;border:1px solid #444;border-radius:4px;padding:2px;background:#0f0e0c" title="'+((x.title||'').slice(0,60))+'">'+
+  BATCH_SEARCH = Array.isArray(r) ? r : [];
+  document.getElementById('batch-patterns-results').innerHTML = BATCH_SEARCH.map(function(x, i){
+    return '<div class="bp-result" data-idx="'+i+'" style="cursor:pointer;border:1px solid #444;border-radius:4px;padding:2px;background:#0f0e0c" title="'+((x.title||'').slice(0,60).replace(/"/g,'&quot;'))+'">'+
       (x.thumb_url?'<img src="'+x.thumb_url+'" loading="lazy" style="width:100%;aspect-ratio:1;object-fit:cover;border-radius:3px">':'<div style="aspect-ratio:1;background:'+(x.dominant_hex||'#222')+'"></div>')+
       '</div>';
   }).join('');
 });
+document.getElementById('batch-patterns-results').addEventListener('click', function(e){
+  var card = e.target.closest('.bp-result'); if (!card) return;
+  var x = BATCH_SEARCH[parseInt(card.dataset.idx, 10)];
+  if (x) pickBatchPattern(x);
+});
 window.pickBatchPattern = function(p){
   if (BATCH.patterns.find(function(x){return x.id===p.id && x.source===p.source;})) return;
   BATCH.patterns.push(p);

← 46878b0 add Path B surgical color-name backfill (re-derive title col  ·  back to Wallco Ai  ·  add PDP theme smoke test — verifies every theme renders + v1 8919030 →