/** Shopify CDN: Minification failed

Line 16:0 Unexpected "{"
Line 16:1 Expected identifier but found "%"
Line 17:8 Unexpected "3D"
Line 18:1 Expected identifier but found "%"
Line 20:0 Unexpected "<"
Line 35:0 Comments in CSS use "/* ... */" instead of "//"
Line 39:0 Comments in CSS use "/* ... */" instead of "//"
Line 40:28 Unexpected ";"
Line 42:0 Comments in CSS use "/* ... */" instead of "//"
Line 46:7 Expected ":"
... and 45 more hidden warnings

**/
{% comment %}
Updated 3D Color Picker to automatically fallback to nodes if materials are missing.
{% endcomment %}

<script type="module">
import "https://unpkg.com/@google/model-viewer/dist/model-viewer.min.js";

const mv = document.getElementById('mv');
const glbInput = document.getElementById('glbInput');
const loadBtn = document.getElementById('loadBtn');
const partSelect = document.getElementById('partSelect');
const partCount = document.getElementById('partCount');
const paletteEl = document.getElementById('palette');
const customColor = document.getElementById('customColor');
const applyCustom = document.getElementById('applyCustom');
const selectionList = document.getElementById('selectionList');
const resetBtn = document.getElementById('resetBtn');
const shareBtn = document.getElementById('shareBtn');

// Palette
const PALETTE = parsePalette(`{{ section.settings.palette | escape }}`);
renderPalette(PALETTE);

// Selections
const currentSelections = {};

// Detect if materials exist, fallback to nodes
let MATERIAL_MODE = 'materials';

function populateParts(){
  const sg = mv.sceneGraph;
  let names = [];
  if(sg?.materials?.length){
    names = sg.materials.map(m=>m.name).filter(Boolean);
  } else if(sg?.model?.children?.length){
    MATERIAL_MODE = 'nodes';
    names = sg.model.children.map(c=>c.name).filter(Boolean);
  }

  partSelect.innerHTML = '';
  names.forEach(n=>{
    const opt = document.createElement('option');
    opt.value = n; opt.textContent = n;
    partSelect.appendChild(opt);
  });
  partCount.textContent = `${names.length} editable part(s)`;
}

mv.addEventListener('load', async () => {
  await mv.updateComplete;
  populateParts();

  const params = new URLSearchParams(location.search);
  const parts = params.get('parts');
  if(parts){
    const parsed = parsePartsParam(parts);
    for(const [name, hex] of Object.entries(parsed)) applyColorToPart(name, hex);
    renderSelections();
  }
});

loadBtn.addEventListener('click', ()=>{ if(glbInput.value) mv.src = glbInput.value; });
shareBtn.addEventListener('click', ()=>{
  const url = new URL(location.href);
  const glb = glbInput.value || mv.src || '';
  const partsParam = encodePartsParam(currentSelections);
  if(glb) url.searchParams.set('glb', glb);
  if(partsParam) url.searchParams.set('parts', partsParam);
  navigator.clipboard.writeText(url.toString());
  shareBtn.textContent='Link Copied!';
  setTimeout(()=>shareBtn.textContent='Copy Share Link',1500);
});

paletteEl.addEventListener('click', e=>{
  const sw = e.target.closest('.swatch');
  if(!sw) return;
  const hex = sw.dataset.hex;
  const part = partSelect.value;
  if(part && hex){ applyColorToPart(part, hex); renderSelections(); }
});
applyCustom.addEventListener('click', ()=>{
  const part = partSelect.value;
  const hex = customColor.value;
  if(part && hex){ applyColorToPart(part, hex); renderSelections(); }
});
resetBtn.addEventListener('click', ()=>{
  const current = mv.src;
  mv.src='';
  setTimeout(()=>{ mv.src=current; currentSelectionsClear(); }, 50);
});

function currentSelectionsClear(){ for(const k of Object.keys(currentSelections)) delete currentSelections[k]; renderSelections(); }
function renderPalette(list){ paletteEl.innerHTML=''; list.forEach(({label,hex})=>{
  const sw = document.createElement('button'); sw.className='swatch'; sw.style.background=hex; sw.title=label||hex; sw.dataset.hex=hex; paletteEl.appendChild(sw);
});}
function applyColorToPart(partName, hex){
  const sg = mv.sceneGraph;
  if(!sg) return;
  let target;
  if(MATERIAL_MODE==='materials') target = sg.materials?.find(m=>m.name===partName);
  else target = sg.model.children.find(n=>n.name===partName);
  if(!target) return;
  const rgba = hexToRgba(hex);
  if(MATERIAL_MODE==='materials') target.pbrMetallicRoughness.setBaseColorFactor(rgba);
  else target.traverse(obj=>{ if(obj.material?.pbrMetallicRoughness) obj.material.pbrMetallicRoughness.setBaseColorFactor(rgba); });
  currentSelections[partName]=hex;
}
function renderSelections(){ selectionList.textContent = Object.keys(currentSelections).length ? Object.entries(currentSelections).map(([k,v])=>`${k}:${v.replace('#','')}`).join(', ') : '—'; }
function encodePartsParam(obj){ return Object.entries(obj).map(([k,v])=>`${encodeURIComponent(k)}:${v.replace('#','')}`).join(','); }
function parsePartsParam(str){ const out={}; str.split(',').forEach(pair=>{ const [k,hex]=pair.split(':'); if(k && hex) out[decodeURIComponent(k)]=`#${hex.toLowerCase()}`; }); return out; }
function hexToRgba(hex){ const h=hex.replace('#',''); return [parseInt(h.slice(0,2),16)/255,parseInt(h.slice(2,4),16)/255,parseInt(h.slice(4,6),16)/255,1]; }
function parseCsv(str){ return str?str.split(',').map(s=>s.trim()).filter(Boolean):[]; }
function parsePalette(str){
  if(!str) return [{label:'Black',hex:'#000000'},{label:'White',hex:'#ffffff'},{label:'Hot Pink',hex:'#ff69b4'}];
  return str.split(',').map(item=>{ const [label,hex]=item.split(':').map(s=>s.trim()); const h=(hex||'').startsWith('#')?hex:`#${hex}`; return {label:label||h,hex:h}; }).filter(x=>/^#[0-9a-fA-F]{6}$/.test(x.hex));
}
</script>
