← back to Wallco Ai
add professional-palette reference + suffix learned from dw_unified
df08ff3f4158b677b18c083f0a5aec4ebf8bc43d · 2026-05-19 23:32:31 -0700 · SteveStudio2
Files touched
A data/professional-palette-reference.jsonA data/professional-palette-suffix.txtA scripts/build_professional_palette_reference.js
Diff
commit df08ff3f4158b677b18c083f0a5aec4ebf8bc43d
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Tue May 19 23:32:31 2026 -0700
add professional-palette reference + suffix learned from dw_unified
---
data/professional-palette-reference.json | 904 ++++++++++++++++++++++++
data/professional-palette-suffix.txt | 1 +
scripts/build_professional_palette_reference.js | 404 +++++++++++
3 files changed, 1309 insertions(+)
diff --git a/data/professional-palette-reference.json b/data/professional-palette-reference.json
new file mode 100644
index 0000000..4e0783a
--- /dev/null
+++ b/data/professional-palette-reference.json
@@ -0,0 +1,904 @@
+{
+ "source": "dw_unified.shopify_color_enrichment",
+ "ts": "2026-05-20T06:32:03.507Z",
+ "vendors_surveyed": [
+ {
+ "vendor": "Kravet",
+ "product_count": 2532
+ },
+ {
+ "vendor": "Koroseal",
+ "product_count": 2522
+ },
+ {
+ "vendor": "Phillip Jeffries",
+ "product_count": 2503
+ },
+ {
+ "vendor": "Schumacher",
+ "product_count": 1690
+ },
+ {
+ "vendor": "Thibaut Wallpaper",
+ "product_count": 1456
+ },
+ {
+ "vendor": "Cole & Son",
+ "product_count": 1246
+ },
+ {
+ "vendor": "Scalamandre Wallpaper",
+ "product_count": 1102
+ },
+ {
+ "vendor": "Osborne & Little",
+ "product_count": 1021
+ },
+ {
+ "vendor": "Wolf Gordon",
+ "product_count": 938
+ },
+ {
+ "vendor": "Designers Guild",
+ "product_count": 861
+ },
+ {
+ "vendor": "Nina Campbell",
+ "product_count": 780
+ },
+ {
+ "vendor": "Maya Romanoff",
+ "product_count": 639
+ },
+ {
+ "vendor": "Anna French",
+ "product_count": 625
+ },
+ {
+ "vendor": "Harlequin",
+ "product_count": 583
+ },
+ {
+ "vendor": "Arte International",
+ "product_count": 581
+ },
+ {
+ "vendor": "Brunschwig & Fils",
+ "product_count": 569
+ },
+ {
+ "vendor": "William Morris",
+ "product_count": 526
+ },
+ {
+ "vendor": "Lee Jofa",
+ "product_count": 465
+ },
+ {
+ "vendor": "Romo",
+ "product_count": 450
+ },
+ {
+ "vendor": "Innovations USA",
+ "product_count": 436
+ },
+ {
+ "vendor": "Thibaut",
+ "product_count": 417
+ },
+ {
+ "vendor": "Clarke And Clarke",
+ "product_count": 368
+ },
+ {
+ "vendor": "Gaston Y Daniela",
+ "product_count": 278
+ },
+ {
+ "vendor": "GP & J Baker",
+ "product_count": 267
+ },
+ {
+ "vendor": "Ralph Lauren",
+ "product_count": 254
+ },
+ {
+ "vendor": "Elitis",
+ "product_count": 244
+ },
+ {
+ "vendor": "Christian Lacroix Europe",
+ "product_count": 170
+ },
+ {
+ "vendor": "Donghia",
+ "product_count": 160
+ },
+ {
+ "vendor": "Mulberry",
+ "product_count": 131
+ },
+ {
+ "vendor": "Pierre Frey",
+ "product_count": 3
+ }
+ ],
+ "product_count": 23817,
+ "hex_instance_count": 62732,
+ "distinct_hex_count": 24212,
+ "top_50_hexes": [
+ {
+ "hex": "#FFFFFF",
+ "pct_of_products": 0.1014,
+ "appearances": 2414,
+ "avg_share_in_design": 21.8,
+ "hsl": {
+ "h": 0,
+ "s": 0,
+ "l": 100
+ },
+ "hue_bucket": "neutral-white",
+ "common_name": "ivory"
+ },
+ {
+ "hex": "#6B382C",
+ "pct_of_products": 0.0742,
+ "appearances": 1768,
+ "avg_share_in_design": 98,
+ "hsl": {
+ "h": 11.4,
+ "s": 41.7,
+ "l": 29.6
+ },
+ "hue_bucket": "red",
+ "common_name": "cocoa"
+ },
+ {
+ "hex": "#D3D3D3",
+ "pct_of_products": 0.0607,
+ "appearances": 1445,
+ "avg_share_in_design": 38.4,
+ "hsl": {
+ "h": 0,
+ "s": 0,
+ "l": 82.7
+ },
+ "hue_bucket": "neutral-gray",
+ "common_name": "oyster"
+ },
+ {
+ "hex": "#F5F5DC",
+ "pct_of_products": 0.0571,
+ "appearances": 1359,
+ "avg_share_in_design": 38.9,
+ "hsl": {
+ "h": 60,
+ "s": 55.6,
+ "l": 91.2
+ },
+ "hue_bucket": "orange-yellow",
+ "common_name": "cream"
+ },
+ {
+ "hex": "#F5F5F5",
+ "pct_of_products": 0.0353,
+ "appearances": 841,
+ "avg_share_in_design": 42.8,
+ "hsl": {
+ "h": 0,
+ "s": 0,
+ "l": 96.1
+ },
+ "hue_bucket": "neutral-white",
+ "common_name": "off-white"
+ },
+ {
+ "hex": "#C0C0C0",
+ "pct_of_products": 0.0304,
+ "appearances": 725,
+ "avg_share_in_design": 41.3,
+ "hsl": {
+ "h": 0,
+ "s": 0,
+ "l": 75.3
+ },
+ "hue_bucket": "neutral-gray",
+ "common_name": "dove"
+ },
+ {
+ "hex": "#D2B48C",
+ "pct_of_products": 0.0264,
+ "appearances": 628,
+ "avg_share_in_design": 35.9,
+ "hsl": {
+ "h": 34.3,
+ "s": 43.7,
+ "l": 68.6
+ },
+ "hue_bucket": "red-orange",
+ "common_name": "fawn"
+ },
+ {
+ "hex": "#000000",
+ "pct_of_products": 0.0256,
+ "appearances": 610,
+ "avg_share_in_design": 22.4,
+ "hsl": {
+ "h": 0,
+ "s": 0,
+ "l": 0
+ },
+ "hue_bucket": "neutral-black",
+ "common_name": "noir"
+ },
+ {
+ "hex": "#F2F2F2",
+ "pct_of_products": 0.0228,
+ "appearances": 542,
+ "avg_share_in_design": 35,
+ "hsl": {
+ "h": 0,
+ "s": 0,
+ "l": 94.9
+ },
+ "hue_bucket": "neutral-white",
+ "common_name": "off-white"
+ },
+ {
+ "hex": "#F0F0F0",
+ "pct_of_products": 0.0174,
+ "appearances": 415,
+ "avg_share_in_design": 54.4,
+ "hsl": {
+ "h": 0,
+ "s": 0,
+ "l": 94.1
+ },
+ "hue_bucket": "neutral-white",
+ "common_name": "cream"
+ },
+ {
+ "hex": "#4A4A4A",
+ "pct_of_products": 0.013,
+ "appearances": 310,
+ "avg_share_in_design": 42.4,
+ "hsl": {
+ "h": 0,
+ "s": 0,
+ "l": 29
+ },
+ "hue_bucket": "neutral-gray",
+ "common_name": "umber"
+ },
+ {
+ "hex": "#F5F3EE",
+ "pct_of_products": 0.013,
+ "appearances": 309,
+ "avg_share_in_design": 43.7,
+ "hsl": {
+ "h": 42.9,
+ "s": 25.9,
+ "l": 94.7
+ },
+ "hue_bucket": "red-orange",
+ "common_name": "off-white"
+ },
+ {
+ "hex": "#6B3827",
+ "pct_of_products": 0.0123,
+ "appearances": 292,
+ "avg_share_in_design": 96.8,
+ "hsl": {
+ "h": 15,
+ "s": 46.6,
+ "l": 28.6
+ },
+ "hue_bucket": "red",
+ "common_name": "cocoa"
+ },
+ {
+ "hex": "#F0E68C",
+ "pct_of_products": 0.0114,
+ "appearances": 272,
+ "avg_share_in_design": 32.4,
+ "hsl": {
+ "h": 54,
+ "s": 76.9,
+ "l": 74.5
+ },
+ "hue_bucket": "orange-yellow",
+ "common_name": "butter"
+ },
+ {
+ "hex": "#F5F0E1",
+ "pct_of_products": 0.0114,
+ "appearances": 271,
+ "avg_share_in_design": 54.4,
+ "hsl": {
+ "h": 45,
+ "s": 50,
+ "l": 92.2
+ },
+ "hue_bucket": "red-orange",
+ "common_name": "cream"
+ },
+ {
+ "hex": "#F5F5F0",
+ "pct_of_products": 0.0113,
+ "appearances": 269,
+ "avg_share_in_design": 50.9,
+ "hsl": {
+ "h": 60,
+ "s": 20,
+ "l": 95.1
+ },
+ "hue_bucket": "orange-yellow",
+ "common_name": "off-white"
+ },
+ {
+ "hex": "#333333",
+ "pct_of_products": 0.0111,
+ "appearances": 264,
+ "avg_share_in_design": 44.6,
+ "hsl": {
+ "h": 0,
+ "s": 0,
+ "l": 20
+ },
+ "hue_bucket": "neutral-gray",
+ "common_name": "charcoal"
+ },
+ {
+ "hex": "#A9A9A9",
+ "pct_of_products": 0.0109,
+ "appearances": 260,
+ "avg_share_in_design": 35.1,
+ "hsl": {
+ "h": 0,
+ "s": 0,
+ "l": 66.3
+ },
+ "hue_bucket": "neutral-gray",
+ "common_name": "stone"
+ },
+ {
+ "hex": "#363636",
+ "pct_of_products": 0.0108,
+ "appearances": 257,
+ "avg_share_in_design": 43.4,
+ "hsl": {
+ "h": 0,
+ "s": 0,
+ "l": 21.2
+ },
+ "hue_bucket": "neutral-gray",
+ "common_name": "charcoal"
+ },
+ {
+ "hex": "#F5F3ED",
+ "pct_of_products": 0.0107,
+ "appearances": 256,
+ "avg_share_in_design": 46.6,
+ "hsl": {
+ "h": 45,
+ "s": 28.6,
+ "l": 94.5
+ },
+ "hue_bucket": "red-orange",
+ "common_name": "off-white"
+ },
+ {
+ "hex": "#F2E8D9",
+ "pct_of_products": 0.0107,
+ "appearances": 254,
+ "avg_share_in_design": 46.3,
+ "hsl": {
+ "h": 36,
+ "s": 49,
+ "l": 90
+ },
+ "hue_bucket": "red-orange",
+ "common_name": "pearl"
+ },
+ {
+ "hex": "#36454F",
+ "pct_of_products": 0.0102,
+ "appearances": 243,
+ "avg_share_in_design": 29.1,
+ "hsl": {
+ "h": 204,
+ "s": 18.8,
+ "l": 26.1
+ },
+ "hue_bucket": "blue",
+ "common_name": "charcoal"
+ },
+ {
+ "hex": "#6B3A2A",
+ "pct_of_products": 0.0101,
+ "appearances": 240,
+ "avg_share_in_design": 96.5,
+ "hsl": {
+ "h": 14.8,
+ "s": 43.6,
+ "l": 29.2
+ },
+ "hue_bucket": "red",
+ "common_name": "cocoa"
+ },
+ {
+ "hex": "#808080",
+ "pct_of_products": 0.0096,
+ "appearances": 229,
+ "avg_share_in_design": 30.6,
+ "hsl": {
+ "h": 0,
+ "s": 0,
+ "l": 50.2
+ },
+ "hue_bucket": "neutral-gray",
+ "common_name": "slate"
+ },
+ {
+ "hex": "#008080",
+ "pct_of_products": 0.009,
+ "appearances": 215,
+ "avg_share_in_design": 36.1,
+ "hsl": {
+ "h": 180,
+ "s": 100,
+ "l": 25.1
+ },
+ "hue_bucket": "blue-green",
+ "common_name": "malachite"
+ },
+ {
+ "hex": "#483C32",
+ "pct_of_products": 0.009,
+ "appearances": 214,
+ "avg_share_in_design": 27.7,
+ "hsl": {
+ "h": 27.3,
+ "s": 18,
+ "l": 23.9
+ },
+ "hue_bucket": "red-orange",
+ "common_name": "walnut"
+ },
+ {
+ "hex": "#F2E7D6",
+ "pct_of_products": 0.0086,
+ "appearances": 204,
+ "avg_share_in_design": 59.5,
+ "hsl": {
+ "h": 36.4,
+ "s": 51.9,
+ "l": 89.4
+ },
+ "hue_bucket": "red-orange",
+ "common_name": "pearl"
+ },
+ {
+ "hex": "#B0C4DE",
+ "pct_of_products": 0.0084,
+ "appearances": 199,
+ "avg_share_in_design": 42.2,
+ "hsl": {
+ "h": 213.9,
+ "s": 41.1,
+ "l": 78
+ },
+ "hue_bucket": "blue",
+ "common_name": "sky"
+ },
+ {
+ "hex": "#87CEEB",
+ "pct_of_products": 0.0076,
+ "appearances": 182,
+ "avg_share_in_design": 29.8,
+ "hsl": {
+ "h": 197.4,
+ "s": 71.4,
+ "l": 72.5
+ },
+ "hue_bucket": "blue",
+ "common_name": "robin egg"
+ },
+ {
+ "hex": "#F8F8F8",
+ "pct_of_products": 0.0074,
+ "appearances": 177,
+ "avg_share_in_design": 27.5,
+ "hsl": {
+ "h": 0,
+ "s": 0,
+ "l": 97.3
+ },
+ "hue_bucket": "neutral-white",
+ "common_name": "off-white"
+ },
+ {
+ "hex": "#F5F3E8",
+ "pct_of_products": 0.0067,
+ "appearances": 160,
+ "avg_share_in_design": 47.2,
+ "hsl": {
+ "h": 50.8,
+ "s": 39.4,
+ "l": 93.5
+ },
+ "hue_bucket": "orange-yellow",
+ "common_name": "cream"
+ },
+ {
+ "hex": "#ADD8E6",
+ "pct_of_products": 0.0067,
+ "appearances": 160,
+ "avg_share_in_design": 32,
+ "hsl": {
+ "h": 194.7,
+ "s": 53.3,
+ "l": 79
+ },
+ "hue_bucket": "blue-green",
+ "common_name": "powder blue"
+ },
+ {
+ "hex": "#BDB76B",
+ "pct_of_products": 0.0066,
+ "appearances": 158,
+ "avg_share_in_design": 39.8,
+ "hsl": {
+ "h": 55.6,
+ "s": 38.3,
+ "l": 58
+ },
+ "hue_bucket": "orange-yellow",
+ "common_name": "fawn"
+ },
+ {
+ "hex": "#F8F8FF",
+ "pct_of_products": 0.0064,
+ "appearances": 153,
+ "avg_share_in_design": 36.7,
+ "hsl": {
+ "h": 240,
+ "s": 100,
+ "l": 98.6
+ },
+ "hue_bucket": "blue-purple",
+ "common_name": "off-white"
+ },
+ {
+ "hex": "#6495ED",
+ "pct_of_products": 0.0063,
+ "appearances": 150,
+ "avg_share_in_design": 40.1,
+ "hsl": {
+ "h": 218.5,
+ "s": 79.2,
+ "l": 66.1
+ },
+ "hue_bucket": "blue",
+ "common_name": "cornflower"
+ },
+ {
+ "hex": "#6B8E23",
+ "pct_of_products": 0.006,
+ "appearances": 143,
+ "avg_share_in_design": 36.7,
+ "hsl": {
+ "h": 79.6,
+ "s": 60.5,
+ "l": 34.7
+ },
+ "hue_bucket": "yellow-green",
+ "common_name": "moss"
+ },
+ {
+ "hex": "#BDBDBD",
+ "pct_of_products": 0.0055,
+ "appearances": 132,
+ "avg_share_in_design": 39.6,
+ "hsl": {
+ "h": 0,
+ "s": 0,
+ "l": 74.1
+ },
+ "hue_bucket": "neutral-gray",
+ "common_name": "dove"
+ },
+ {
+ "hex": "#808000",
+ "pct_of_products": 0.0052,
+ "appearances": 124,
+ "avg_share_in_design": 26.5,
+ "hsl": {
+ "h": 60,
+ "s": 100,
+ "l": 25.1
+ },
+ "hue_bucket": "orange-yellow",
+ "common_name": "bronze"
+ },
+ {
+ "hex": "#C2B280",
+ "pct_of_products": 0.0051,
+ "appearances": 122,
+ "avg_share_in_design": 30.2,
+ "hsl": {
+ "h": 45.5,
+ "s": 35.1,
+ "l": 63.1
+ },
+ "hue_bucket": "orange-yellow",
+ "common_name": "fawn"
+ },
+ {
+ "hex": "#E5DDCB",
+ "pct_of_products": 0.0051,
+ "appearances": 121,
+ "avg_share_in_design": 43.9,
+ "hsl": {
+ "h": 41.5,
+ "s": 33.3,
+ "l": 84.7
+ },
+ "hue_bucket": "red-orange",
+ "common_name": "bone"
+ },
+ {
+ "hex": "#F2F0E6",
+ "pct_of_products": 0.005,
+ "appearances": 119,
+ "avg_share_in_design": 41.4,
+ "hsl": {
+ "h": 50,
+ "s": 31.6,
+ "l": 92.5
+ },
+ "hue_bucket": "orange-yellow",
+ "common_name": "cream"
+ },
+ {
+ "hex": "#696969",
+ "pct_of_products": 0.0049,
+ "appearances": 117,
+ "avg_share_in_design": 42,
+ "hsl": {
+ "h": 0,
+ "s": 0,
+ "l": 41.2
+ },
+ "hue_bucket": "neutral-gray",
+ "common_name": "fern"
+ },
+ {
+ "hex": "#708090",
+ "pct_of_products": 0.0047,
+ "appearances": 112,
+ "avg_share_in_design": 31.8,
+ "hsl": {
+ "h": 210,
+ "s": 12.6,
+ "l": 50.2
+ },
+ "hue_bucket": "neutral-gray",
+ "common_name": "slate"
+ },
+ {
+ "hex": "#F0E6D2",
+ "pct_of_products": 0.0047,
+ "appearances": 111,
+ "avg_share_in_design": 36.4,
+ "hsl": {
+ "h": 40,
+ "s": 50,
+ "l": 88.2
+ },
+ "hue_bucket": "red-orange",
+ "common_name": "parchment"
+ },
+ {
+ "hex": "#FFFFF0",
+ "pct_of_products": 0.0045,
+ "appearances": 107,
+ "avg_share_in_design": 39.6,
+ "hsl": {
+ "h": 60,
+ "s": 100,
+ "l": 97.1
+ },
+ "hue_bucket": "orange-yellow",
+ "common_name": "ivory"
+ },
+ {
+ "hex": "#E0E0E0",
+ "pct_of_products": 0.0043,
+ "appearances": 103,
+ "avg_share_in_design": 34.1,
+ "hsl": {
+ "h": 0,
+ "s": 0,
+ "l": 87.8
+ },
+ "hue_bucket": "neutral-white",
+ "common_name": "linen"
+ },
+ {
+ "hex": "#E8E8E8",
+ "pct_of_products": 0.0043,
+ "appearances": 102,
+ "avg_share_in_design": 31.8,
+ "hsl": {
+ "h": 0,
+ "s": 0,
+ "l": 91
+ },
+ "hue_bucket": "neutral-white",
+ "common_name": "pearl"
+ },
+ {
+ "hex": "#F08080",
+ "pct_of_products": 0.0043,
+ "appearances": 102,
+ "avg_share_in_design": 24.6,
+ "hsl": {
+ "h": 0,
+ "s": 78.9,
+ "l": 72.2
+ },
+ "hue_bucket": "red",
+ "common_name": "coral"
+ },
+ {
+ "hex": "#4682B4",
+ "pct_of_products": 0.0042,
+ "appearances": 100,
+ "avg_share_in_design": 33.1,
+ "hsl": {
+ "h": 207.3,
+ "s": 44,
+ "l": 49
+ },
+ "hue_bucket": "blue",
+ "common_name": "denim"
+ },
+ {
+ "hex": "#E0D8C4",
+ "pct_of_products": 0.004,
+ "appearances": 95,
+ "avg_share_in_design": 38.7,
+ "hsl": {
+ "h": 42.9,
+ "s": 31.1,
+ "l": 82.4
+ },
+ "hue_bucket": "red-orange",
+ "common_name": "bone"
+ }
+ ],
+ "hue_distribution": {
+ "red-orange": 0.2781,
+ "neutral-gray": 0.1945,
+ "orange-yellow": 0.1244,
+ "blue": 0.0875,
+ "red": 0.0871,
+ "neutral-white": 0.0851,
+ "blue-green": 0.0446,
+ "yellow-green": 0.0402,
+ "green": 0.0193,
+ "neutral-black": 0.0167,
+ "magenta-pink": 0.0099,
+ "blue-purple": 0.0093,
+ "purple": 0.0033
+ },
+ "saturation_distribution": {
+ "0-20": 0.3668,
+ "20-40": 0.2838,
+ "40-60": 0.2382,
+ "60-80": 0.0759,
+ "80-100": 0.0353
+ },
+ "lightness_distribution": {
+ "0-20": 0.0225,
+ "20-40": 0.1689,
+ "40-60": 0.1847,
+ "60-80": 0.2828,
+ "80-100": 0.3411
+ },
+ "hexes_per_design_distribution": [
+ {
+ "distinct_hexes": 1,
+ "product_count": 1525,
+ "pct": 0.064
+ },
+ {
+ "distinct_hexes": 2,
+ "product_count": 11026,
+ "pct": 0.4629
+ },
+ {
+ "distinct_hexes": 3,
+ "product_count": 8258,
+ "pct": 0.3467
+ },
+ {
+ "distinct_hexes": 4,
+ "product_count": 1543,
+ "pct": 0.0648
+ },
+ {
+ "distinct_hexes": 5,
+ "product_count": 681,
+ "pct": 0.0286
+ },
+ {
+ "distinct_hexes": 6,
+ "product_count": 718,
+ "pct": 0.0301
+ },
+ {
+ "distinct_hexes": 7,
+ "product_count": 42,
+ "pct": 0.0018
+ },
+ {
+ "distinct_hexes": 8,
+ "product_count": 18,
+ "pct": 0.0008
+ },
+ {
+ "distinct_hexes": 9,
+ "product_count": 3,
+ "pct": 0.0001
+ },
+ {
+ "distinct_hexes": 10,
+ "product_count": 2,
+ "pct": 0.0001
+ },
+ {
+ "distinct_hexes": 11,
+ "product_count": 1,
+ "pct": 0
+ }
+ ],
+ "professional_palette_words": [
+ "ivory",
+ "cocoa",
+ "oyster",
+ "cream",
+ "off-white",
+ "dove",
+ "fawn",
+ "noir",
+ "umber",
+ "butter",
+ "charcoal",
+ "stone",
+ "pearl",
+ "slate",
+ "malachite",
+ "walnut",
+ "sky",
+ "robin egg",
+ "powder blue",
+ "cornflower",
+ "moss",
+ "bronze",
+ "bone",
+ "fern",
+ "parchment",
+ "linen",
+ "coral",
+ "denim"
+ ],
+ "methodology": {
+ "table": "shopify_color_enrichment",
+ "filter": "vendor IN PRO_VENDORS AND hex_codes is non-empty jsonb array",
+ "counting": "one vote per product per distinct hex (deduped within a product)",
+ "naming": "nearest weighted-RGB match against a curated 70-name designer-paint vocab",
+ "hue_bins": "11 hue bins (30°-ish) + neutral-white / neutral-gray / neutral-black when S<15%"
+ }
+}
\ No newline at end of file
diff --git a/data/professional-palette-suffix.txt b/data/professional-palette-suffix.txt
new file mode 100644
index 0000000..816b637
--- /dev/null
+++ b/data/professional-palette-suffix.txt
@@ -0,0 +1 @@
+Professional designer-wallpaper palette grounded in real-world high-end vendor catalogs (Schumacher, Maya Romanoff, Thibaut, Arte International, Cole & Son, Brunschwig & Fils, Scalamandre, Phillip Jeffries, Koroseal, Romo, Pierre Frey, Designers Guild, Osborne & Little — 23,817 real products analyzed in dw_unified.shopify_color_enrichment). Use ONLY colors from this learned palette: #FFFFFF (ivory), #6B382C (cocoa), #D3D3D3 (oyster), #F5F5DC (cream), #F5F5F5 (off-white), #C0C0C0 (dove), #D2B48C (fawn), #000000 (noir), #F2F2F2 (off-white), #F0F0F0 (cream), #4A4A4A (umber), #F5F3EE (off-white), #6B3827 (cocoa), #F0E68C (butter), #F5F0E1 (cream), #F5F5F0 (off-white), #333333 (charcoal), #A9A9A9 (stone), #363636 (charcoal), #F5F3ED (off-white), #F2E8D9 (pearl), #36454F (charcoal), #6B3A2A (cocoa), #808080 (slate), #008080 (malachite). Saturation distribution observed in real professional wallpaper: S<20% = 36.7% of all color usage, S 20–40% = 28.4%, S>60% = 11.1%. Most professional wallpapers use a low-saturation, tone-on-tone or 2–3 color story. Median distinct hues per design = 2; 90th percentile = 4. Keep total distinct hues per design ≤ 5. Never use neon, fluorescent, rainbow, primary-saturated red/yellow/blue, hot pink, or electric green. Prefer aged, slightly-grayed, archival pigments over pure chromatic colors.
diff --git a/scripts/build_professional_palette_reference.js b/scripts/build_professional_palette_reference.js
new file mode 100644
index 0000000..60d491d
--- /dev/null
+++ b/scripts/build_professional_palette_reference.js
@@ -0,0 +1,404 @@
+#!/usr/bin/env node
+/**
+ * Build professional-palette-reference.json + professional-palette-suffix.txt
+ * from REAL dw_unified.shopify_color_enrichment rows for high-end vendors.
+ *
+ * Source: dw_unified.shopify_color_enrichment (jsonb hex_codes + colors[{hex,name,percentage}])
+ *
+ * Steve's rule: stop using made-up "ivory/taupe/sand" lists. Use REAL
+ * Schumacher / Maya Romanoff / Thibaut / Arte / Cole & Son palettes.
+ */
+const { execSync } = require('child_process');
+const fs = require('fs');
+const path = require('path');
+
+// Canonical "professional / luxury wallpaper" vendors that actually exist in
+// dw_unified.shopify_color_enrichment (verified before adding here).
+const PRO_VENDORS = [
+ 'Schumacher',
+ 'Maya Romanoff',
+ 'Thibaut',
+ 'Thibaut Wallpaper',
+ 'Arte International',
+ 'Cole & Son',
+ 'Brunschwig & Fils',
+ 'Scalamandre',
+ 'Scalamandre Wallpaper',
+ 'Phillip Jeffries',
+ 'Koroseal',
+ 'Romo',
+ 'Designtex',
+ 'Pierre Frey',
+ 'Elitis',
+ 'Élitis',
+ 'Hermès',
+ 'Fromental',
+ 'de Gournay',
+ 'Nina Campbell',
+ 'Osborne & Little',
+ 'Designers Guild',
+ 'Harlequin',
+ 'Anna French',
+ 'Lee Jofa',
+ 'GP & J Baker',
+ 'Mulberry',
+ 'Ralph Lauren',
+ 'William Morris',
+ 'Donghia',
+ 'Kravet',
+ 'Wolf Gordon',
+ 'Innovations USA',
+ 'Christian Lacroix Europe',
+ 'Gaston Y Daniela',
+ 'Clarke And Clarke',
+];
+
+// ---------- color math helpers ----------
+function hexToRgb(hex) {
+ if (typeof hex !== 'string') return null;
+ let h = hex.trim().replace(/^#/, '');
+ if (h.length === 3) h = h.split('').map((c) => c + c).join('');
+ if (h.length !== 6 || /[^0-9a-f]/i.test(h)) return null;
+ const n = parseInt(h, 16);
+ return { r: (n >> 16) & 255, g: (n >> 8) & 255, b: n & 255 };
+}
+
+function rgbToHsl({ r, g, b }) {
+ const R = r / 255, G = g / 255, B = b / 255;
+ const max = Math.max(R, G, B), min = Math.min(R, G, B);
+ const l = (max + min) / 2;
+ let h = 0, s = 0;
+ if (max !== min) {
+ const d = max - min;
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
+ switch (max) {
+ case R: h = (G - B) / d + (G < B ? 6 : 0); break;
+ case G: h = (B - R) / d + 2; break;
+ case B: h = (R - G) / d + 4; break;
+ }
+ h *= 60;
+ }
+ return { h, s: s * 100, l: l * 100 };
+}
+
+// 30° bins on hue + neutral bucket when saturation <15%
+const HUE_BINS = [
+ [ 0, 15, 'red'],
+ [ 15, 45, 'red-orange'],
+ [ 45, 75, 'orange-yellow'],
+ [ 75, 105, 'yellow-green'],
+ [105, 150, 'green'],
+ [150, 195, 'blue-green'],
+ [195, 240, 'blue'],
+ [240, 285, 'blue-purple'],
+ [285, 315, 'purple'],
+ [315, 345, 'magenta-pink'],
+ [345, 360, 'red'],
+];
+function hueBucket(hsl) {
+ if (hsl.s < 15) {
+ if (hsl.l < 20) return 'neutral-black';
+ if (hsl.l > 85) return 'neutral-white';
+ return 'neutral-gray';
+ }
+ for (const [a, b, label] of HUE_BINS) {
+ if (hsl.h >= a && hsl.h < b) return label;
+ }
+ return 'red';
+}
+
+// Nearest plain-English name from a small designer-paint vocabulary.
+// Curated specifically for wallpaper trade colors — names a designer says,
+// not a Crayola box.
+const COLOR_VOCAB = [
+ // neutrals
+ ['ivory', '#FFFFF0'],
+ ['cream', '#F5F0E1'],
+ ['ecru', '#E6DDC2'],
+ ['parchment', '#EFE4C8'],
+ ['linen', '#E8DFD0'],
+ ['bone', '#E3DAC9'],
+ ['oyster', '#D6CFC2'],
+ ['stone', '#A8A294'],
+ ['mushroom', '#A99F8C'],
+ ['taupe', '#9C8E7D'],
+ ['greige', '#A89A87'],
+ ['fawn', '#C2B091'],
+ ['dove', '#C9C5BC'],
+ ['pearl', '#EAE6DF'],
+ ['ash', '#9B9B95'],
+ ['charcoal', '#3A3A3A'],
+ ['ink', '#1B1B1B'],
+ ['noir', '#0A0A0A'],
+ ['off-white', '#FAF7F0'],
+ // greens
+ ['sage', '#9CAE92'],
+ ['celadon', '#A7C3A4'],
+ ['olive', '#6B6B3A'],
+ ['moss', '#7A8450'],
+ ['fern', '#5C7050'],
+ ['forest', '#2E4A2F'],
+ ['eucalyptus', '#A3B8A0'],
+ ['hunter', '#355E3B'],
+ ['emerald', '#1F6E4A'],
+ ['malachite', '#0F6B43'],
+ ['jade', '#3F7368'],
+ // blues
+ ['robin egg', '#9AC0CD'],
+ ['powder blue', '#B8C9D8'],
+ ['cornflower', '#6A8FC5'],
+ ['sky', '#A6C5DA'],
+ ['slate', '#6C7B89'],
+ ['denim', '#5076A0'],
+ ['indigo', '#2A3E66'],
+ ['midnight', '#152238'],
+ ['navy', '#1B2A47'],
+ ['teal', '#2F6970'],
+ ['peacock', '#1E5A6D'],
+ ['cerulean', '#2E5C8A'],
+ ['delft blue', '#385E8A'],
+ // purples
+ ['lavender', '#B7A6C9'],
+ ['mauve', '#A88A99'],
+ ['plum', '#5E3B5C'],
+ ['aubergine', '#3D2B3E'],
+ ['heather', '#9B8AA0'],
+ // reds / pinks
+ ['blush', '#E5C2BC'],
+ ['rose', '#C99393'],
+ ['coral', '#D58A75'],
+ ['terracotta', '#B8634B'],
+ ['rust', '#9C5234'],
+ ['oxblood', '#5A1F22'],
+ ['burgundy', '#5C1A2B'],
+ ['claret', '#6E2334'],
+ ['cinnabar', '#A33A2B'],
+ ['venetian red', '#A33A2B'],
+ // yellows / browns
+ ['butter', '#EDDFA6'],
+ ['straw', '#D8C58A'],
+ ['mustard', '#B58A2D'],
+ ['ochre', '#B68642'],
+ ['gold', '#A98740'],
+ ['bronze', '#8C6E3F'],
+ ['caramel', '#8E5D32'],
+ ['cocoa', '#5E3F2C'],
+ ['walnut', '#4E3A2A'],
+ ['chocolate', '#3D2A1E'],
+ ['umber', '#5A4232'],
+ ['sienna', '#8A4A30'],
+];
+
+function colorDist(a, b) {
+ // simple weighted RGB; fine for "closest plain-English label"
+ const dr = a.r - b.r, dg = a.g - b.g, db = a.b - b.b;
+ return dr * dr * 0.3 + dg * dg * 0.59 + db * db * 0.11;
+}
+function nearestName(hex) {
+ const rgb = hexToRgb(hex);
+ if (!rgb) return 'unknown';
+ let best = null, bestD = Infinity;
+ for (const [name, vhex] of COLOR_VOCAB) {
+ const v = hexToRgb(vhex);
+ const d = colorDist(rgb, v);
+ if (d < bestD) { bestD = d; best = name; }
+ }
+ return best;
+}
+
+// ---------- main ----------
+(async () => {
+ const vendorList = PRO_VENDORS.map(v => v.replace(/'/g, "''")).map(v => `'${v}'`).join(',');
+ const sql = `
+ SELECT coalesce(json_agg(row_to_json(t)), '[]') FROM (
+ SELECT vendor, hex_codes, colors
+ FROM shopify_color_enrichment
+ WHERE vendor IN (${vendorList})
+ AND hex_codes IS NOT NULL
+ AND jsonb_typeof(hex_codes) = 'array'
+ AND jsonb_array_length(hex_codes) > 0
+ ) t
+ `;
+ const raw = execSync(
+ `psql -d dw_unified -tA -c ${JSON.stringify(sql.replace(/\s+/g, ' '))}`,
+ { encoding: 'utf8', maxBuffer: 256 << 20 }
+ );
+ const rows = JSON.parse(raw.trim());
+ console.log(`[pro-palette] surveyed ${rows.length.toLocaleString()} professional products`);
+
+ const vendorCounts = {};
+ const hexFreq = new Map(); // hex -> product count (one vote per product)
+ const hexProducts = new Map(); // hex -> Set(rowIdx) for pct math
+ const hexPercentSum = new Map(); // hex -> sum of "percentage" across rows when colors{} present
+ const hexPercentN = new Map(); // hex -> n contributing
+ let hexInstancesTotal = 0;
+ const hueBucketCount = {};
+ const satBuckets = { '0-20':0, '20-40':0, '40-60':0, '60-80':0, '80-100':0 };
+ const lightBuckets = { '0-20':0, '20-40':0, '40-60':0, '60-80':0, '80-100':0 };
+ let colorsPerDesignHist = {}; // n distinct hexes per product
+
+ rows.forEach((row, idx) => {
+ vendorCounts[row.vendor] = (vendorCounts[row.vendor] || 0) + 1;
+ const hexes = Array.isArray(row.hex_codes) ? row.hex_codes : [];
+ const named = Array.isArray(row.colors) ? row.colors : [];
+ // dedupe within a product so we count "products that contain hex" not "instances"
+ const uniq = new Set();
+ hexes.forEach((h) => {
+ if (typeof h !== 'string') return;
+ const norm = h.trim().toUpperCase();
+ if (!/^#[0-9A-F]{6}$/.test(norm)) return;
+ uniq.add(norm);
+ });
+ const n = uniq.size;
+ colorsPerDesignHist[n] = (colorsPerDesignHist[n] || 0) + 1;
+ uniq.forEach((h) => {
+ hexFreq.set(h, (hexFreq.get(h) || 0) + 1);
+ hexInstancesTotal++;
+ const rgb = hexToRgb(h);
+ if (rgb) {
+ const hsl = rgbToHsl(rgb);
+ const bucket = hueBucket(hsl);
+ hueBucketCount[bucket] = (hueBucketCount[bucket] || 0) + 1;
+ const sBin = Math.min(Math.floor(hsl.s / 20), 4);
+ const lBin = Math.min(Math.floor(hsl.l / 20), 4);
+ const sKey = `${sBin*20}-${(sBin+1)*20}`;
+ const lKey = `${lBin*20}-${(lBin+1)*20}`;
+ satBuckets[sKey] = (satBuckets[sKey] || 0) + 1;
+ lightBuckets[lKey] = (lightBuckets[lKey] || 0) + 1;
+ }
+ });
+ // accumulate named percentage info
+ named.forEach((c) => {
+ if (!c || typeof c.hex !== 'string') return;
+ const norm = c.hex.trim().toUpperCase();
+ if (!/^#[0-9A-F]{6}$/.test(norm)) return;
+ const pct = Number(c.percentage);
+ if (!Number.isFinite(pct)) return;
+ hexPercentSum.set(norm, (hexPercentSum.get(norm) || 0) + pct);
+ hexPercentN.set(norm, (hexPercentN.get(norm) || 0) + 1);
+ });
+ });
+
+ const ranked = Array.from(hexFreq.entries())
+ .sort((a, b) => b[1] - a[1])
+ .slice(0, 50)
+ .map(([hex, count]) => {
+ const rgb = hexToRgb(hex);
+ const hsl = rgbToHsl(rgb);
+ return {
+ hex,
+ pct_of_products: +(count / rows.length).toFixed(4),
+ appearances: count,
+ avg_share_in_design: hexPercentN.get(hex)
+ ? +(hexPercentSum.get(hex) / hexPercentN.get(hex)).toFixed(1)
+ : null,
+ hsl: { h: +hsl.h.toFixed(1), s: +hsl.s.toFixed(1), l: +hsl.l.toFixed(1) },
+ hue_bucket: hueBucket(hsl),
+ common_name: nearestName(hex),
+ };
+ });
+
+ // normalize hue distribution as a percentage
+ const totalHex = hexInstancesTotal;
+ const hueDist = {};
+ Object.entries(hueBucketCount)
+ .sort((a, b) => b[1] - a[1])
+ .forEach(([k, v]) => { hueDist[k] = +(v / totalHex).toFixed(4); });
+
+ const satDist = {};
+ const lightDist = {};
+ Object.entries(satBuckets).forEach(([k, v]) => satDist[k] = +(v / totalHex).toFixed(4));
+ Object.entries(lightBuckets).forEach(([k, v]) => lightDist[k] = +(v / totalHex).toFixed(4));
+
+ const hexesPerDesign = Object.entries(colorsPerDesignHist)
+ .sort((a, b) => Number(a[0]) - Number(b[0]))
+ .map(([n, c]) => ({ distinct_hexes: Number(n), product_count: c, pct: +(c/rows.length).toFixed(4) }));
+
+ const reference = {
+ source: 'dw_unified.shopify_color_enrichment',
+ ts: new Date().toISOString(),
+ vendors_surveyed: Object.entries(vendorCounts).sort((a,b)=>b[1]-a[1]).map(([v,c]) => ({ vendor: v, product_count: c })),
+ product_count: rows.length,
+ hex_instance_count: hexInstancesTotal,
+ distinct_hex_count: hexFreq.size,
+ top_50_hexes: ranked,
+ hue_distribution: hueDist,
+ saturation_distribution: satDist,
+ lightness_distribution: lightDist,
+ hexes_per_design_distribution: hexesPerDesign,
+ professional_palette_words: Array.from(new Set(ranked.map(r => r.common_name))),
+ methodology: {
+ table: 'shopify_color_enrichment',
+ filter: 'vendor IN PRO_VENDORS AND hex_codes is non-empty jsonb array',
+ counting: 'one vote per product per distinct hex (deduped within a product)',
+ naming: 'nearest weighted-RGB match against a curated 70-name designer-paint vocab',
+ hue_bins: '11 hue bins (30°-ish) + neutral-white / neutral-gray / neutral-black when S<15%',
+ },
+ };
+
+ // ---------- write reference json ----------
+ const outDir = path.join(__dirname, '..', 'data');
+ const refPath = path.join(outDir, 'professional-palette-reference.json');
+ fs.writeFileSync(refPath, JSON.stringify(reference, null, 2));
+ console.log(`[pro-palette] wrote ${refPath}`);
+
+ // ---------- build the prompt suffix ----------
+ // Pick the top ~25 hexes for the suffix (any more and prompts get bloated).
+ const topForSuffix = ranked.slice(0, 25);
+ const hexList = topForSuffix.map(r => `${r.hex} (${r.common_name})`).join(', ');
+
+ const satLow = ((satDist['0-20'] || 0) * 100).toFixed(1);
+ const satMid = ((satDist['20-40'] || 0) * 100).toFixed(1);
+ const satHigh = (((satDist['60-80'] || 0) + (satDist['80-100'] || 0)) * 100).toFixed(1);
+
+ // observed median number of distinct hexes per product
+ const sorted = [];
+ hexesPerDesign.forEach(({ distinct_hexes, product_count }) => {
+ for (let i = 0; i < product_count; i++) sorted.push(distinct_hexes);
+ });
+ sorted.sort((a, b) => a - b);
+ const median = sorted[Math.floor(sorted.length / 2)] || 0;
+ const p90 = sorted[Math.floor(sorted.length * 0.9)] || 0;
+
+ const suffix =
+`Professional designer-wallpaper palette grounded in real-world high-end vendor catalogs ` +
+`(Schumacher, Maya Romanoff, Thibaut, Arte International, Cole & Son, Brunschwig & Fils, Scalamandre, ` +
+`Phillip Jeffries, Koroseal, Romo, Pierre Frey, Designers Guild, Osborne & Little — ${rows.length.toLocaleString()} ` +
+`real products analyzed in dw_unified.shopify_color_enrichment). ` +
+`Use ONLY colors from this learned palette: ${hexList}. ` +
+`Saturation distribution observed in real professional wallpaper: S<20% = ${satLow}% of all color usage, ` +
+`S 20–40% = ${satMid}%, S>60% = ${satHigh}%. ` +
+`Most professional wallpapers use a low-saturation, tone-on-tone or 2–3 color story. ` +
+`Median distinct hues per design = ${median}; 90th percentile = ${p90}. ` +
+`Keep total distinct hues per design ≤ ${Math.max(p90, 5)}. ` +
+`Never use neon, fluorescent, rainbow, primary-saturated red/yellow/blue, hot pink, or electric green. ` +
+`Prefer aged, slightly-grayed, archival pigments over pure chromatic colors.`;
+
+ const suffixPath = path.join(outDir, 'professional-palette-suffix.txt');
+ fs.writeFileSync(suffixPath, suffix + '\n');
+ console.log(`[pro-palette] wrote ${suffixPath}`);
+
+ // ---------- console headline ----------
+ console.log('\n=== HEADLINE ===');
+ console.log(`vendors surveyed: ${Object.keys(vendorCounts).length}`);
+ console.log(`products surveyed: ${rows.length.toLocaleString()}`);
+ console.log(`distinct hexes: ${hexFreq.size.toLocaleString()}`);
+ console.log(`hex instances total: ${hexInstancesTotal.toLocaleString()}`);
+ console.log(`S<20%: ${satLow}% of color usage`);
+ console.log(`S 20–40%: ${satMid}%`);
+ console.log(`S>60%: ${satHigh}%`);
+ console.log(`median hues/design: ${median}`);
+ console.log(`p90 hues/design: ${p90}`);
+ console.log('\nTop 10 hexes:');
+ ranked.slice(0, 10).forEach((r, i) => {
+ console.log(` ${String(i+1).padStart(2)}. ${r.hex} ${String(r.common_name).padEnd(14)} ${(r.pct_of_products*100).toFixed(1)}% of products bucket=${r.hue_bucket}`);
+ });
+ console.log('\nHue distribution:');
+ Object.entries(hueDist).slice(0, 12).forEach(([k, v]) => {
+ console.log(` ${k.padEnd(16)} ${(v*100).toFixed(1)}%`);
+ });
+
+})().catch((e) => {
+ console.error(e);
+ process.exit(1);
+});
← eee8638 aesthetic purge: log 1007 designs to do-not-want.jsonl (neon
·
back to Wallco Ai
·
generator hardening: tone-on-tone suffix + neon/rainbow nega dec2cf7 →