[object Object]

← back to Wallco Ai

design-edit: surface preset colorways (method 3) on the recolor-wheel block — all 3 replace-color methods on one admin surface

2d7a39ee568c1bfd0d1cae2dd67127235c20bde6 · 2026-06-12 07:47:55 -0700 · Steve Abrams

Files touched

Diff

commit 2d7a39ee568c1bfd0d1cae2dd67127235c20bde6
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Fri Jun 12 07:47:55 2026 -0700

    design-edit: surface preset colorways (method 3) on the recolor-wheel block — all 3 replace-color methods on one admin surface
---
 server.js | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 61 insertions(+), 1 deletion(-)

diff --git a/server.js b/server.js
index 7431856..b1ed008 100644
--- a/server.js
+++ b/server.js
@@ -16014,7 +16014,23 @@ ${(() => {
             <div style="display:flex;align-items:center;gap:8px;margin-bottom:8px">
               <button id="recolor-all" style="flex:1;padding:6px 14px;background:linear-gradient(90deg,#FF4F00,#107A3B,#FF0000,#A98B5B,#006837,#D70000,#1B5E20,#F4D24F);color:#fff;border:0;border-radius:6px;cursor:pointer;font:600 11px var(--sans);text-transform:uppercase;letter-spacing:.04em">✦ Spin all hexes (parallel)</button>
             </div>
-            <div id="recolor-status" style="font:12px var(--sans);color:var(--ink-soft,#555);min-height:18px"></div>
+
+            <!-- METHOD 3: PRESET COLORWAYS (Steve 2026-06-12 "replace colors → new
+                 SKU, all 3 methods on ONE surface"). Each chip applies a curated
+                 (ground,figure) preset by remapping THIS design's two dominant
+                 tones — free PIL recolor, no Gemini. Produces ONE is_published
+                 =FALSE child SKU via /api/colorways/preset (same child pattern as
+                 the swatch wheel + AI recolor). Chips are built from
+                 /api/colorways/presets (the COLORWAY_PAIRS list — one source). -->
+            <div style="margin-top:14px;padding-top:12px;border-top:1px dashed var(--line)">
+              <label style="font:11px var(--sans);text-transform:uppercase;letter-spacing:.08em;color:var(--ink-faint);display:block;margin-bottom:6px">Preset colorways · free PIL recolor</label>
+              <p style="font:11px var(--sans);color:var(--ink-faint);margin:0 0 8px">Click a preset to remap this design's two dominant tones into a curated colorway. Lands as an unpublished child SKU — publish it from the colorways list. For exact per-swatch control use the <strong>Color</strong> tab's dot wheel.</p>
+              <div id="preset-chips" style="display:flex;flex-wrap:wrap;gap:6px">
+                <span style="font:11px var(--sans);color:var(--ink-faint)">Loading presets…</span>
+              </div>
+            </div>
+
+            <div id="recolor-status" style="font:12px var(--sans);color:var(--ink-soft,#555);min-height:18px;margin-top:10px"></div>
             <div id="recolor-results" style="display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:8px;margin-top:10px"></div>
           </div>
           <script>
@@ -16089,6 +16105,50 @@ ${(() => {
               }));
               allBtn.disabled = false; allBtn.style.opacity = '1';
             });
+
+            // ── METHOD 3 wiring: preset colorways ──────────────────────────
+            const presetWrap = document.getElementById('preset-chips');
+            const firePreset = async (slug, chip) => {
+              if (chip) { chip.disabled = true; chip.style.opacity = '0.5'; }
+              const startTxt = '→ preset ' + slug + '…';
+              stat.textContent = (stat.textContent ? stat.textContent + '  ' : '') + startTxt;
+              try {
+                const r = await fetch('/api/colorways/preset', {
+                  method: 'POST', headers: { 'Content-Type': 'application/json' },
+                  body: JSON.stringify({ design_id: ${design.id}, slug }),
+                });
+                const j = await r.json();
+                if (!j.ok) throw new Error(j.error || 'failed');
+                const card = document.createElement('div');
+                card.innerHTML = '<a href="/design/' + j.new_design_id + '" target="_blank" rel="noopener noreferrer" style="display:block;text-decoration:none;color:inherit">'
+                  + '<img src="/designs/img/by-id/' + j.new_design_id + '" style="width:100%;aspect-ratio:1;object-fit:cover;border-radius:6px;border:1px solid var(--line)">'
+                  + '<div style="font:10px var(--sans);text-align:center;margin-top:4px;color:var(--ink-soft,#555)">' + slug + ' · #' + j.new_design_id + ' (unpublished)</div>'
+                  + '</a>';
+                results.appendChild(card);
+                stat.textContent = stat.textContent.replace(startTxt, '✓ preset ' + slug);
+              } catch (e) {
+                stat.textContent = stat.textContent.replace(startTxt, '✗ preset ' + slug + ':' + e.message);
+              } finally {
+                if (chip) { chip.disabled = false; chip.style.opacity = '1'; }
+              }
+            };
+            if (presetWrap) {
+              fetch('/api/colorways/presets').then(r => r.json()).then(j => {
+                if (!j || !j.ok || !Array.isArray(j.presets)) { presetWrap.innerHTML = '<span style="font:11px var(--sans);color:var(--ink-faint)">presets unavailable</span>'; return; }
+                presetWrap.innerHTML = '';
+                j.presets.forEach(p => {
+                  const chip = document.createElement('button');
+                  chip.type = 'button';
+                  chip.title = p.slug + ' — ground ' + p.ground + ' / figure ' + p.figure;
+                  chip.style.cssText = 'display:inline-flex;align-items:center;gap:6px;padding:5px 9px;border:1px solid var(--line);border-radius:14px;background:#fff;cursor:pointer;font:11px var(--sans);color:var(--ink)';
+                  chip.innerHTML = '<span style="display:inline-block;width:12px;height:12px;border-radius:50%;background:' + p.ground + ';border:1px solid rgba(0,0,0,.15)"></span>'
+                    + '<span style="display:inline-block;width:12px;height:12px;border-radius:50%;background:' + p.figure + ';border:1px solid rgba(0,0,0,.15)"></span>'
+                    + p.slug;
+                  chip.addEventListener('click', () => firePreset(p.slug, chip));
+                  presetWrap.appendChild(chip);
+                });
+              }).catch(() => { presetWrap.innerHTML = '<span style="font:11px var(--sans);color:var(--ink-faint)">presets failed to load</span>'; });
+            }
           })();
           </script>
           ` : ''}

← 62ff6da colorways: add preset-colorway endpoint (method 3) + LIMIT 1  ·  back to Wallco Ai  ·  TODO: log replace-colors-3-methods work (AI-recolor gate fix a0bab98 →