← back to Designer Wallcoverings
contrarian fixes: (1) read color_details BEFORE legacy color_dots_json (no shadowing); (2) anchor marketed color to dominant swatch on near-solid products (Gold foil no longer 'Walnut'), canonical lexicon names
b21d7edb50aed94ffaced2fd85bb9d004391203e · 2026-07-07 10:15:25 -0700 · Steve
Files touched
M shopify/scripts/enrich-color-details-local.pyM shopify/theme-LIVE-591/snippets/color-palette.liquid
Diff
commit b21d7edb50aed94ffaced2fd85bb9d004391203e
Author: Steve <steve@designerwallcoverings.com>
Date: Tue Jul 7 10:15:25 2026 -0700
contrarian fixes: (1) read color_details BEFORE legacy color_dots_json (no shadowing); (2) anchor marketed color to dominant swatch on near-solid products (Gold foil no longer 'Walnut'), canonical lexicon names
---
shopify/scripts/enrich-color-details-local.py | 59 +++++++++++++++++++++-
.../theme-LIVE-591/snippets/color-palette.liquid | 8 +--
2 files changed, 62 insertions(+), 5 deletions(-)
diff --git a/shopify/scripts/enrich-color-details-local.py b/shopify/scripts/enrich-color-details-local.py
index 3569ca3b..62b8e8d8 100644
--- a/shopify/scripts/enrich-color-details-local.py
+++ b/shopify/scripts/enrich-color-details-local.py
@@ -44,11 +44,12 @@ def gql(query, variables=None):
def get_products(ids, query_str, limit):
+ cn = 'cn: metafield(namespace:"custom", key:"color_name"){ value }'
if ids:
gids = ','.join(f'"gid://shopify/Product/{i}"' for i in ids)
- q = f'{{ nodes(ids:[{gids}]){{ ... on Product {{ id title featuredImage{{url}} }} }} }}'
+ q = f'{{ nodes(ids:[{gids}]){{ ... on Product {{ id title featuredImage{{url}} {cn} }} }} }}'
return [n for n in gql(q)['data']['nodes'] if n and n.get('featuredImage')]
- q = 'query($q:String!,$n:Int!){ products(first:$n, query:$q){ edges{ node{ id title featuredImage{url} } } } }'
+ q = f'query($q:String!,$n:Int!){{ products(first:$n, query:$q){{ edges{{ node{{ id title featuredImage{{url}} {cn} }} }} }} }}'
return [e['node'] for e in gql(q, {'q': query_str, 'n': limit})['data']['products']['edges'] if e['node'].get('featuredImage')]
@@ -56,6 +57,7 @@ def iter_published(skip_enriched=True):
"""Cursor-paginate ALL published products, yielding those needing enrichment."""
q = ('query($c:String){ products(first:100, after:$c, query:"status:active published_status:published"){ '
'pageInfo{hasNextPage endCursor} edges{ node{ id title featuredImage{url} '
+ 'cn: metafield(namespace:"custom", key:"color_name"){ value } '
'cd: metafield(namespace:"custom", key:"color_details"){ value } } } } }')
cursor = None
while True:
@@ -202,11 +204,64 @@ def write_metafield(gid, colors):
raise RuntimeError(errs)
+_MATERIAL_WORDS = ('Wallcovering', 'Wallcoverings', 'Wallpaper', 'Fabric', 'Fabrics',
+ 'Mural', 'Murals', 'Commercial', 'Grasscloth', 'Vinyl')
+
+
+def marketed_color(p):
+ """The product's SOLD color name (custom.color_name, else parsed from the title)."""
+ name = ((p.get('cn') or {}).get('value') or '').strip() if p.get('cn') else ''
+ if not name:
+ t = (p.get('title') or '').split('|')[0].strip()
+ if ' - ' in t:
+ name = t.split(' - ')[-1].strip()
+ for w in _MATERIAL_WORDS:
+ name = name.replace(w, '').strip()
+ name = ' '.join(name.split())
+ return name if 1 < len(name) <= 24 else None
+
+
+def resolve_color(name):
+ """Marketed color name -> (canonical lexicon name, RGB). Exact, then color-word substring."""
+ if not name:
+ return None
+ low = name.lower()
+ for k, v in NAMED_COLORS.items():
+ if k.lower() == low:
+ return (k, v)
+ for k, v in sorted(NAMED_COLORS.items(), key=lambda kv: -len(kv[0])):
+ if k.lower() in low:
+ return (k, v)
+ return None
+
+
+def anchor_marketed(colors, p):
+ """Tag the product's SOLD color onto the right swatch, using the canonical lexicon name:
+ - near-solid product (dominant >= 58%): the product IS its color -> rename the DOMINANT.
+ - multi-color pattern: rename the swatch NEAREST the marketed color (tags the accent, not the ground).
+ Keeps every real hex."""
+ resolved = resolve_color(marketed_color(p))
+ if not resolved or not colors:
+ return colors
+ canon, kr = resolved
+ if colors[0].get('pct', 0) >= 58:
+ idx = 0
+ else:
+ def dist(hexstr):
+ h = hexstr.lstrip('#'); r, g, b = int(h[0:2], 16), int(h[2:4], 16), int(h[4:6], 16)
+ return (r - kr[0]) ** 2 + (g - kr[1]) ** 2 + (b - kr[2]) ** 2
+ idx = min(range(len(colors)), key=lambda i: dist(colors[i]['hex']))
+ if all(c['name'] != canon for j, c in enumerate(colors) if j != idx):
+ colors[idx]['name'] = canon
+ return colors
+
+
def enrich_one(p, dry):
img_bytes = urllib.request.urlopen(p['featuredImage']['url'], timeout=40).read()
hexpct = dominant_colors(img_bytes)
names = name_hexes([h for h, _ in hexpct]) # deterministic table (no model call)
colors = [{'name': names[i], 'hex': hexpct[i][0], 'pct': hexpct[i][1]} for i in range(len(hexpct))]
+ colors = anchor_marketed(colors, p) # tag the marketed color onto its nearest swatch
if not dry:
write_metafield(p['id'], colors)
return ', '.join(f"{c['name']} {c['pct']}%" for c in colors)
diff --git a/shopify/theme-LIVE-591/snippets/color-palette.liquid b/shopify/theme-LIVE-591/snippets/color-palette.liquid
index fd5ae41e..055af45c 100644
--- a/shopify/theme-LIVE-591/snippets/color-palette.liquid
+++ b/shopify/theme-LIVE-591/snippets/color-palette.liquid
@@ -12,12 +12,14 @@
`pct`/`percentage` field when present, else are computed live from the image.
{% endcomment %}
+{% comment %} Read the authoritative enriched field FIRST (color_details); legacy
+ color_dots_json is only a fallback so it can never shadow fresh enrichment. {% endcomment %}
{% assign color_dots_json_val = product.metafields.custom.color_dots_json.value %}
{% assign color_details_val = product.metafields.custom.color_details.value %}
-{% if color_dots_json_val != blank %}
- {% assign color_details_raw = color_dots_json_val | json %}
-{% elsif color_details_val != blank %}
+{% if color_details_val != blank %}
{% assign color_details_raw = color_details_val | json %}
+{% elsif color_dots_json_val != blank %}
+ {% assign color_details_raw = color_dots_json_val | json %}
{% else %}
{% assign color_details_raw = blank %}
{% endif %}
← 75188a05 enrich: replace LLM naming with deterministic ~115-color int
·
back to Designer Wallcoverings
·
cadence: Alan Campbell productType fallback Wallcovering->Fa f1fea763 →