[object Object]

← back to Dw Pairs Well

Design Coordinate v7: 6+6 cards, rename AI→Auto Generated (no AI wording), compact CTA/headings; fix /api/ai-coordinates to honor ?limit

2d70921d77d3ad3ebe9940b823fadc2ad3c966af · 2026-06-26 10:31:57 -0700 · Steve

Files touched

Diff

commit 2d70921d77d3ad3ebe9940b823fadc2ad3c966af
Author: Steve <steve@designerwallcoverings.com>
Date:   Fri Jun 26 10:31:57 2026 -0700

    Design Coordinate v7: 6+6 cards, rename AI→Auto Generated (no AI wording), compact CTA/headings; fix /api/ai-coordinates to honor ?limit
---
 deploy/shopify/design-coordinate.liquid | 45 +++++++++++++++++----------------
 public/preview.html                     | 32 +++++++++++------------
 server.js                               |  6 ++++-
 3 files changed, 44 insertions(+), 39 deletions(-)

diff --git a/deploy/shopify/design-coordinate.liquid b/deploy/shopify/design-coordinate.liquid
index e6f1e6d..91eec81 100644
--- a/deploy/shopify/design-coordinate.liquid
+++ b/deploy/shopify/design-coordinate.liquid
@@ -1,10 +1,11 @@
 {%- comment -%}
-    Design Coordinate v6 — Steve's spec:
-      • Catalog grid: 24 stripe/plaid/check coordinates that use the source palette
-      • Density slider 3-12 cols (default 8), localStorage-persisted
-      • AI Generated Coordinates: 24 SVG-poster placeholders seeded from palette
+    Design Coordinate v7 — Steve's spec:
+      • Catalog grid: 6 stripe/plaid/check coordinates that use the source palette ("active patterns")
+      • Density slider 3-12 cols (default 6), localStorage-persisted
+      • Auto Generated: 6 SVG-poster placeholders seeded from palette (no "AI" wording, customer-facing)
       • Vendor picker: SW / DE / BM / F&B (default on) + Behr / PPG / RAL (opt-in)
       • Each card shows palette dots + selected vendors' paint chips
+      • Compact: smaller CTA pill + headings (v7 "debig"); place AFTER the product image in the theme.
     Service: https://pairs.designerwallcoverings.com  (commit history at ~/Projects/dw-pairs-well)
     CSS namespace kept as .pairs-well__* — only the button TEXT is "Design Coordinate".
   {%- endcomment -%}
@@ -43,8 +44,8 @@
         <h3>Stripes &amp; Plaids from our catalog <span class="count" id="pairs-well-catalog-count"></span></h3>
         <div class="pairs-well__controls">
           <label for="pairs-well-cols-catalog">Columns</label>
-          <input id="pairs-well-cols-catalog" type="range" min="3" max="12" value="8" step="1">
-          <span class="pairs-well__cols-val" id="pairs-well-cols-catalog-val">8</span>
+          <input id="pairs-well-cols-catalog" type="range" min="3" max="12" value="6" step="1">
+          <span class="pairs-well__cols-val" id="pairs-well-cols-catalog-val">6</span>
         </div>
       </div>
       <div id="pairs-well-grid" class="pairs-well__grid"></div>
@@ -52,11 +53,11 @@
 
     <div class="pairs-well__ai" id="pairs-well-ai" hidden>
       <div class="pairs-well__panel-head">
-        <h3>AI Generated Coordinates <span class="ai-badge">Preview · 24 options</span></h3>
+        <h3>Auto Generated <span class="ai-badge">Preview · 6 options</span></h3>
         <div class="pairs-well__controls">
           <label for="pairs-well-cols-ai">Columns</label>
-          <input id="pairs-well-cols-ai" type="range" min="3" max="12" value="8" step="1">
-          <span class="pairs-well__cols-val" id="pairs-well-cols-ai-val">8</span>
+          <input id="pairs-well-cols-ai" type="range" min="3" max="12" value="6" step="1">
+          <span class="pairs-well__cols-val" id="pairs-well-cols-ai-val">6</span>
         </div>
       </div>
       <div id="pairs-well-ai-grid" class="pairs-well__grid pairs-well__grid--ai"></div>
