AnasSarkiz/muse-voice-remote

Muse Voice Remote — bare ESP32-S3R8, 155 TOP JLCPCB parts with real supplier CAD, native 3D assembly and GLB exports. Revised 17 mm case allocation; 0.30 mm via drill /0.45 mm pad. Checked prototype PCB; mechanical, RF and MUSE qualification pending.

Version
1.3.2
License
unset
Stars
0

scripts/preserve-jlcpcb-vias.ts

// Thermal VIAs supplied inside an exposed pad are footprint geometry. The
// upstream JSX exporter emits standalone unconnected <via> components instead.
// Retain coordinates/drills/copper and bind the plated holes to the owning pin.
export function preserveThermalVias(tsx: string, circuit: Record<string, any>[]) {
  const vias = circuit.filter(e=>e.type==="pcb_via")
  const bindings = vias.map(via=>{
    const radius = via.outer_diameter/2
    const owners = circuit.filter(p=>p.type==="pcb_smtpad" && p.shape==="rect" &&
      Math.abs(via.x-p.x)+radius<=p.width/2 && Math.abs(via.y-p.y)+radius<=p.height/2)
    if (owners.length!==1) throw new Error("Cannot identify supplier thermal-via pad; do not drop or redraw it.")
    const pin = owners[0].port_hints?.find((hint:string)=>/^pin\d+$/.test(hint))
    if (!pin) throw new Error("Supplier thermal pad lacks a physical pin binding.")
    return {via,pad:owners[0],pin}
  })
  const converted = tsx.replace(/<via\s+[^>]+\/>/g,tag=>{
    const x=Number.parseFloat(tag.match(/pcbX="([^"]+)"/)?.[1]??"NaN")
    const y=Number.parseFloat(tag.match(/pcbY="([^"]+)"/)?.[1]??"NaN")
    const binding=bindings.find(b=>Math.hypot(b.via.x-x,b.via.y-y)<0.00001)
    if (!binding) throw new Error("Generated via does not match retained supplier geometry.")
    return tag.replace(/^<via/,`<platedhole portHints={["${binding.pin}"]} shape="circle"`)
      .replace(/\s+layers=\{\[[^\]]+\]\}/,"")
  })
  const convertedCircuit=circuit.map(e=>{
    const binding=bindings.find(b=>b.via===e)
    if (!binding) return e
    return {type:"pcb_plated_hole",pcb_plated_hole_id:`supplier_thermal_${e.pcb_via_id}`,
      pcb_component_id:binding.pad.pcb_component_id,pcb_port_id:binding.pad.pcb_port_id,
      shape:"circle",x:e.x,y:e.y,outer_diameter:e.outer_diameter,hole_diameter:e.hole_diameter,
      port_hints:[binding.pin],layers:["top","bottom"]}
  })
  return {tsx:converted,circuit:convertedCircuit,bindings:bindings.map(b=>({
    pin:b.pin,x:b.via.x,y:b.via.y,outerDiameter:b.via.outer_diameter,holeDiameter:b.via.hole_diameter}))}
}