← back to Flock Fix Viewer
non-astek flock: verify + segment 192 non-PR flock-tagged; apply print_type=Flock to 33 active genuine-flock (reversible); gated per-vendor plan
3133a6208a87d5ed7c126c37a4a3b3e02971465b · 2026-09-02 14:31:45 -0700 · Steve Abrams
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A8mCr1GHE1PGSWJd1yydnh
Files touched
A apply-theme-sqft2.mjsA nonastek-printtype-snapshot.jsonA nonastek-printtype.mjsA theme-backup/spec-sheet-button.liquid
Diff
commit 3133a6208a87d5ed7c126c37a4a3b3e02971465b
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Wed Sep 2 14:31:45 2026 -0700
non-astek flock: verify + segment 192 non-PR flock-tagged; apply print_type=Flock to 33 active genuine-flock (reversible); gated per-vendor plan
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A8mCr1GHE1PGSWJd1yydnh
---
apply-theme-sqft2.mjs | 23 +++
nonastek-printtype-snapshot.json | 266 +++++++++++++++++++++++++++++++++
nonastek-printtype.mjs | 42 ++++++
theme-backup/spec-sheet-button.liquid | 271 ++++++++++++++++++++++++++++++++++
4 files changed, 602 insertions(+)
diff --git a/apply-theme-sqft2.mjs b/apply-theme-sqft2.mjs
new file mode 100644
index 0000000..8d0caba
--- /dev/null
+++ b/apply-theme-sqft2.mjs
@@ -0,0 +1,23 @@
+// Adds "Sq Ft S/R" + "Sq Ft D/R" rows to the PRINTABLE spec-sheet block (snippets/spec-sheet-button.liquid),
+// so sqft shows in BOTH spec areas. Run: node apply-theme-sqft2.mjs (idempotent, backs up first)
+import fs from 'fs';
+const SHOP='designer-laboratory-sandbox.myshopify.com', MAIN='145121607731';
+const KEY='snippets/spec-sheet-button.liquid';
+const TOKEN=(fs.readFileSync('/Users/macstudio3/Projects/secrets-manager/.env','utf8')
+ .split('\n').find(l=>l.startsWith('SHOPIFY_THEME_TOKEN='))||'').split('=').slice(1).join('=').replace(/["']/g,'').trim();
+const API=`https://${SHOP}/admin/api/2024-10`, H={'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'};
+
+const anchor="{%- if rep != blank -%}<div class=\"dw-spec-item\"><strong>Repeat:</strong><span>{{ rep | escape }}</span></div>{%- endif -%}";
+const add="\n {%- assign sqftsr = mf.global.sqft_single_roll | default: mf.custom.sqft_single_roll -%}"+
+ "\n {%- if sqftsr != blank -%}<div class=\"dw-spec-item\"><strong>Sq Ft S/R:</strong><span>{{ sqftsr | escape }}</span></div>{%- endif -%}"+
+ "\n {%- assign sqftdr = mf.global.sqft_double_roll | default: mf.custom.sqft_double_roll -%}"+
+ "\n {%- if sqftdr != blank -%}<div class=\"dw-spec-item\"><strong>Sq Ft D/R:</strong><span>{{ sqftdr | escape }}</span></div>{%- endif -%}";
+
+const r=await (await fetch(`${API}/themes/${MAIN}/assets.json?asset%5Bkey%5D=${encodeURIComponent(KEY)}`,{headers:H})).json();
+let body=r.asset.value;
+fs.writeFileSync('/Users/macstudio3/Projects/flock-fix-viewer/theme-backup/spec-sheet-button.PRE-SQFT.liquid', body);
+if(body.includes('Sq Ft S/R')){console.log('Already applied — nothing to do.');process.exit(0);}
+if(body.split(anchor).length!==2){console.error('anchor not found uniquely — aborting');process.exit(1);}
+body=body.replace(anchor,anchor+add);
+const put=await (await fetch(`${API}/themes/${MAIN}/assets.json`,{method:'PUT',headers:H,body:JSON.stringify({asset:{key:KEY,value:body}})})).json();
+console.log(put.asset?('OK — updated '+put.asset.key+'. Sq Ft now shows on the printable spec sheet too.'):('ERROR '+JSON.stringify(put)));
diff --git a/nonastek-printtype-snapshot.json b/nonastek-printtype-snapshot.json
new file mode 100644
index 0000000..3fa499d
--- /dev/null
+++ b/nonastek-printtype-snapshot.json
@@ -0,0 +1,266 @@
+[
+ {
+ "id": "gid://shopify/Product/1497626443888",
+ "handle": "chateau-de-chantilly-flock-velvet-ere-44560",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ },
+ {
+ "id": "gid://shopify/Product/6555440709683",
+ "handle": "dwgb-700164",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ },
+ {
+ "id": "gid://shopify/Product/6610816991283",
+ "handle": "chennai-cobalt-blue-wallpaper",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ },
+ {
+ "id": "gid://shopify/Product/6610836848691",
+ "handle": "dwdg-985631",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ },
+ {
+ "id": "gid://shopify/Product/6610836881459",
+ "handle": "dwdg-985641",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ },
+ {
+ "id": "gid://shopify/Product/6610836914227",
+ "handle": "dwdg-985651",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ },
+ {
+ "id": "gid://shopify/Product/6610836979763",
+ "handle": "dwdg-985661",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ },
+ {
+ "id": "gid://shopify/Product/6610837045299",
+ "handle": "dwdg-985761",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ },
+ {
+ "id": "gid://shopify/Product/7513501990963",
+ "handle": "eur-80274-ncw4273-designer-wallcoverings-los-angeles",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ },
+ {
+ "id": "gid://shopify/Product/7513513885747",
+ "handle": "eur-80576-pcl7019-designer-wallcoverings-los-angeles",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ },
+ {
+ "id": "gid://shopify/Product/7513954156595",
+ "handle": "eur-90320-designer-wallcoverings-los-angeles",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ },
+ {
+ "id": "gid://shopify/Product/7514183467059",
+ "handle": "eur-70022-designer-wallcoverings-los-angeles",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ },
+ {
+ "id": "gid://shopify/Product/7514183499827",
+ "handle": "eur-70023-designer-wallcoverings-los-angeles",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ },
+ {
+ "id": "gid://shopify/Product/7515288830003",
+ "handle": "eur-71099-designer-wallcoverings-los-angeles",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ },
+ {
+ "id": "gid://shopify/Product/7515288862771",
+ "handle": "eur-71100-designer-wallcoverings-los-angeles",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ },
+ {
+ "id": "gid://shopify/Product/7515288895539",
+ "handle": "eur-71101-designer-wallcoverings-los-angeles",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ },
+ {
+ "id": "gid://shopify/Product/7515288928307",
+ "handle": "eur-71102-designer-wallcoverings-los-angeles",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ },
+ {
+ "id": "gid://shopify/Product/7515288961075",
+ "handle": "eur-71103-designer-wallcoverings-los-angeles",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ },
+ {
+ "id": "gid://shopify/Product/7515288993843",
+ "handle": "eur-71104-designer-wallcoverings-los-angeles",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ },
+ {
+ "id": "gid://shopify/Product/7515289026611",
+ "handle": "eur-71105-designer-wallcoverings-los-angeles",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ },
+ {
+ "id": "gid://shopify/Product/7515289059379",
+ "handle": "eur-71106-designer-wallcoverings-los-angeles",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ },
+ {
+ "id": "gid://shopify/Product/7515289092147",
+ "handle": "eur-71107-designer-wallcoverings-los-angeles",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ },
+ {
+ "id": "gid://shopify/Product/7515289124915",
+ "handle": "eur-71108-designer-wallcoverings-los-angeles",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ },
+ {
+ "id": "gid://shopify/Product/7515289157683",
+ "handle": "eur-71109-designer-wallcoverings-los-angeles",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ },
+ {
+ "id": "gid://shopify/Product/7515289190451",
+ "handle": "eur-71110-designer-wallcoverings-los-angeles",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ },
+ {
+ "id": "gid://shopify/Product/7515289223219",
+ "handle": "eur-71111-designer-wallcoverings-los-angeles",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ },
+ {
+ "id": "gid://shopify/Product/7515289255987",
+ "handle": "eur-71112-designer-wallcoverings-los-angeles",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ },
+ {
+ "id": "gid://shopify/Product/7515289288755",
+ "handle": "eur-71113-designer-wallcoverings-los-angeles",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ },
+ {
+ "id": "gid://shopify/Product/7515289321523",
+ "handle": "eur-71114-designer-wallcoverings-los-angeles",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ },
+ {
+ "id": "gid://shopify/Product/7515289354291",
+ "handle": "eur-71115-designer-wallcoverings-los-angeles",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ },
+ {
+ "id": "gid://shopify/Product/7515289387059",
+ "handle": "eur-71116-designer-wallcoverings-los-angeles",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ },
+ {
+ "id": "gid://shopify/Product/7515289419827",
+ "handle": "eur-71117-designer-wallcoverings-los-angeles",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ },
+ {
+ "id": "gid://shopify/Product/7515289452595",
+ "handle": "eur-71118-designer-wallcoverings-los-angeles",
+ "old": {
+ "global_print_type": null,
+ "custom_print_type": null
+ }
+ }
+]
\ No newline at end of file
diff --git a/nonastek-printtype.mjs b/nonastek-printtype.mjs
new file mode 100644
index 0000000..f6442fc
--- /dev/null
+++ b/nonastek-printtype.mjs
@@ -0,0 +1,42 @@
+// Non-Astek flock: set print_type=Flock (global+custom) on ACTIVE, genuinely-flock
+// (title/handle contains "flock"), non-Phillipe-Romano products that lack it.
+// Self-evident from the product's own stated construction. Reversible + ledgered. --apply to write.
+import fs from 'fs'; import os from 'os'; import path from 'path';
+const SHOP='designer-laboratory-sandbox.myshopify.com';
+const TOKEN=fs.readFileSync(new URL('./.token',import.meta.url),'utf8').trim();
+const LEDGER=path.join(os.homedir(),'.claude/yolo-queue/executed-reversible/ledger.jsonl');
+const SNAP=new URL('./nonastek-printtype-snapshot.json',import.meta.url);
+const DRY=!process.argv.includes('--apply');
+const API=`https://${SHOP}/admin/api/2024-10`;
+const H={'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'};
+const sleep=ms=>new Promise(r=>setTimeout(r,ms));
+const gql=async(q,v={})=>(await (await fetch(`${API}/graphql.json`,{method:'POST',headers:H,body:JSON.stringify({query:q,variables:v})})).json());
+const Q=`query($c:String){ products(first:100, query:"tag:'Flock Velvet' -vendor:'Phillipe Romano' status:active", after:$c){
+ pageInfo{hasNextPage endCursor}
+ edges{node{ id handle title vendor
+ gpt:metafield(namespace:"global",key:"print_type"){value}
+ cpt:metafield(namespace:"custom",key:"print_type"){value}
+ }}}}`;
+let cur=null,all=[];
+do{const r=await gql(Q,{c:cur});const p=r?.data?.products;if(!p){console.error(JSON.stringify(r));break;}all=all.concat(p.edges);cur=p.pageInfo.hasNextPage?p.pageInfo.endCursor:null;}while(cur);
+const isFlock=n=>/flock/i.test(n.title)||/flock/i.test(n.handle);
+const targets=all.map(e=>e.node).filter(n=>isFlock(n) && !(n.gpt?.value) && !(n.cpt?.value));
+console.log(`ACTIVE non-PR genuine-flock lacking print_type: ${targets.length}`);
+const snap=[];
+let done=0;
+for(const n of targets){
+ if(DRY){ if(done<12) console.log(' '+n.vendor.padEnd(22)+' '+n.title.slice(0,46)); done++; continue; }
+ const mf=[
+ {ownerId:n.id,namespace:'global',key:'print_type',type:'single_line_text_field',value:'Flock'},
+ {ownerId:n.id,namespace:'custom',key:'print_type',type:'single_line_text_field',value:'Flock'},
+ ];
+ const r=await gql('mutation($mf:[MetafieldsSetInput!]!){metafieldsSet(metafields:$mf){userErrors{field message}}}',{mf});
+ const errs=r?.data?.metafieldsSet?.userErrors;
+ if(errs&&errs.length){console.log(' FAIL',n.handle,JSON.stringify(errs));continue;}
+ snap.push({id:n.id,handle:n.handle,old:{global_print_type:null,custom_print_type:null}});
+ fs.appendFileSync(LEDGER,JSON.stringify({ts:new Date().toISOString(),agent:'vp-dw-commerce:nonastek-flock-printtype',ticket:'TK-11128',action:`set print_type=Flock (global+custom) on ${n.handle} [${n.vendor}]`,blast_radius:1,undo_cmd:`metafieldsDelete global/custom.print_type on ${n.id} (old=null)`,verify:'PDP shows Print Type: Flock'})+'\n');
+ done++; if(done%10===0)console.log(` …${done}/${targets.length}`);
+ await sleep(200);
+}
+if(!DRY) fs.writeFileSync(SNAP,JSON.stringify(snap,null,1));
+console.log(`${DRY?'DRY':'DONE'} · ${done} (${targets.length} targets)`);
diff --git a/theme-backup/spec-sheet-button.liquid b/theme-backup/spec-sheet-button.liquid
new file mode 100644
index 0000000..f2f2a98
--- /dev/null
+++ b/theme-backup/spec-sheet-button.liquid
@@ -0,0 +1,271 @@
+{% comment %}
+ Spec Sheet Button — INLINE PDF download (no page navigation, no iframe).
+
+ FIX HISTORY
+ -----------
+ v1 (broken): fetch('/pages/spec-sheet') + DOMParser + html2canvas. DOMParser
+ never runs scripts, so #pdf-content was always the "Loading Product..."
+ placeholder -> 0-dim canvas -> jsPDF threw -> "Error".
+
+ v2 (broken on LIVE): hidden same-origin iframe -> /pages/spec-sheet -> call its
+ downloadPDF(). DEAD ON ARRIVAL: the storefront sends `X-Frame-Options: DENY`
+ + CSP `frame-ancestors 'none'` STORE-WIDE (verified 2026-06-23 on /, /pages/*,
+ /collections/*, /products/*). DENY blocks ALL framing — including same-origin —
+ so the iframe never renders and contentDocument is inaccessible.
+
+ v3 (this version — works against live CSP):
+ Build the spec-sheet DOM RIGHT HERE on the product page from Liquid-injected
+ product data + metafields (no fetch, no iframe, no /pages/spec-sheet
+ dependency). On click we lazy-load jsPDF + html2canvas from cdnjs (the live
+ CSP has NO script-src/default-src directive, so cdnjs is allowed — verified),
+ render the offscreen node with html2canvas (useCORS:true; the product image
+ is on cdn.shopify.com which returns `access-control-allow-origin: *`, so the
+ canvas does NOT taint), and jsPDF.save() it. One click, fully self-contained.
+
+ The standalone /pages/spec-sheet page still works for a direct visit (it has
+ {% layout none %} so it's a clean print page), but the BUTTON no longer needs it.
+{% endcomment %}
+
+{%- liquid
+ assign p = product
+ assign mf = product.metafields
+ # ---- Color: real color name only (spreadsheet/metafield/variant option), never AI ----
+ assign spec_color = ''
+ if mf.custom.color != blank
+ assign spec_color = mf.custom.color
+ elsif mf.global.Color != blank
+ assign spec_color = mf.global.Color
+ elsif mf.dwc.color != blank
+ assign spec_color = mf.dwc.color
+ endif
+ # ---- Mfr SKU ----
+ assign mfr_sku = ''
+ if mf.custom.manufacturer_sku != blank
+ assign mfr_sku = mf.custom.manufacturer_sku
+ elsif mf.dwc.manufacturer_sku != blank
+ assign mfr_sku = mf.dwc.manufacturer_sku
+ endif
+ # ---- DW SKU (strip -Sample) ----
+ assign dw_sku = p.selected_or_first_available_variant.sku | default: p.variants.first.sku
+ assign dw_sku = dw_sku | replace: '-Sample', '' | replace: '-SAMPLE', '' | replace: '-sample', ''
+-%}
+
+<div class="spec-sheet-controls">
+ <button type="button"
+ class="spec-sheet-btn pdf-download-btn"
+ data-dw-spec-trigger
+ aria-label="Download spec sheet PDF for {{ p.title | escape }}">
+ 🖨️ Download PDF
+ </button>
+</div>
+
+{%- comment %} Offscreen, fully-rendered spec sheet built from Liquid data {% endcomment -%}
+<div id="dw-spec-sheet" aria-hidden="true"
+ style="position:fixed;left:-10000px;top:0;width:816px;background:#fff;z-index:-1;">
+ <div class="dw-spec-page">
+ <div class="dw-spec-header">
+ <img class="dw-spec-logo"
+ crossorigin="anonymous"
+ src="https://www.designerwallcoverings.com/cdn/shop/files/Screen_Shot_2019-02-06_at_12.08.24_PM_300x172.png?v=1613539653"
+ alt="Designer Wallcoverings">
+ </div>
+ <div class="dw-spec-body">
+ <h1 class="dw-spec-title">{{ p.title | escape }}</h1>
+ {%- if p.featured_image -%}
+ <img class="dw-spec-img" crossorigin="anonymous"
+ src="{{ p.featured_image | image_url: width: 600 }}"
+ alt="{{ p.title | escape }}">
+ {%- endif -%}
+ <div class="dw-spec-grid">
+ <div class="dw-spec-item"><strong>DW SKU:</strong><span>{{ dw_sku | escape }}</span></div>
+ {%- if spec_color != blank -%}<div class="dw-spec-item"><strong>Color:</strong><span>{{ spec_color | escape }}</span></div>{%- endif -%}
+ {%- assign pname = mf.custom.pattern_name | default: mf.dwc.pattern_name -%}
+ {%- if pname != blank -%}<div class="dw-spec-item"><strong>Pattern Name:</strong><span>{{ pname | escape }}</span></div>{%- endif -%}
+ {%- if p.vendor != blank -%}<div class="dw-spec-item"><strong>Brand:</strong><span>{{ p.vendor | escape }}</span></div>{%- endif -%}
+ {%- assign coll = mf.custom.collection_name | default: mf.global.Collection -%}
+ {%- if coll != blank -%}<div class="dw-spec-item"><strong>Collection:</strong><span>{{ coll | escape }}</span></div>{%- endif -%}
+ {%- assign spec_style = mf.specs.style | default: mf.global.Style -%}
+ {%- if spec_style != blank -%}<div class="dw-spec-item"><strong>Style:</strong><span>{{ spec_style | escape }}</span></div>{%- endif -%}
+ {%- assign comp = mf.custom.material | default: mf.specs.material | default: mf.global.Content | default: mf.global.Contents -%}
+ {%- if comp != blank -%}<div class="dw-spec-item"><strong>Composition:</strong><span>{{ comp | escape }}</span></div>{%- endif -%}
+ {%- assign w = mf.custom.width | default: mf.global.width | default: mf.global.Width -%}
+ {%- if w != blank -%}<div class="dw-spec-item"><strong>Width:</strong><span>{{ w | escape }}</span></div>{%- endif -%}
+ {%- assign len = mf.custom.length | default: mf.global.length | default: mf.global.Length -%}
+ {%- if len != blank -%}<div class="dw-spec-item"><strong>{% if product.vendor == 'Designtex' %}Bolt size:{% else %}Length:{% endif %}</strong><span>{{ len | escape }}</span></div>{%- endif -%}
+ {%- assign rep = mf.custom.pattern_repeat | default: mf.custom.repeat | default: mf.specs.pattern_repeat | default: mf.specs.repeat_v | default: mf.dwc.repeat | default: mf.global.repeat -%}
+ {%- if rep != blank -%}<div class="dw-spec-item"><strong>Repeat:</strong><span>{{ rep | escape }}</span></div>{%- endif -%}
+ {%- assign mt = mf.custom.match_type | default: mf.global.Match | default: mf.global.MATCH -%}
+ {%- if mt != blank -%}<div class="dw-spec-item"><strong>Match:</strong><span>{{ mt | escape }}</span></div>{%- endif -%}
+ {%- assign wt = mf.custom.product_weight | default: mf.global.Weight -%}
+ {%- if wt != blank -%}<div class="dw-spec-item"><strong>Weight:</strong><span>{{ wt | escape }}</span></div>{%- endif -%}
+ {%- assign fin = mf.custom.finish | default: mf.global.Finish | default: mf.global.FINISH -%}
+ {%- if fin != blank -%}<div class="dw-spec-item"><strong>Finish:</strong><span>{{ fin | escape }}</span></div>{%- endif -%}
+ {%- assign care = mf.custom.care | default: mf.global.Cleaning -%}
+ {%- if care != blank -%}<div class="dw-spec-item"><strong>Care:</strong><span>{{ care | escape }}</span></div>{%- endif -%}
+ {%- assign fire = mf.custom.fire_rating | default: mf.global.fire_rating | default: mf.global.FLAMMABILITY -%}
+ {%- if fire != blank -%}<div class="dw-spec-item"><strong>Fire Rating:</strong><span>{{ fire | escape }}</span></div>{%- endif -%}
+ {%- assign origin = mf.custom.origin | default: mf.global.Country -%}
+ {%- if origin != blank -%}<div class="dw-spec-item"><strong>Country of Origin:</strong><span>{{ origin | escape }}</span></div>{%- endif -%}
+ {%- assign uom = mf.custom.unit_of_measure | default: mf.global.unit_of_measure -%}
+ {%- if uom != blank -%}<div class="dw-spec-item"><strong>Sold:</strong><span>{{ uom | escape }}</span></div>{%- endif -%}
+ {%- assign minord = mf.custom.minimum.value | default: mf.dwc.minimum_order_quantity.value | default: mf.global.v_prods_quantity_order_min.value -%}
+ {%- assign ulabel = '' -%}
+ {%- if uom contains 'Double Roll' -%}{%- assign ulabel = 'double rolls' -%}{%- elsif uom contains 'Roll' -%}{%- assign ulabel = 'rolls' -%}{%- elsif uom contains 'Yard' -%}{%- assign ulabel = 'yards' -%}{%- elsif uom contains 'Meter' -%}{%- assign ulabel = 'meters' -%}{%- endif -%}
+ {%- if minord != blank and minord != '1' and minord != 1 -%}<div class="dw-spec-item"><strong>Minimum Order:</strong><span>{{ minord | escape }}{%- if ulabel != blank %} {{ ulabel }}{% endif -%}</span></div>{%- endif -%}
+ {%- comment -%} mfr SKU "Extra Info" row removed 2026-07-16 (DTD verdict A, unanimous): private-label reverse-lookup key — backend metafield only, never on the downloadable spec sheet {%- endcomment -%}
+ </div>
+ </div>
+ <div class="dw-spec-footer">
+ <div class="dw-spec-footer-line company">Designer Wallcoverings Purchasing Agency</div>
+ <div class="dw-spec-footer-line">15442 Ventura Bl #102, Sherman Oaks, CA 91403</div>
+ <div class="dw-spec-footer-line">Phone: (888) 373-4564 | info@designerwallcoverings.com | www.designerwallcoverings.com</div>
+ <div class="dw-spec-footer-line italic">Purchasing Agency for the Wallcovering and Surface Material Industry</div>
+ <div class="dw-spec-footer-line tiny">Spec sheet information valid only when purchased through Designer Wallcoverings Purchasing</div>
+ </div>
+ </div>
+</div>
+
+<script>
+(function () {
+ if (window.__dwSpecPdfInit) return;
+ window.__dwSpecPdfInit = true;
+
+ var JSPDF_SRC = 'https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js';
+ var H2C_SRC = 'https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js';
+
+ function loadScript(src) {
+ return new Promise(function (resolve, reject) {
+ var existing = document.querySelector('script[src="' + src + '"]');
+ if (existing) {
+ if (existing.dataset.loaded === '1') return resolve();
+ existing.addEventListener('load', function () { resolve(); });
+ existing.addEventListener('error', function () { reject(new Error('load fail ' + src)); });
+ return;
+ }
+ var s = document.createElement('script');
+ s.src = src;
+ s.async = true;
+ s.addEventListener('load', function () { s.dataset.loaded = '1'; resolve(); });
+ s.addEventListener('error', function () { reject(new Error('load fail ' + src)); });
+ document.head.appendChild(s);
+ });
+ }
+
+ function ensureLibs() {
+ var need = [];
+ if (typeof window.html2canvas !== 'function') need.push(loadScript(H2C_SRC));
+ if (!(window.jspdf && window.jspdf.jsPDF)) need.push(loadScript(JSPDF_SRC));
+ return Promise.all(need);
+ }
+
+ // Decode the protocol-relative shopify logo to avoid a tainting surprise; the
+ // product image + logo are both cdn.shopify.com (ACAO:*), so useCORS handles them.
+ async function generate(btn) {
+ var node = document.getElementById('dw-spec-sheet');
+ if (!node) throw new Error('spec node missing');
+ // Make it renderable (html2canvas needs layout; keep it offscreen, not display:none).
+ node.style.zIndex = '-1';
+ node.style.visibility = 'visible';
+
+ await ensureLibs();
+
+ var canvas = await window.html2canvas(node.querySelector('.dw-spec-page'), {
+ scale: 2,
+ useCORS: true,
+ allowTaint: false,
+ backgroundColor: '#ffffff',
+ logging: false,
+ windowWidth: 816
+ });
+
+ var jsPDF = window.jspdf.jsPDF;
+ var imgData = canvas.toDataURL('image/png');
+ var pdf = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'letter' });
+ var imgWidth = 216;
+ var imgHeight = (canvas.height * imgWidth) / canvas.width;
+ pdf.addImage(imgData, 'PNG', 0, 0, imgWidth, imgHeight);
+
+ var sku = (node.querySelector('.dw-spec-item span') || {}).textContent || 'spec';
+ pdf.save('Designer-Wallcoverings-Los-Angeles-' + sku.trim().replace(/[^A-Za-z0-9\-]+/g, '-') + '.pdf');
+ }
+
+ function onClick(e) {
+ var btn = e.currentTarget;
+ var orig = btn.textContent;
+ btn.textContent = '⏳ Generating...';
+ btn.disabled = true;
+ generate(btn).then(function () {
+ btn.textContent = '✓ Downloaded!';
+ setTimeout(function () { btn.textContent = orig; btn.disabled = false; }, 2000);
+ }).catch(function (err) {
+ console.error('Spec PDF generation failed:', err);
+ btn.textContent = '✗ Error - Try Again';
+ setTimeout(function () { btn.textContent = orig; btn.disabled = false; }, 2500);
+ });
+ }
+
+ function wire() {
+ var btns = document.querySelectorAll('[data-dw-spec-trigger]');
+ for (var i = 0; i < btns.length; i++) {
+ if (btns[i].dataset.dwWired === '1') continue;
+ btns[i].dataset.dwWired = '1';
+ btns[i].addEventListener('click', onClick);
+ }
+ }
+
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', wire);
+ } else {
+ wire();
+ }
+})();
+</script>
+
+<style>
+.spec-sheet-controls {
+ display: inline-block;
+ margin: 0;
+ /* nudge the Download-PDF button up to align with the REQUEST SAMPLE row (Steve 2026-06-24) */
+ transform: translateY(-6px);
+}
+.spec-sheet-btn {
+ display: inline-block;
+ padding: 18px 24px;
+ background: #2c5f7f;
+ color: white;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 13px;
+ font-weight: 600;
+ transition: all 0.3s ease;
+ white-space: nowrap;
+}
+.spec-sheet-btn:hover {
+ background: #1e4557;
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15);
+}
+.spec-sheet-btn:active { transform: translateY(0); }
+.spec-sheet-btn:disabled { opacity: 0.6; cursor: not-allowed; }
+.pdf-download-btn { vertical-align: top; }
+
+/* Offscreen spec-sheet print styles (rendered by html2canvas, never shown to the user) */
+#dw-spec-sheet * { margin: 0; padding: 0; box-sizing: border-box; }
+#dw-spec-sheet .dw-spec-page { width: 816px; background: #fff; font-family: Arial, Helvetica, sans-serif; }
+#dw-spec-sheet .dw-spec-header { padding: 30px 40px; text-align: center; border-bottom: 2px solid #2c5f7f; }
+#dw-spec-sheet .dw-spec-logo { max-width: 200px; display: block; margin: 0 auto; }
+#dw-spec-sheet .dw-spec-body { padding: 30px 40px; }
+#dw-spec-sheet .dw-spec-title { font-size: 18px; color: #333; margin-bottom: 15px; font-weight: normal; text-align: center; text-transform: capitalize; }
+#dw-spec-sheet .dw-spec-img { max-width: 500px; max-height: 500px; object-fit: contain; display: block; margin: 0 auto 20px; width: 100%; }
+#dw-spec-sheet .dw-spec-grid { max-width: 560px; margin: 0 auto; display: grid; grid-template-columns: 1fr; gap: 10px; }
+#dw-spec-sheet .dw-spec-item { font-size: 14px; line-height: 1.6; display: flex; align-items: baseline; }
+#dw-spec-sheet .dw-spec-item strong { color: #333; font-weight: bold; margin-right: 6px; white-space: nowrap; }
+#dw-spec-sheet .dw-spec-item span { color: #555; flex: 1; }
+#dw-spec-sheet .dw-spec-footer { padding: 20px 40px; text-align: center; border-top: 2px solid #2c5f7f; background: #f9f9f9; }
+#dw-spec-sheet .dw-spec-footer-line { font-size: 10px; color: #666; margin: 3px 0; }
+#dw-spec-sheet .dw-spec-footer-line.company { font-size: 12px; font-weight: bold; color: #333; margin-bottom: 6px; }
+#dw-spec-sheet .dw-spec-footer-line.italic { font-style: italic; margin-top: 10px; }
+#dw-spec-sheet .dw-spec-footer-line.tiny { font-size: 9px; margin-top: 8px; }
+</style>
+
← 5f23184 flock: enrichment rollout (27 Astek-matched, exact match+con
·
back to Flock Fix Viewer
·
auto-data-snapshot: 2026-09-02T14:42:45 (1 data files) — str 21f8dea →