@@ -64,13 +65,13 @@
   </section>
 
   <style>
-    #pairs-well-with { font-family: inherit; --cols-catalog: 8; --cols-ai: 8; }
+    #pairs-well-with { font-family: inherit; --cols-catalog: 6; --cols-ai: 6; }
     .pairs-well__cta {
-      display: flex; align-items: center; justify-content: space-between;
-      width: 100%; padding: 18px 24px; margin: 0;
+      display: inline-flex; align-items: center; justify-content: center; gap: 8px;
+      width: auto; max-width: 100%; padding: 10px 18px; margin: 0;
       background: #111; color: #fff;
       border: 0; border-radius: 999px;
-      font-size: 15px; font-weight: 500; letter-spacing: 0.06em;
+      font-size: 12px; font-weight: 500; letter-spacing: 0.06em;
       text-transform: uppercase; cursor: pointer;
       transition: background 160ms ease, transform 120ms ease;
     }
@@ -102,9 +103,9 @@
 
     .pairs-well__panel-head {
       display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap;
-      gap: 12px; margin: 24px 0 12px;
+      gap: 12px; margin: 16px 0 10px;
     }
-    .pairs-well__panel-head h3 { margin: 0; font: 500 18px/1.2 ui-serif, Georgia, serif; }
+    .pairs-well__panel-head h3 { margin: 0; font: 500 15px/1.2 ui-serif, Georgia, serif; }
     .pairs-well__panel-head .count { color: #888; font-size: 12px; margin-left: 6px; font-family: -apple-system, sans-serif; font-weight: 400; }
     .pairs-well__panel-head .ai-badge {
       display: inline-block; padding: 3px 8px; background: #2a2a2a; color: #fff;
@@ -263,7 +264,7 @@
 
       function renderCard(p, isAi) {
         var img = p.image_url ? esc(p.image_url) : '';
-        var why = (p.why && p.why[0]) ? esc(p.why[0]) : (p.motif ? 'AI ' + esc(p.motif) : '');
+        var why = (p.why && p.why[0]) ? esc(p.why[0]) : (p.motif ? esc(p.motif) : '');
         var palette = (p.palette || []).map(function (c) {
           var hex = typeof c === 'string' ? c : c.hex;
           var sw = c && c.paints && c.paints.sw, de = c && c.paints && c.paints.de;
@@ -288,7 +289,7 @@
           +   '<div class="pairs-well__img-wrap">' + (img ? '<img class="pairs-well__img" src="' + img + '" alt="' + esc(p.title) + '" loading="lazy">' : '') + '</div>'
           +   '<div class="pairs-well__meta">'
           +     '<p class="pairs-well__title">' + esc(p.title || '') + '</p>'
-          +     '<p class="pairs-well__vendor">' + esc(p.vendor || (isAi ? 'AI-Generated' : '')) + '</p>'
+          +     '<p class="pairs-well__vendor">' + esc(p.vendor || (isAi ? 'Auto Generated' : '')) + '</p>'
           +     (why ? '<span class="pairs-well__why">' + why + '</span>' : '')
           +     (palette ? '<div class="pairs-well__palette">' + palette + '</div>' : '')
           +     paintsBlock
@@ -318,12 +319,12 @@
       function load() {
         if (loaded) return Promise.resolve();
         loaded = true;
-        renderSkeletons(catalogGrid, 12);
-        renderSkeletons(aiGrid, 12);
+        renderSkeletons(catalogGrid, 6);
+        renderSkeletons(aiGrid, 6);
         catalogPanel.hidden = false;
         aiPanel.hidden = false;
 
-        var pairsPromise = fetch(buildUrl(endpointPairs, 24), { credentials: 'omit' })
+        var pairsPromise = fetch(buildUrl(endpointPairs, 6), { credentials: 'omit' })
           .then(function (r) { return r.ok ? r.json() : Promise.reject(new Error('http ' + r.status)); })
           .then(function (d) {
             if (!d || d.ok === false) throw new Error((d && d.error) || 'bad payload');
@@ -338,14 +339,14 @@
           })
           .catch(function () { catalogGrid.innerHTML = '<div class="pairs-well__empty">Could not load coordinates right now.</div>'; });
 
-        var aiPromise = fetch(buildUrl(endpointAi, 24), { credentials: 'omit' })
+        var aiPromise = fetch(buildUrl(endpointAi, 6), { credentials: 'omit' })
           .then(function (r) { return r.ok ? r.json() : Promise.reject(new Error('http ' + r.status)); })
           .then(function (d) {
             if (!d || d.ok === false) throw new Error('bad payload');
             cachedAi = d.items || [];
             renderGrid(aiGrid, cachedAi, true);
           })
-          .catch(function () { aiGrid.innerHTML = '<div class="pairs-well__empty">AI generator not ready.</div>'; });
+          .catch(function () { aiGrid.innerHTML = '<div class="pairs-well__empty">Auto Generated coordinates not ready.</div>'; });
 
         return Promise.all([pairsPromise, aiPromise]);
       }
diff --git a/public/preview.html b/public/preview.html
index 5b56ed0..98b9081 100644
--- a/public/preview.html
+++ b/public/preview.html
@@ -7,7 +7,7 @@
 <style>
   :root {
     --gutter: 40px; --text: #1a1a1a; --muted: #777; --line: #e7e7e7;
-    --cols: 8;    /* density slider sets this */
+    --cols: 6;    /* density slider sets this */
   }
   * { box-sizing: border-box; }
   body { margin: 0; color: var(--text); background: #fafafa;
@@ -65,7 +65,7 @@
     gap: 12px; margin-bottom: 14px;
   }
   .dc-section h2 {
-    margin: 0; font: 500 22px/1.2 ui-serif, Georgia, serif;
+    margin: 0; font: 500 16px/1.2 ui-serif, Georgia, serif;
   }
   .dc-section h2 .count { color: var(--muted); font-size: 13px; margin-left: 6px; }
   .dc-controls {
@@ -79,10 +79,10 @@
   }
 
   .pairs-well__cta {
-    display: flex; align-items: center; justify-content: space-between;
-    width: 100%; padding: 18px 24px; margin: 0;
+    display: inline-flex; align-items: center; justify-content: center; gap: 8px;
+    width: auto; max-width: 100%; padding: 10px 18px; margin: 0;
     background: #111; color: #fff; border: 0; border-radius: 999px;
-    font-size: 15px; font-weight: 500; letter-spacing: 0.06em;
+    font-size: 12px; font-weight: 500; letter-spacing: 0.06em;
     text-transform: uppercase; cursor: pointer;
     transition: background 160ms ease, transform 120ms ease;
   }
@@ -200,8 +200,8 @@
       <h2>Stripes &amp; Plaids from our catalog <span class="count" id="catalog-count"></span></h2>
       <div class="dc-controls">
         <label for="catalog-cols">Columns</label>
-        <input id="catalog-cols" type="range" min="3" max="12" value="8" step="1">
-        <span class="colcount" id="catalog-cols-val">8</span>
+        <input id="catalog-cols" type="range" min="3" max="12" value="6" step="1">
+        <span class="colcount" id="catalog-cols-val">6</span>
       </div>
     </div>
     <div id="catalog-grid" class="pairs-well__grid" hidden></div>
@@ -210,11 +210,11 @@
   <!-- AI-GENERATED COORDINATES -->
   <section id="ai-coords-section" class="dc-section ai-coords" hidden>
     <div class="dc-header">
-      <h2>AI Generated Coordinates <span class="ai-badge">Preview · 24 options</span></h2>
+      <h2>Auto Generated <span class="ai-badge">Preview · 6 options</span></h2>
       <div class="dc-controls">
         <label for="ai-cols">Columns</label>
-        <input id="ai-cols" type="range" min="3" max="12" value="8" step="1">
-        <span class="colcount" id="ai-cols-val">8</span>
+        <input id="ai-cols" type="range" min="3" max="12" value="6" step="1">
+        <span class="colcount" id="ai-cols-val">6</span>
       </div>
     </div>
     <div id="ai-grid" class="pairs-well__grid"></div>
@@ -323,7 +323,7 @@
   function renderCard(p, opts) {
     opts = opts || {};
     var img = p.image_url ? esc(p.image_url) : '';
-    var why = (p.why && p.why[0]) ? esc(p.why[0]) : (p.motif ? 'AI ' + esc(p.motif) : '');
+    var why = (p.why && p.why[0]) ? esc(p.why[0]) : (p.motif ? esc(p.motif) : '');
     var palette = (p.palette || []).map(function (c) {
       var hex = typeof c === 'string' ? c : c.hex;
       var sw = c && c.paints && c.paints.sw, de = c && c.paints && c.paints.de;
@@ -392,7 +392,7 @@
 
     var qs = buildQuery(key);
     try {
-      var r = await fetch(endpointPairs + '?' + qs + '&limit=24');
+      var r = await fetch(endpointPairs + '?' + qs + '&limit=6');
       if (!r.ok) throw new Error('http ' + r.status);
       var d = await r.json();
       if (d && d.ok === false) throw new Error(d.error || 'bad payload');
@@ -416,15 +416,15 @@
     }
 
     // Kick off AI generation in parallel — show its section as soon as it loads
-    showSkeletons(aiGrid, 24);
+    showSkeletons(aiGrid, 6);
     aiSection.hidden = false;
     try {
-      var ar = await fetch(endpointAi + '?' + qs + '&limit=24');
+      var ar = await fetch(endpointAi + '?' + qs + '&limit=6');
       if (!ar.ok) throw new Error('http ' + ar.status);
       var ad = await ar.json();
       if (ad && ad.ok === false) throw new Error(ad.error || 'bad payload');
-      if (ad && ad.items) renderGrid(aiGrid, ad.items, { aiBadge: 'AI-Generated' });
-      else aiGrid.innerHTML = '<div class="pairs-well__empty">AI generator not ready yet.</div>';
+      if (ad && ad.items) renderGrid(aiGrid, ad.items, { aiBadge: 'Auto Generated' });
+      else aiGrid.innerHTML = '<div class="pairs-well__empty">Auto Generated coordinates not ready yet.</div>';
     } catch (e) {
       console.error('[preview] ai fetch failed:', e, 'url=', endpointAi + '?' + qs);
       aiGrid.innerHTML = '<div class="pairs-well__empty">AI fetch failed: ' + esc(e && e.message ? e.message : 'unknown') + ' (key: ' + esc(key) + ')</div>';
diff --git a/server.js b/server.js
index af03800..268b113 100644
--- a/server.js
+++ b/server.js
@@ -946,6 +946,10 @@ app.get('/api/ai-coordinates', async (req, res) => {
       return res.status(400).json({ ok: false, error: 'pass dw_sku, handle, or palette' });
     }
     const tile_in = Math.max(2, Math.min(60, parseFloat(req.query.tile_in) || 24));
+    // Honor ?limit= the same way /api/pairs does (default 6, hard cap 24) so the
+    // Auto Generated grid renders exactly what the caller asks for — without this,
+    // generateCoordinates() always returned its full set and the client showed 24.
+    const requestedK = Math.max(1, Math.min(24, parseInt(req.query.limit, 10) || 6));
 
     let source, palette;
     if (paletteParam.length) {
@@ -960,7 +964,7 @@ app.get('/api/ai-coordinates', async (req, res) => {
     }
     if (!palette.length) return res.json({ ok: true, source: { dw_sku: source.dw_sku, handle: source.handle }, items: [] });
 
-    const items = generateCoordinates({ palette, tile_in });
+    const items = generateCoordinates({ palette, tile_in }).slice(0, requestedK);
 
     res.set('Cache-Control', 'public, max-age=300');
     res.json({

← adf33a8 auto-save: 2026-06-26T10:14:48 (5 files) — tools/pattern-id-  ·  back to Dw Pairs Well  ·  collection-grouped feed: emit color/style facets (only popul fc5135e →