[object Object]

← back to CelebritySignatures

wear: auto-detect campaign-<slug>.jpg for the 'worn' shot instead of a hardcoded map

d8381ce41a569442ba5f99a52b16cac8586bfd33 · 2026-09-09 16:50:29 -0700 · Steve

Per peer coordination (celebritysignatures-e9 owns generation + naming): derive
slug candidates from the signer's name (surname / joined last-two / full / first,
+ overrides for vangogh & frida) and probe which campaign-<slug>.jpg exists, so
every shot the pipeline generates (Shakespeare, da Vinci, Goethe, … the top 50)
surfaces on its product page automatically with no code change. Fixes Lincoln to
use campaign-lincoln.jpg (was pointing at -seated). Verified Darwin/Lincoln worn,
Shakespeare no-shot, 0 errors. Local only, not deployed. Reverse: git revert HEAD.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit d8381ce41a569442ba5f99a52b16cac8586bfd33
Author: Steve <steve@designerwallcoverings.com>
Date:   Wed Sep 9 16:50:29 2026 -0700

    wear: auto-detect campaign-<slug>.jpg for the 'worn' shot instead of a hardcoded map
    
    Per peer coordination (celebritysignatures-e9 owns generation + naming): derive
    slug candidates from the signer's name (surname / joined last-two / full / first,
    + overrides for vangogh & frida) and probe which campaign-<slug>.jpg exists, so
    every shot the pipeline generates (Shakespeare, da Vinci, Goethe, … the top 50)
    surfaces on its product page automatically with no code change. Fixes Lincoln to
    use campaign-lincoln.jpg (was pointing at -seated). Verified Darwin/Lincoln worn,
    Shakespeare no-shot, 0 errors. Local only, not deployed. Reverse: git revert HEAD.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 public/wear.html | 55 +++++++++++++++++++++++++++++++++++++------------------
 1 file changed, 37 insertions(+), 18 deletions(-)

diff --git a/public/wear.html b/public/wear.html
index 8ee1810..848d8b2 100644
--- a/public/wear.html
+++ b/public/wear.html
@@ -223,17 +223,27 @@ const INK_PRESETS = [
   { id:'white',    label:'White',    hex:'#ffffff' },
 ];
 // Editorial "worn" shots — the celebrity seated in the full Signature Edit
-// (AI-generated). Only some signers have one; matched by name. Non-wear-eligible
-// names (Einstein etc.) never reach /wear, so listing them is harmless.
-const LIFESTYLE = [
-  ['charles darwin','darwin'], ['benjamin franklin','franklin'], ['abraham lincoln','lincoln-seated'],
-  ['napoleon','napoleon'], ['michelangelo','michelangelo'], ['van gogh','vangogh'],
-  ['albert einstein','einstein'], ['frida','frida'], ['pablo picasso','picasso'], ['mark twain','twain'],
-];
-function lifestyleFor(sig){
-  const n = (sig && sig.full_name || '').toLowerCase();
-  const hit = LIFESTYLE.find(([m]) => n.includes(m));
-  return hit ? `/assets/campaign-${hit[1]}.jpg` : null;
+// (AI-generated, named /assets/campaign-<slug>.jpg by the campaign pipeline).
+// Rather than hardcode a name→file map (which wouldn't pick up newly generated
+// shots), AUTO-DETECT: derive candidate slugs from the person's name and probe
+// which campaign-<slug>.jpg actually exists. Handles the naming quirks — some
+// files are keyed on the surname (darwin, napoleon), some on a joined full
+// surname (vangogh), some on a first name (frida). A few explicit overrides
+// pin the non-obvious existing files.
+const LIFESTYLE_OVERRIDE = { 'vincent van gogh':'vangogh', 'frida kahlo':'frida' };
+function lifestyleCandidates(name){
+  const n = (name || '').toLowerCase().replace(/[^a-z ]/g,'').trim();
+  if (!n) return [];
+  const cands = [];
+  if (LIFESTYLE_OVERRIDE[n]) cands.push(LIFESTYLE_OVERRIDE[n]);
+  const parts = n.split(/\s+/).filter(Boolean);
+  if (parts.length){
+    cands.push(parts[parts.length-1]);            // surname (darwin, lincoln, shakespeare)
+    if (parts.length >= 2) cands.push(parts.slice(-2).join(''));  // joined last two (vangogh)
+    cands.push(parts.join(''));                   // full name joined (leonardodavinci)
+    cands.push(parts[0]);                          // first name (frida)
+  }
+  return [...new Set(cands)].filter(Boolean);
 }
 // Toggle the modal's left visual between the editorial "worn" shot and the
 // interactive garment mockup.
@@ -247,14 +257,23 @@ function setLifeView(mode){
   document.getElementById('lifestyleToggle').textContent = worn ? 'See on garment' : 'See it worn';
 }
 function applyLifestyle(){
-  const url = lifestyleFor(state.sig);
   const shot = document.getElementById('lifestyleShot'), tog = document.getElementById('lifestyleToggle');
-  if (url){
-    shot.src = url; shot.alt = `${state.sig.full_name} in the Signature Edit`;
-    tog.hidden = false; setLifeView('worn');        // lead with the editorial shot
-  } else {
-    tog.hidden = true; shot.hidden = true; shot.removeAttribute('src'); setLifeView('garment');
-  }
+  // Default to the garment mockup; probe for a worn shot and switch in if found.
+  tog.hidden = true; shot.hidden = true; shot.removeAttribute('src'); setLifeView('garment');
+  const sig = state.sig;
+  const cands = lifestyleCandidates(sig && sig.full_name);
+  (function tryNext(i){
+    if (i >= cands.length || state.sig !== sig) return;
+    const url = `/assets/campaign-${cands[i]}.jpg`;
+    const probe = new Image();
+    probe.onload = () => {
+      if (state.sig !== sig) return;               // signer changed while probing
+      shot.src = url; shot.alt = `${sig.full_name} in the Signature Edit`;
+      tog.hidden = false; setLifeView('worn');      // lead with the editorial shot
+    };
+    probe.onerror = () => tryNext(i + 1);
+    probe.src = url;
+  })(0);
 }
 // Top-level HTML-attribute escape (the autocomplete's own `esc` is scoped to
 // that block, so the grid renderer needs its own).

← 523cb04 wear: 'See it worn' — feature each celebrity's seated editor  ·  back to CelebritySignatures  ·  wear: accent-fold worn-shot slug candidates (Velázquez→velaz 22b0eba →