@jlcpcb/cli 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,49 @@
1
1
  # @jlcpcb/cli
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 0.3.0
8
+
9
+ Minor Changes
10
+
11
+ CLI:
12
+ - Add component delete functionality with D shortcut in component view
13
+ - Add O shortcut to open datasheet (replaces D)
14
+ - Add library regenerate command to regenerate all installed components
15
+ - Add validation suite for footprint/symbol comparison (jlc validate)
16
+ - Enable 3D models by default during installation
17
+ - Remove non-functional S/F/M shortcuts from component view
18
+
19
+ Core:
20
+ - Add symbol text parsing and rendering for connector components
21
+ - Add arc interpolation for SOLIDREGION polygon paths (improves complex footprint accuracy)
22
+ - Add remove() method to library service for deleting components
23
+
24
+ Patch Changes
25
+
26
+ Core:
27
+ - Strip temperature prefix from component descriptions
28
+ - Improve hole detection and polygon pad handling in footprint converter
29
+
30
+ CLI:
31
+ - Fix async handling in renderApp exit
32
+ - Simplify loading indicators
33
+
34
+ MCP:
35
+ - Fix typecheck errors in batch and library tools
36
+ - Correct validationData property paths
37
+ - Fix InstalledComponent property access
38
+
39
+ Validator:
40
+ - Align SVG reference extraction with KiCad converter
41
+
42
+ ### Patch Changes
43
+
44
+ - Updated dependencies []:
45
+ - @jlcpcb/core@0.3.0
46
+
3
47
  ## 0.2.0
4
48
 
5
49
  ### Minor Changes
@@ -452,22 +452,22 @@
452
452
  </div>
453
453
 
454
454
  <script>
455
- function EJ(Z){let $=[],J=0;while(J<Z.length){let K=Z[J];if(/\s/.test(K)){J++;continue}if(K==="("||K===")"){$.push(K),J++;continue}if(K==='"'){let V="";J++;while(J<Z.length)if(Z[J]==="\\"&&J+1<Z.length)V+=Z[J+1],J+=2;else if(Z[J]==='"'){J++;break}else V+=Z[J],J++;$.push(`"${V}"`);continue}let U="";while(J<Z.length&&!/[\s()]/.test(Z[J]))U+=Z[J],J++;if(U)$.push(U)}return $}function ZJ(Z){if(Z.length===0)return{expr:[],remaining:[]};let $=Z[0];if($==="("){let K=[],U=Z.slice(1);while(U.length>0&&U[0]!==")"){let{expr:V,remaining:j}=ZJ(U);K.push(V),U=j}if(U[0]===")")U=U.slice(1);return{expr:K,remaining:U}}if($===")")return{expr:[],remaining:Z.slice(1)};let J=$;if(J.startsWith('"')&&J.endsWith('"'))J=J.slice(1,-1);return{expr:J,remaining:Z.slice(1)}}function d(Z){let $=EJ(Z),{expr:J}=ZJ($);return J}function A(Z){return Array.isArray(Z)}function X(Z){return typeof Z==="string"}function T(Z){if(A(Z)&&Z.length>0&&X(Z[0]))return Z[0];return}function z(Z,$){if(!A(Z))return;for(let J of Z)if(A(J)&&T(J)===$)return J;return}function I(Z,$){if(!A(Z))return[];let J=[];for(let K of Z)if(A(K)&&T(K)===$)J.push(K);return J}function y(Z,$){let J=z(Z,$);if(J&&J.length>=2&&X(J[1]))return J[1];return}function g(Z,$){let J=y(Z,$);if(J!==void 0){let K=parseFloat(J);if(!isNaN(K))return K}return}function _(Z,$){let J=z(Z,$);if(J&&J.length>=3){let K=parseFloat(X(J[1])?J[1]:""),U=parseFloat(X(J[2])?J[2]:"");if(!isNaN(K)&&!isNaN(U))return{x:K,y:U}}return}function c(Z,$){let J=z(Z,$);if(J&&J.length>=3){let K=parseFloat(X(J[1])?J[1]:""),U=parseFloat(X(J[2])?J[2]:"");if(!isNaN(K)&&!isNaN(U)){let V={x:K,y:U};if(J.length>=4&&X(J[3])){let j=parseFloat(J[3]);if(!isNaN(j))V.rotation=j}return V}}return}function o(Z){let $=z(Z,"size");if($&&$.length>=3){let J=parseFloat(X($[1])?$[1]:""),K=parseFloat(X($[2])?$[2]:"");if(!isNaN(J)&&!isNaN(K))return{width:J,height:K}}return}function P(Z){let $=z(Z,"stroke");if(!$)return;let J=g($,"width")??0.254,K=y($,"type")??"default";return{width:J,type:K}}function L(Z){let $=z(Z,"fill");if(!$)return;let J=y($,"type");if(J)return J;if($.length>=2&&X($[1]))return $[1];return}function u(Z){let $=z(Z,"pts");if(!$)return[];let J=[];for(let K of $)if(A(K)&&T(K)==="xy"&&K.length>=3){let U=parseFloat(X(K[1])?K[1]:""),V=parseFloat(X(K[2])?K[2]:"");if(!isNaN(U)&&!isNaN(V))J.push({x:U,y:V})}return J}function v(Z){let $=y(Z,"layer");if($)return[$];let J=z(Z,"layers");if(!J)return[];let K=[];for(let U=1;U<J.length;U++){let V=J[U];if(X(V))K.push(V)}return K}var B={background:"#FFFFF8",body:"#840000",bodyFill:"#FFFFC4",pin:"#840000",pinText:"#008484",text:"#000000"},w={background:"#000000",fCu:"#840000",bCu:"#008400",in1Cu:"#C2C200",in2Cu:"#C200C2",fSilkS:"#008484",bSilkS:"#840084",fMask:"#840084",bMask:"#848400",fPaste:"#840000",bPaste:"#00C2C2",fFab:"#848484",bFab:"#000084",fCrtYd:"#C2C2C2",bCrtYd:"#848484",edgeCuts:"#C2C200",dwgsUser:"#C2C2C2",cmtsUser:"#848484",margin:"#C200C2",padFront:"#840000",padBack:"#008400",padThruHole:"#C2C200",drill:"#848484"},f=10;function b(Z){return Z.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function a(Z){if(!Z||Z.trim()==="")return S("No symbol data");try{let $=d(Z);if(!A($)||T($)!=="symbol")return S("Invalid symbol format");let J=[],K={minX:1/0,maxX:-1/0,minY:1/0,maxY:-1/0},U=I($,"symbol");for(let D of U){for(let H of I(D,"rectangle")){let W=XJ(H,K);if(W)J.push(W)}for(let H of I(D,"polyline")){let W=wJ(H,K);if(W)J.push(W)}for(let H of I(D,"circle")){let W=MJ(H,K);if(W)J.push(W)}for(let H of I(D,"arc")){let W=IJ(H,K);if(W)J.push(W)}for(let H of I(D,"pin")){let W=YJ(H,K);if(W)J.push(W)}}let V=5;if(!isFinite(K.minX))K.minX=-20,K.maxX=20,K.minY=-20,K.maxY=20;let j=(K.maxX-K.minX+V*2)*f,Q=(K.maxY-K.minY+V*2)*f;return`<svg xmlns="http://www.w3.org/2000/svg" viewBox="${`${(K.minX-V)*f} ${(-K.maxY-V)*f} ${j} ${Q}`}" width="100%" height="100%" style="background-color: ${B.background}">
456
- <g transform="scale(${f}, ${-f})">
455
+ function EJ(Z){let $=[],J=0;while(J<Z.length){let K=Z[J];if(/\s/.test(K)){J++;continue}if(K==="("||K===")"){$.push(K),J++;continue}if(K==='"'){let V="";J++;while(J<Z.length)if(Z[J]==="\\"&&J+1<Z.length)V+=Z[J+1],J+=2;else if(Z[J]==='"'){J++;break}else V+=Z[J],J++;$.push(`"${V}"`);continue}let U="";while(J<Z.length&&!/[\s()]/.test(Z[J]))U+=Z[J],J++;if(U)$.push(U)}return $}function ZJ(Z){if(Z.length===0)return{expr:[],remaining:[]};let $=Z[0];if($==="("){let K=[],U=Z.slice(1);while(U.length>0&&U[0]!==")"){let{expr:V,remaining:j}=ZJ(U);K.push(V),U=j}if(U[0]===")")U=U.slice(1);return{expr:K,remaining:U}}if($===")")return{expr:[],remaining:Z.slice(1)};let J=$;if(J.startsWith('"')&&J.endsWith('"'))J=J.slice(1,-1);return{expr:J,remaining:Z.slice(1)}}function d(Z){let $=EJ(Z),{expr:J}=ZJ($);return J}function B(Z){return Array.isArray(Z)}function X(Z){return typeof Z==="string"}function v(Z){if(B(Z)&&Z.length>0&&X(Z[0]))return Z[0];return}function z(Z,$){if(!B(Z))return;for(let J of Z)if(B(J)&&v(J)===$)return J;return}function I(Z,$){if(!B(Z))return[];let J=[];for(let K of Z)if(B(K)&&v(K)===$)J.push(K);return J}function y(Z,$){let J=z(Z,$);if(J&&J.length>=2&&X(J[1]))return J[1];return}function b(Z,$){let J=y(Z,$);if(J!==void 0){let K=parseFloat(J);if(!isNaN(K))return K}return}function _(Z,$){let J=z(Z,$);if(J&&J.length>=3){let K=parseFloat(X(J[1])?J[1]:""),U=parseFloat(X(J[2])?J[2]:"");if(!isNaN(K)&&!isNaN(U))return{x:K,y:U}}return}function c(Z,$){let J=z(Z,$);if(J&&J.length>=3){let K=parseFloat(X(J[1])?J[1]:""),U=parseFloat(X(J[2])?J[2]:"");if(!isNaN(K)&&!isNaN(U)){let V={x:K,y:U};if(J.length>=4&&X(J[3])){let j=parseFloat(J[3]);if(!isNaN(j))V.rotation=j}return V}}return}function o(Z){let $=z(Z,"size");if($&&$.length>=3){let J=parseFloat(X($[1])?$[1]:""),K=parseFloat(X($[2])?$[2]:"");if(!isNaN(J)&&!isNaN(K))return{width:J,height:K}}return}function P(Z){let $=z(Z,"stroke");if(!$)return;let J=b($,"width")??0.254,K=y($,"type")??"default";return{width:J,type:K}}function f(Z){let $=z(Z,"fill");if(!$)return;let J=y($,"type");if(J)return J;if($.length>=2&&X($[1]))return $[1];return}function u(Z){let $=z(Z,"pts");if(!$)return[];let J=[];for(let K of $)if(B(K)&&v(K)==="xy"&&K.length>=3){let U=parseFloat(X(K[1])?K[1]:""),V=parseFloat(X(K[2])?K[2]:"");if(!isNaN(U)&&!isNaN(V))J.push({x:U,y:V})}return J}function R(Z){let $=y(Z,"layer");if($)return[$];let J=z(Z,"layers");if(!J)return[];let K=[];for(let U=1;U<J.length;U++){let V=J[U];if(X(V))K.push(V)}return K}var A={background:"#FFFFF8",body:"#840000",bodyFill:"#FFFFC4",pin:"#840000",pinText:"#008484",text:"#000000"},w={background:"#000000",fCu:"#840000",bCu:"#008400",in1Cu:"#C2C200",in2Cu:"#C200C2",fSilkS:"#008484",bSilkS:"#840084",fMask:"#840084",bMask:"#848400",fPaste:"#840000",bPaste:"#00C2C2",fFab:"#848484",bFab:"#000084",fCrtYd:"#C2C2C2",bCrtYd:"#848484",edgeCuts:"#C2C200",dwgsUser:"#C2C2C2",cmtsUser:"#848484",margin:"#C200C2",padFront:"#840000",padBack:"#008400",padThruHole:"#C2C200",drill:"#848484"},S=10;function g(Z){return Z.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function a(Z){if(!Z||Z.trim()==="")return T("No symbol data");try{let $=d(Z);if(B($)&&v($)==="kicad_symbol_lib"){let D=I($,"symbol");if(D.length===0)return T("No symbol found in library");$=D[0]}if(!B($)||v($)!=="symbol")return T("Invalid symbol format");let J=[],K={minX:1/0,maxX:-1/0,minY:1/0,maxY:-1/0},U=I($,"symbol");for(let D of U){for(let H of I(D,"rectangle")){let W=XJ(H,K);if(W)J.push(W)}for(let H of I(D,"polyline")){let W=wJ(H,K);if(W)J.push(W)}for(let H of I(D,"circle")){let W=MJ(H,K);if(W)J.push(W)}for(let H of I(D,"arc")){let W=IJ(H,K);if(W)J.push(W)}for(let H of I(D,"pin")){let W=YJ(H,K);if(W)J.push(W)}}let V=5;if(!isFinite(K.minX))K.minX=-20,K.maxX=20,K.minY=-20,K.maxY=20;let j=(K.maxX-K.minX+V*2)*S,Q=(K.maxY-K.minY+V*2)*S;return`<svg xmlns="http://www.w3.org/2000/svg" viewBox="${`${(K.minX-V)*S} ${(-K.maxY-V)*S} ${j} ${Q}`}" width="100%" height="100%" style="background-color: ${A.background}">
456
+ <g transform="scale(${S}, ${-S})">
457
457
  ${J.join(`
458
458
  `)}
459
459
  </g>
460
- </svg>`}catch($){return S(`Parse error: ${$}`)}}function XJ(Z,$){let J=_(Z,"start"),K=_(Z,"end");if(!J||!K)return"";let U=P(Z),j=L(Z)==="background"?B.bodyFill:"none";E($,J.x,J.y),E($,K.x,K.y);let Q=Math.min(J.x,K.x),G=Math.min(J.y,K.y),D=Math.abs(K.x-J.x),H=Math.abs(K.y-J.y);return`<rect x="${Q}" y="${G}" width="${D}" height="${H}" fill="${j}" stroke="${B.body}" stroke-width="${U?.width??0.254}"/>`}function wJ(Z,$){let J=u(Z);if(J.length<2)return"";let K=P(Z),V=L(Z)==="background"?B.bodyFill:"none";return`<polyline points="${J.map((Q)=>{return E($,Q.x,Q.y),`${Q.x},${Q.y}`}).join(" ")}" fill="${V}" stroke="${B.body}" stroke-width="${K?.width??0.254}" stroke-linecap="round" stroke-linejoin="round"/>`}function MJ(Z,$){let J=_(Z,"center"),K=g(Z,"radius");if(!J||K===void 0)return"";let U=P(Z),j=L(Z)==="background"?B.bodyFill:"none";return E($,J.x-K,J.y-K),E($,J.x+K,J.y+K),`<circle cx="${J.x}" cy="${J.y}" r="${K}" fill="${j}" stroke="${B.body}" stroke-width="${U?.width??0.254}"/>`}function IJ(Z,$){let J=_(Z,"start"),K=_(Z,"mid"),U=_(Z,"end");if(!J||!K||!U)return"";let V=P(Z);return E($,J.x,J.y),E($,K.x,K.y),E($,U.x,U.y),`<path d="${jJ(J,K,U)}" fill="none" stroke="${B.body}" stroke-width="${V?.width??0.254}" stroke-linecap="round"/>`}function jJ(Z,$,J){let{x:K,y:U}=Z,V=$.x,j=$.y,Q=J.x,G=J.y,D=2*(K*(j-G)+V*(G-U)+Q*(U-j));if(Math.abs(D)<0.0001)return`M ${Z.x} ${Z.y} L ${J.x} ${J.y}`;let H=((K*K+U*U)*(j-G)+(V*V+j*j)*(G-U)+(Q*Q+G*G)*(U-j))/D,W=((K*K+U*U)*(Q-V)+(V*V+j*j)*(K-Q)+(Q*Q+G*G)*(V-K))/D,q=Math.sqrt((K-H)**2+(U-W)**2),M=(V-K)*(G-U)-(j-U)*(Q-K)>0?0:1,N=0;return`M ${Z.x} ${Z.y} A ${q} ${q} 0 ${N} ${M} ${J.x} ${J.y}`}function YJ(Z,$){let J=c(Z,"at"),K=g(Z,"length")??2.54;if(!J)return"";let U=z(Z,"name"),V=z(Z,"number"),j=U&&U.length>=2&&X(U[1])?U[1]:"",Q=V&&V.length>=2&&X(V[1])?V[1]:"",G=J.rotation??0,D=G*Math.PI/180,H=J.x+K*Math.cos(D),W=J.y+K*Math.sin(D);E($,J.x,J.y),E($,H,W);let q=[];if(q.push(`<line x1="${J.x}" y1="${J.y}" x2="${H}" y2="${W}" stroke="${B.pin}" stroke-width="0.254"/>`),q.push(`<circle cx="${J.x}" cy="${J.y}" r="0.3" fill="${B.pin}"/>`),j&&j!=="~"){let M=H,N=W,k="start",F="middle";if(G===0)M=H+0.5,k="start";else if(G===180)M=H-0.5,k="end";else if(G===90)N=W+0.5,F="hanging",k="middle";else if(G===270)N=W-0.5,F="alphabetic",k="middle";q.push(`<text x="${M}" y="${N}" fill="${B.pinText}" font-size="1" font-family="sans-serif" text-anchor="${k}" dominant-baseline="${F}" transform="scale(1,-1) translate(0,${-2*N})">${b(j)}</text>`)}if(Q){let Y=(J.x+H)/2,M=(J.y+W)/2,N=G===0||G===180?0.8:0;q.push(`<text x="${Y}" y="${M+N}" fill="${B.pinText}" font-size="0.8" font-family="sans-serif" text-anchor="middle" dominant-baseline="middle" transform="scale(1,-1) translate(0,${-2*(M+N)})">${b(Q)}</text>`)}return q.join(`
461
- `)}function s(Z){if(!Z||Z.trim()==="")return S("No footprint data");try{let $=d(Z);if(!A($)||T($)!=="footprint")return S("Invalid footprint format");let J=[],K={minX:1/0,maxX:-1/0,minY:1/0,maxY:-1/0};for(let G of I($,"pad")){let D=NJ(G,K);if(D)J.push(D)}for(let G of I($,"fp_line")){let D=$J(G,K);if(D)J.push(D)}for(let G of I($,"fp_circle")){let D=KJ(G,K);if(D)J.push(D)}for(let G of I($,"fp_arc")){let D=UJ(G,K);if(D)J.push(D)}for(let G of I($,"fp_rect")){let D=DJ(G,K);if(D)J.push(D)}for(let G of I($,"fp_poly")){let D=VJ(G,K);if(D)J.push(D)}for(let G of I($,"gr_line")){let D=$J(G,K);if(D)J.push(D)}for(let G of I($,"gr_circle")){let D=KJ(G,K);if(D)J.push(D)}for(let G of I($,"gr_arc")){let D=UJ(G,K);if(D)J.push(D)}for(let G of I($,"gr_rect")){let D=DJ(G,K);if(D)J.push(D)}for(let G of I($,"gr_poly")){let D=VJ(G,K);if(D)J.push(D)}for(let G of I($,"fp_text")){let D=zJ(G,K);if(D)J.push(D)}let U=1;if(!isFinite(K.minX))K.minX=-5,K.maxX=5,K.minY=-5,K.maxY=5;let V=K.maxX-K.minX+U*2,j=K.maxY-K.minY+U*2;return`<svg xmlns="http://www.w3.org/2000/svg" viewBox="${`${K.minX-U} ${K.minY-U} ${V} ${j}`}" width="100%" height="100%" style="background-color: ${w.background}">
460
+ </svg>`}catch($){return T(`Parse error: ${$}`)}}function XJ(Z,$){let J=_(Z,"start"),K=_(Z,"end");if(!J||!K)return"";let U=P(Z),j=f(Z)==="background"?A.bodyFill:"none";E($,J.x,J.y),E($,K.x,K.y);let Q=Math.min(J.x,K.x),G=Math.min(J.y,K.y),D=Math.abs(K.x-J.x),H=Math.abs(K.y-J.y);return`<rect x="${Q}" y="${G}" width="${D}" height="${H}" fill="${j}" stroke="${A.body}" stroke-width="${U?.width??0.254}"/>`}function wJ(Z,$){let J=u(Z);if(J.length<2)return"";let K=P(Z),V=f(Z)==="background"?A.bodyFill:"none";return`<polyline points="${J.map((Q)=>{return E($,Q.x,Q.y),`${Q.x},${Q.y}`}).join(" ")}" fill="${V}" stroke="${A.body}" stroke-width="${K?.width??0.254}" stroke-linecap="round" stroke-linejoin="round"/>`}function MJ(Z,$){let J=_(Z,"center"),K=b(Z,"radius");if(!J||K===void 0)return"";let U=P(Z),j=f(Z)==="background"?A.bodyFill:"none";return E($,J.x-K,J.y-K),E($,J.x+K,J.y+K),`<circle cx="${J.x}" cy="${J.y}" r="${K}" fill="${j}" stroke="${A.body}" stroke-width="${U?.width??0.254}"/>`}function IJ(Z,$){let J=_(Z,"start"),K=_(Z,"mid"),U=_(Z,"end");if(!J||!K||!U)return"";let V=P(Z);return E($,J.x,J.y),E($,K.x,K.y),E($,U.x,U.y),`<path d="${jJ(J,K,U)}" fill="none" stroke="${A.body}" stroke-width="${V?.width??0.254}" stroke-linecap="round"/>`}function jJ(Z,$,J){let{x:K,y:U}=Z,V=$.x,j=$.y,Q=J.x,G=J.y,D=2*(K*(j-G)+V*(G-U)+Q*(U-j));if(Math.abs(D)<0.0001)return`M ${Z.x} ${Z.y} L ${J.x} ${J.y}`;let H=((K*K+U*U)*(j-G)+(V*V+j*j)*(G-U)+(Q*Q+G*G)*(U-j))/D,W=((K*K+U*U)*(Q-V)+(V*V+j*j)*(K-Q)+(Q*Q+G*G)*(V-K))/D,q=Math.sqrt((K-H)**2+(U-W)**2),M=(V-K)*(G-U)-(j-U)*(Q-K)>0?0:1,N=0;return`M ${Z.x} ${Z.y} A ${q} ${q} 0 ${N} ${M} ${J.x} ${J.y}`}function YJ(Z,$){let J=c(Z,"at"),K=b(Z,"length")??2.54;if(!J)return"";let U=z(Z,"name"),V=z(Z,"number"),j=U&&U.length>=2&&X(U[1])?U[1]:"",Q=V&&V.length>=2&&X(V[1])?V[1]:"",G=J.rotation??0,D=G*Math.PI/180,H=J.x+K*Math.cos(D),W=J.y+K*Math.sin(D);E($,J.x,J.y),E($,H,W);let q=[];if(q.push(`<line x1="${J.x}" y1="${J.y}" x2="${H}" y2="${W}" stroke="${A.pin}" stroke-width="0.254"/>`),q.push(`<circle cx="${J.x}" cy="${J.y}" r="0.3" fill="${A.pin}"/>`),j&&j!=="~"){let M=H,N=W,k="start",F="middle";if(G===0)M=H+0.5,k="start";else if(G===180)M=H-0.5,k="end";else if(G===90)N=W+0.5,F="hanging",k="middle";else if(G===270)N=W-0.5,F="alphabetic",k="middle";q.push(`<text x="${M}" y="${N}" fill="${A.pinText}" font-size="1" font-family="sans-serif" text-anchor="${k}" dominant-baseline="${F}" transform="scale(1,-1) translate(0,${-2*N})">${g(j)}</text>`)}if(Q){let Y=(J.x+H)/2,M=(J.y+W)/2,N=G===0||G===180?0.8:0;q.push(`<text x="${Y}" y="${M+N}" fill="${A.pinText}" font-size="0.8" font-family="sans-serif" text-anchor="middle" dominant-baseline="middle" transform="scale(1,-1) translate(0,${-2*(M+N)})">${g(Q)}</text>`)}return q.join(`
461
+ `)}function s(Z){if(!Z||Z.trim()==="")return T("No footprint data");try{let $=d(Z);if(!B($)||v($)!=="footprint")return T("Invalid footprint format");let J=[],K={minX:1/0,maxX:-1/0,minY:1/0,maxY:-1/0};for(let G of I($,"pad")){let D=NJ(G,K);if(D)J.push(D)}for(let G of I($,"fp_line")){let D=$J(G,K);if(D)J.push(D)}for(let G of I($,"fp_circle")){let D=KJ(G,K);if(D)J.push(D)}for(let G of I($,"fp_arc")){let D=UJ(G,K);if(D)J.push(D)}for(let G of I($,"fp_rect")){let D=DJ(G,K);if(D)J.push(D)}for(let G of I($,"fp_poly")){let D=VJ(G,K);if(D)J.push(D)}for(let G of I($,"gr_line")){let D=$J(G,K);if(D)J.push(D)}for(let G of I($,"gr_circle")){let D=KJ(G,K);if(D)J.push(D)}for(let G of I($,"gr_arc")){let D=UJ(G,K);if(D)J.push(D)}for(let G of I($,"gr_rect")){let D=DJ(G,K);if(D)J.push(D)}for(let G of I($,"gr_poly")){let D=VJ(G,K);if(D)J.push(D)}for(let G of I($,"fp_text")){let D=zJ(G,K);if(D)J.push(D)}let U=1;if(!isFinite(K.minX))K.minX=-5,K.maxX=5,K.minY=-5,K.maxY=5;let V=K.maxX-K.minX+U*2,j=K.maxY-K.minY+U*2;return`<svg xmlns="http://www.w3.org/2000/svg" viewBox="${`${K.minX-U} ${K.minY-U} ${V} ${j}`}" width="100%" height="100%" style="background-color: ${w.background}">
462
462
  <g>
463
463
  ${J.join(`
464
464
  `)}
465
465
  </g>
466
- </svg>`}catch($){return S(`Parse error: ${$}`)}}function C(Z){if(Z.includes("F.Cu"))return w.fCu;if(Z.includes("B.Cu"))return w.bCu;if(/In\d+\.Cu/.test(Z))return w.in1Cu;if(Z.includes("F.SilkS"))return w.fSilkS;if(Z.includes("B.SilkS"))return w.bSilkS;if(Z.includes("F.Mask"))return w.fMask;if(Z.includes("B.Mask"))return w.bMask;if(Z.includes("F.Paste"))return w.fPaste;if(Z.includes("B.Paste"))return w.bPaste;if(Z.includes("F.Fab"))return w.fFab;if(Z.includes("B.Fab"))return w.bFab;if(Z.includes("F.CrtYd"))return w.fCrtYd;if(Z.includes("B.CrtYd"))return w.bCrtYd;if(Z.includes("Edge.Cuts"))return w.edgeCuts;if(Z.includes("Dwgs.User"))return w.dwgsUser;if(Z.includes("Cmts.User"))return w.cmtsUser;if(Z.includes("Margin"))return w.margin;return w.fSilkS}function NJ(Z,$){if(!A(Z)||Z.length<4)return"";let J=X(Z[1])?Z[1]:"",K=X(Z[2])?Z[2]:"",U=X(Z[3])?Z[3]:"",V=c(Z,"at"),j=o(Z);if(!V||!j)return"";E($,V.x-j.width/2,V.y-j.height/2),E($,V.x+j.width/2,V.y+j.height/2);let Q=[],G=v(Z),D;if(K==="thru_hole"||K==="np_thru_hole")D=w.padThruHole;else if(G.some((q)=>q.includes("B.")))D=w.padBack;else D=w.padFront;let H=V.rotation??0,W=H!==0?` transform="rotate(${H}, ${V.x}, ${V.y})"`:"";if(U==="circle"){let q=Math.min(j.width,j.height)/2;Q.push(`<circle cx="${V.x}" cy="${V.y}" r="${q}" fill="${D}"${W}/>`)}else if(U==="oval"){let q=j.width/2,Y=j.height/2;Q.push(`<ellipse cx="${V.x}" cy="${V.y}" rx="${q}" ry="${Y}" fill="${D}"${W}/>`)}else if(U==="roundrect"){let q=g(Z,"roundrect_rratio")??0.25,Y=Math.min(j.width,j.height)*q/2,M=V.x-j.width/2,N=V.y-j.height/2;Q.push(`<rect x="${M}" y="${N}" width="${j.width}" height="${j.height}" rx="${Y}" fill="${D}"${W}/>`)}else if(U==="custom"){let q=z(Z,"primitives");if(q)for(let Y of I(q,"gr_poly")){let M=u(Y);if(M.length>=3){let N=M.map((k)=>{let F=V.x+k.x,m=V.y+k.y;return E($,F,m),`${F},${m}`}).join(" ");Q.push(`<polygon points="${N}" fill="${D}"${W}/>`)}}if(Q.length===0){let Y=V.x-j.width/2,M=V.y-j.height/2;Q.push(`<rect x="${Y}" y="${M}" width="${j.width}" height="${j.height}" fill="${D}"${W}/>`)}}else{let q=V.x-j.width/2,Y=V.y-j.height/2;Q.push(`<rect x="${q}" y="${Y}" width="${j.width}" height="${j.height}" fill="${D}"${W}/>`)}if(K==="thru_hole"||K==="np_thru_hole"){let q=z(Z,"drill");if(q&&q.length>=2)if(X(q[1])&&q[1]==="oval"&&q.length>=4){let M=parseFloat(X(q[2])?q[2]:"0"),N=parseFloat(X(q[3])?q[3]:"0");Q.push(`<ellipse cx="${V.x}" cy="${V.y}" rx="${M/2}" ry="${N/2}" fill="${w.drill}"${W}/>`)}else{let M=parseFloat(X(q[1])?q[1]:"0");Q.push(`<circle cx="${V.x}" cy="${V.y}" r="${M/2}" fill="${w.drill}"/>`)}}if(J){let q=Math.min(j.width,j.height)*0.5;Q.push(`<text x="${V.x}" y="${V.y}" fill="#FFFFFF" font-size="${q}" font-family="sans-serif" text-anchor="middle" dominant-baseline="central">${b(J)}</text>`)}return Q.join(`
467
- `)}function $J(Z,$){let J=_(Z,"start"),K=_(Z,"end");if(!J||!K)return"";let U=P(Z),j=v(Z)[0]??"F.SilkS";if(j.includes("CrtYd"))return"";let Q=C(j);return E($,J.x,J.y),E($,K.x,K.y),`<line x1="${J.x}" y1="${J.y}" x2="${K.x}" y2="${K.y}" stroke="${Q}" stroke-width="${U?.width??0.15}" stroke-linecap="round"/>`}function KJ(Z,$){let J=_(Z,"center"),K=_(Z,"end");if(!J||!K)return"";let U=Math.sqrt((K.x-J.x)**2+(K.y-J.y)**2),V=P(Z),Q=v(Z)[0]??"F.SilkS";if(Q.includes("CrtYd"))return"";let G=C(Q);return E($,J.x-U,J.y-U),E($,J.x+U,J.y+U),`<circle cx="${J.x}" cy="${J.y}" r="${U}" fill="none" stroke="${G}" stroke-width="${V?.width??0.15}"/>`}function UJ(Z,$){let J=_(Z,"start"),K=_(Z,"mid"),U=_(Z,"end");if(!J||!K||!U)return"";let V=P(Z),Q=v(Z)[0]??"F.SilkS";if(Q.includes("CrtYd"))return"";let G=C(Q);return E($,J.x,J.y),E($,K.x,K.y),E($,U.x,U.y),`<path d="${jJ(J,K,U)}" fill="none" stroke="${G}" stroke-width="${V?.width??0.15}" stroke-linecap="round"/>`}function VJ(Z,$){let J=u(Z);if(J.length<3)return"";let U=v(Z)[0]??"F.Cu";if(U.includes("CrtYd"))return"";let V=C(U),j=P(Z),Q=L(Z),G=Q==="solid"||Q==="yes"?V:"none",D=J.map((q)=>{return E($,q.x,q.y),`${q.x},${q.y}`}).join(" "),H=j?.width??0,W=H>0?` stroke="${V}" stroke-width="${H}"`:"";return`<polygon points="${D}" fill="${G}"${W}/>`}function DJ(Z,$){let J=_(Z,"start"),K=_(Z,"end");if(!J||!K)return"";let V=v(Z)[0]??"F.SilkS";if(V.includes("CrtYd"))return"";let j=C(V),Q=P(Z),D=L(Z)==="solid"?j:"none";E($,J.x,J.y),E($,K.x,K.y);let H=Math.min(J.x,K.x),W=Math.min(J.y,K.y),q=Math.abs(K.x-J.x),Y=Math.abs(K.y-J.y);return`<rect x="${H}" y="${W}" width="${q}" height="${Y}" fill="${D}" stroke="${j}" stroke-width="${Q?.width??0.15}"/>`}function zJ(Z,$){if(!A(Z)||Z.length<3)return"";let J=X(Z[1])?Z[1]:"",K=X(Z[2])?Z[2]:"";if(J==="reference"||J==="value")return"";let U=c(Z,"at");if(!U)return"";let j=v(Z)[0]??"F.SilkS",Q=C(j),G=z(Z,"effects"),D=1,H="middle";if(G){let N=z(G,"font");if(N){let F=o(N);if(F)D=F.height}let k=z(G,"justify");if(k)for(let F=1;F<k.length;F++){let m=X(k[F])?k[F]:"";if(m==="left")H="start";else if(m==="right")H="end"}}let W=K.length*D*0.6,q=D;if(H==="start")E($,U.x,U.y-q/2),E($,U.x+W,U.y+q/2);else if(H==="end")E($,U.x-W,U.y-q/2),E($,U.x,U.y+q/2);else E($,U.x-W/2,U.y-q/2),E($,U.x+W/2,U.y+q/2);let Y=U.rotation??0,M=Y!==0?` transform="rotate(${Y}, ${U.x}, ${U.y})"`:"";return`<text x="${U.x}" y="${U.y}" dy="0.35em" fill="${Q}" font-size="${D}" font-family="sans-serif" text-anchor="${H}"${M}>${b(K)}</text>`}function E(Z,$,J){Z.minX=Math.min(Z.minX,$),Z.maxX=Math.max(Z.maxX,$),Z.minY=Math.min(Z.minY,J),Z.maxY=Math.max(Z.maxY,J)}function S(Z){return`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 100" width="100%" height="100%">
466
+ </svg>`}catch($){return T(`Parse error: ${$}`)}}function C(Z){if(Z.includes("F.Cu"))return w.fCu;if(Z.includes("B.Cu"))return w.bCu;if(/In\d+\.Cu/.test(Z))return w.in1Cu;if(Z.includes("F.SilkS"))return w.fSilkS;if(Z.includes("B.SilkS"))return w.bSilkS;if(Z.includes("F.Mask"))return w.fMask;if(Z.includes("B.Mask"))return w.bMask;if(Z.includes("F.Paste"))return w.fPaste;if(Z.includes("B.Paste"))return w.bPaste;if(Z.includes("F.Fab"))return w.fFab;if(Z.includes("B.Fab"))return w.bFab;if(Z.includes("F.CrtYd"))return w.fCrtYd;if(Z.includes("B.CrtYd"))return w.bCrtYd;if(Z.includes("Edge.Cuts"))return w.edgeCuts;if(Z.includes("Dwgs.User"))return w.dwgsUser;if(Z.includes("Cmts.User"))return w.cmtsUser;if(Z.includes("Margin"))return w.margin;return w.fSilkS}function NJ(Z,$){if(!B(Z)||Z.length<4)return"";let J=X(Z[1])?Z[1]:"",K=X(Z[2])?Z[2]:"",U=X(Z[3])?Z[3]:"",V=c(Z,"at"),j=o(Z);if(!V||!j)return"";E($,V.x-j.width/2,V.y-j.height/2),E($,V.x+j.width/2,V.y+j.height/2);let Q=[],G=R(Z),D;if(K==="thru_hole"||K==="np_thru_hole")D=w.padThruHole;else if(G.some((q)=>q.includes("B.")))D=w.padBack;else D=w.padFront;let H=V.rotation??0,W=H!==0?` transform="rotate(${H}, ${V.x}, ${V.y})"`:"";if(U==="circle"){let q=Math.min(j.width,j.height)/2;Q.push(`<circle cx="${V.x}" cy="${V.y}" r="${q}" fill="${D}"${W}/>`)}else if(U==="oval"){let q=j.width/2,Y=j.height/2;Q.push(`<ellipse cx="${V.x}" cy="${V.y}" rx="${q}" ry="${Y}" fill="${D}"${W}/>`)}else if(U==="roundrect"){let q=b(Z,"roundrect_rratio")??0.25,Y=Math.min(j.width,j.height)*q/2,M=V.x-j.width/2,N=V.y-j.height/2;Q.push(`<rect x="${M}" y="${N}" width="${j.width}" height="${j.height}" rx="${Y}" fill="${D}"${W}/>`)}else if(U==="custom"){let q=z(Z,"primitives");if(q)for(let Y of I(q,"gr_poly")){let M=u(Y);if(M.length>=3){let N=M.map((k)=>{let F=V.x+k.x,m=V.y+k.y;return E($,F,m),`${F},${m}`}).join(" ");Q.push(`<polygon points="${N}" fill="${D}"${W}/>`)}}if(Q.length===0){let Y=V.x-j.width/2,M=V.y-j.height/2;Q.push(`<rect x="${Y}" y="${M}" width="${j.width}" height="${j.height}" fill="${D}"${W}/>`)}}else{let q=V.x-j.width/2,Y=V.y-j.height/2;Q.push(`<rect x="${q}" y="${Y}" width="${j.width}" height="${j.height}" fill="${D}"${W}/>`)}if(K==="thru_hole"||K==="np_thru_hole"){let q=z(Z,"drill");if(q&&q.length>=2)if(X(q[1])&&q[1]==="oval"&&q.length>=4){let M=parseFloat(X(q[2])?q[2]:"0"),N=parseFloat(X(q[3])?q[3]:"0");Q.push(`<ellipse cx="${V.x}" cy="${V.y}" rx="${M/2}" ry="${N/2}" fill="${w.drill}"${W}/>`)}else{let M=parseFloat(X(q[1])?q[1]:"0");Q.push(`<circle cx="${V.x}" cy="${V.y}" r="${M/2}" fill="${w.drill}"/>`)}}if(J){let q=Math.min(j.width,j.height)*0.5;Q.push(`<text x="${V.x}" y="${V.y}" fill="#FFFFFF" font-size="${q}" font-family="sans-serif" text-anchor="middle" dominant-baseline="central">${g(J)}</text>`)}return Q.join(`
467
+ `)}function $J(Z,$){let J=_(Z,"start"),K=_(Z,"end");if(!J||!K)return"";let U=P(Z),j=R(Z)[0]??"F.SilkS";if(j.includes("CrtYd"))return"";let Q=C(j);return E($,J.x,J.y),E($,K.x,K.y),`<line x1="${J.x}" y1="${J.y}" x2="${K.x}" y2="${K.y}" stroke="${Q}" stroke-width="${U?.width??0.15}" stroke-linecap="round"/>`}function KJ(Z,$){let J=_(Z,"center"),K=_(Z,"end");if(!J||!K)return"";let U=Math.sqrt((K.x-J.x)**2+(K.y-J.y)**2),V=P(Z),Q=R(Z)[0]??"F.SilkS";if(Q.includes("CrtYd"))return"";let G=C(Q);return E($,J.x-U,J.y-U),E($,J.x+U,J.y+U),`<circle cx="${J.x}" cy="${J.y}" r="${U}" fill="none" stroke="${G}" stroke-width="${V?.width??0.15}"/>`}function UJ(Z,$){let J=_(Z,"start"),K=_(Z,"mid"),U=_(Z,"end");if(!J||!K||!U)return"";let V=P(Z),Q=R(Z)[0]??"F.SilkS";if(Q.includes("CrtYd"))return"";let G=C(Q);return E($,J.x,J.y),E($,K.x,K.y),E($,U.x,U.y),`<path d="${jJ(J,K,U)}" fill="none" stroke="${G}" stroke-width="${V?.width??0.15}" stroke-linecap="round"/>`}function VJ(Z,$){let J=u(Z);if(J.length<3)return"";let U=R(Z)[0]??"F.Cu";if(U.includes("CrtYd"))return"";let V=C(U),j=P(Z),Q=f(Z),G=Q==="solid"||Q==="yes"?V:"none",D=J.map((q)=>{return E($,q.x,q.y),`${q.x},${q.y}`}).join(" "),H=j?.width??0,W=H>0?` stroke="${V}" stroke-width="${H}"`:"";return`<polygon points="${D}" fill="${G}"${W}/>`}function DJ(Z,$){let J=_(Z,"start"),K=_(Z,"end");if(!J||!K)return"";let V=R(Z)[0]??"F.SilkS";if(V.includes("CrtYd"))return"";let j=C(V),Q=P(Z),D=f(Z)==="solid"?j:"none";E($,J.x,J.y),E($,K.x,K.y);let H=Math.min(J.x,K.x),W=Math.min(J.y,K.y),q=Math.abs(K.x-J.x),Y=Math.abs(K.y-J.y);return`<rect x="${H}" y="${W}" width="${q}" height="${Y}" fill="${D}" stroke="${j}" stroke-width="${Q?.width??0.15}"/>`}function zJ(Z,$){if(!B(Z)||Z.length<3)return"";let J=X(Z[1])?Z[1]:"",K=X(Z[2])?Z[2]:"";if(J==="reference"||J==="value")return"";let U=c(Z,"at");if(!U)return"";let j=R(Z)[0]??"F.SilkS",Q=C(j),G=z(Z,"effects"),D=1,H="middle";if(G){let N=z(G,"font");if(N){let F=o(N);if(F)D=F.height}let k=z(G,"justify");if(k)for(let F=1;F<k.length;F++){let m=X(k[F])?k[F]:"";if(m==="left")H="start";else if(m==="right")H="end"}}let W=K.length*D*0.6,q=D;if(H==="start")E($,U.x,U.y-q/2),E($,U.x+W,U.y+q/2);else if(H==="end")E($,U.x-W,U.y-q/2),E($,U.x,U.y+q/2);else E($,U.x-W/2,U.y-q/2),E($,U.x+W/2,U.y+q/2);let Y=U.rotation??0,M=Y!==0?` transform="rotate(${Y}, ${U.x}, ${U.y})"`:"";return`<text x="${U.x}" y="${U.y}" dy="0.35em" fill="${Q}" font-size="${D}" font-family="sans-serif" text-anchor="${H}"${M}>${g(K)}</text>`}function E(Z,$,J){Z.minX=Math.min(Z.minX,$),Z.maxX=Math.max(Z.maxX,$),Z.minY=Math.min(Z.minY,J),Z.maxY=Math.max(Z.maxY,J)}function T(Z){return`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 100" width="100%" height="100%">
468
468
  <rect width="200" height="100" fill="#FFF0F0"/>
469
- <text x="100" y="50" fill="#CC0000" font-size="12" font-family="sans-serif" text-anchor="middle" dominant-baseline="central">${b(Z)}</text>
470
- </svg>`}var JJ=1,h="",WJ="user",i=!1,n=null,O=document.getElementById("search-input"),GJ=document.getElementById("source-select"),_J=document.getElementById("search-btn"),e=document.getElementById("results-grid"),r=document.getElementById("pagination"),FJ=document.getElementById("loading"),l=document.getElementById("preview-modal"),p=document.getElementById("modal-content"),kJ=document.getElementById("modal-close");function qJ(){let Z=window.__INITIAL_QUERY__;if(Z)O.value=Z,h=Z,R();else{let J=new URLSearchParams(window.location.search).get("q");if(J)O.value=J,h=J,R()}O.addEventListener("input",BJ),O.addEventListener("keydown",($)=>{if($.key==="Enter")$.preventDefault(),R()}),_J.addEventListener("click",R),GJ.addEventListener("change",()=>{if(WJ=GJ.value,h)R()}),kJ.addEventListener("click",t),l.addEventListener("click",($)=>{if($.target===l)t()}),document.addEventListener("keydown",($)=>{if($.key==="Escape")t()})}function BJ(){if(n)clearTimeout(n);n=window.setTimeout(()=>{let Z=O.value.trim();if(Z&&Z!==h)h=Z,JJ=1,R()},300)}async function R(){let Z=O.value.trim();if(!Z||i)return;h=Z,i=!0,QJ(!0);try{let $=new URLSearchParams({q:Z,source:WJ,page:String(JJ),limit:"20"}),J=await fetch(`/api/search?${$}`);if(!J.ok)throw Error("Search failed");let K=await J.json();AJ(K.results),RJ(K.pagination)}catch($){console.error("Search error:",$),e.innerHTML='<div class="error">Search failed. Please try again.</div>'}finally{i=!1,QJ(!1)}}function AJ(Z){if(Z.length===0){e.innerHTML='<div class="no-results">No components found. Try a different search term.</div>';return}e.innerHTML=Z.map(($)=>`
469
+ <text x="100" y="50" fill="#CC0000" font-size="12" font-family="sans-serif" text-anchor="middle" dominant-baseline="central">${g(Z)}</text>
470
+ </svg>`}var JJ=1,h="",WJ="user",i=!1,n=null,O=document.getElementById("search-input"),GJ=document.getElementById("source-select"),_J=document.getElementById("search-btn"),e=document.getElementById("results-grid"),r=document.getElementById("pagination"),FJ=document.getElementById("loading"),l=document.getElementById("preview-modal"),p=document.getElementById("modal-content"),kJ=document.getElementById("modal-close");function qJ(){let Z=window.__INITIAL_QUERY__;if(Z)O.value=Z,h=Z,L();else{let J=new URLSearchParams(window.location.search).get("q");if(J)O.value=J,h=J,L()}O.addEventListener("input",BJ),O.addEventListener("keydown",($)=>{if($.key==="Enter")$.preventDefault(),L()}),_J.addEventListener("click",L),GJ.addEventListener("change",()=>{if(WJ=GJ.value,h)L()}),kJ.addEventListener("click",t),l.addEventListener("click",($)=>{if($.target===l)t()}),document.addEventListener("keydown",($)=>{if($.key==="Escape")t()})}function BJ(){if(n)clearTimeout(n);n=window.setTimeout(()=>{let Z=O.value.trim();if(Z&&Z!==h)h=Z,JJ=1,L()},300)}async function L(){let Z=O.value.trim();if(!Z||i)return;h=Z,i=!0,QJ(!0);try{let $=new URLSearchParams({q:Z,source:WJ,page:String(JJ),limit:"20"}),J=await fetch(`/api/search?${$}`);if(!J.ok)throw Error("Search failed");let K=await J.json();AJ(K.results),vJ(K.pagination)}catch($){console.error("Search error:",$),e.innerHTML='<div class="error">Search failed. Please try again.</div>'}finally{i=!1,QJ(!1)}}function AJ(Z){if(Z.length===0){e.innerHTML='<div class="no-results">No components found. Try a different search term.</div>';return}e.innerHTML=Z.map(($)=>`
471
471
  <div class="card" data-uuid="${$.uuid}">
472
472
  <div class="card-images">
473
473
  <div class="image-container symbol-container" data-uuid="${$.uuid}">
@@ -491,7 +491,7 @@
491
491
  </button>
492
492
  </div>
493
493
  </div>
494
- `).join(""),document.querySelectorAll(".copy-btn").forEach(($)=>{$.addEventListener("click",(J)=>{J.stopPropagation();let K=$.dataset.uuid;if(K)HJ(K,$)})}),document.querySelectorAll(".image-container").forEach(($)=>{$.addEventListener("click",async()=>{let J=$.dataset.uuid;if(J)vJ(J)})}),Z.forEach(($)=>PJ($.uuid))}async function PJ(Z){let $=document.querySelector(`.symbol-container[data-uuid="${Z}"]`),J=document.querySelector(`.footprint-container[data-uuid="${Z}"]`);if(!$&&!J)return;try{let K=await fetch(`/api/component/${Z}`);if(!K.ok){if($)$.innerHTML='<div class="no-preview">Error</div><div class="image-label">Symbol</div>';if(J)J.innerHTML='<div class="no-preview">Error</div><div class="image-label">Footprint</div>';return}let U=await K.json();if($)if(U.symbolSexpr){let V=a(U.symbolSexpr);$.innerHTML=V+'<div class="image-label">Symbol</div>'}else $.innerHTML='<div class="no-preview">No preview</div><div class="image-label">Symbol</div>';if(J)if(U.footprintSexpr){let V=s(U.footprintSexpr);J.innerHTML=V+'<div class="image-label">Footprint</div>'}else J.innerHTML='<div class="no-preview">No preview</div><div class="image-label">Footprint</div>'}catch{if($)$.innerHTML='<div class="no-preview">Error</div><div class="image-label">Symbol</div>';if(J)J.innerHTML='<div class="no-preview">Error</div><div class="image-label">Footprint</div>'}}async function vJ(Z){l.classList.remove("hidden"),p.innerHTML='<div class="modal-loading">Loading...</div>';try{let $=await fetch(`/api/component/${Z}`);if(!$.ok)throw Error("Failed to fetch component");let J=await $.json(),K=J.symbolSexpr?a(J.symbolSexpr):"",U=J.footprintSexpr?s(J.footprintSexpr):"";p.innerHTML=`
494
+ `).join(""),document.querySelectorAll(".copy-btn").forEach(($)=>{$.addEventListener("click",(J)=>{J.stopPropagation();let K=$.dataset.uuid;if(K)HJ(K,$)})}),document.querySelectorAll(".image-container").forEach(($)=>{$.addEventListener("click",async()=>{let J=$.dataset.uuid;if(J)RJ(J)})}),Z.forEach(($)=>PJ($.uuid))}async function PJ(Z){let $=document.querySelector(`.symbol-container[data-uuid="${Z}"]`),J=document.querySelector(`.footprint-container[data-uuid="${Z}"]`);if(!$&&!J)return;try{let K=await fetch(`/api/component/${Z}`);if(!K.ok){if($)$.innerHTML='<div class="no-preview">Error</div><div class="image-label">Symbol</div>';if(J)J.innerHTML='<div class="no-preview">Error</div><div class="image-label">Footprint</div>';return}let U=await K.json();if($)if(U.symbolSexpr){let V=a(U.symbolSexpr);$.innerHTML=V+'<div class="image-label">Symbol</div>'}else $.innerHTML='<div class="no-preview">No preview</div><div class="image-label">Symbol</div>';if(J)if(U.footprintSexpr){let V=s(U.footprintSexpr);J.innerHTML=V+'<div class="image-label">Footprint</div>'}else J.innerHTML='<div class="no-preview">No preview</div><div class="image-label">Footprint</div>'}catch{if($)$.innerHTML='<div class="no-preview">Error</div><div class="image-label">Symbol</div>';if(J)J.innerHTML='<div class="no-preview">Error</div><div class="image-label">Footprint</div>'}}async function RJ(Z){l.classList.remove("hidden"),p.innerHTML='<div class="modal-loading">Loading...</div>';try{let $=await fetch(`/api/component/${Z}`);if(!$.ok)throw Error("Failed to fetch component");let J=await $.json(),K=J.symbolSexpr?a(J.symbolSexpr):"",U=J.footprintSexpr?s(J.footprintSexpr):"";p.innerHTML=`
495
495
  <div class="modal-header">
496
496
  <h2>${x(J.title)}</h2>
497
497
  <div class="modal-uuid">
@@ -513,7 +513,7 @@
513
513
  <p><strong>Description:</strong> ${x(J.description||"N/A")}</p>
514
514
  ${J.model3d?"<p><strong>3D Model:</strong> Available</p>":""}
515
515
  </div>
516
- `;let V=p.querySelector(".modal-copy");if(V)V.addEventListener("click",()=>{HJ(J.uuid,V)})}catch($){console.error("Modal error:",$),p.innerHTML='<div class="modal-error">Failed to load component details</div>'}}function t(){l.classList.add("hidden")}function RJ(Z){if(Z.totalPages<=1){r.innerHTML="";return}r.innerHTML=`
516
+ `;let V=p.querySelector(".modal-copy");if(V)V.addEventListener("click",()=>{HJ(J.uuid,V)})}catch($){console.error("Modal error:",$),p.innerHTML='<div class="modal-error">Failed to load component details</div>'}}function t(){l.classList.add("hidden")}function vJ(Z){if(Z.totalPages<=1){r.innerHTML="";return}r.innerHTML=`
517
517
  <button class="page-btn" ${Z.hasPrev?"":"disabled"} data-page="${Z.page-1}">
518
518
  ← Prev
519
519
  </button>
@@ -521,7 +521,7 @@
521
521
  <button class="page-btn" ${Z.hasNext?"":"disabled"} data-page="${Z.page+1}">
522
522
  Next →
523
523
  </button>
524
- `,r.querySelectorAll(".page-btn").forEach(($)=>{$.addEventListener("click",()=>{let J=parseInt($.dataset.page||"1",10);if(J>0)JJ=J,R(),window.scrollTo(0,0)})})}async function HJ(Z,$){try{await navigator.clipboard.writeText(Z),$.classList.add("copied"),setTimeout(()=>$.classList.remove("copied"),1500)}catch{let J=document.createElement("input");J.value=Z,document.body.appendChild(J),J.select(),document.execCommand("copy"),document.body.removeChild(J),$.classList.add("copied"),setTimeout(()=>$.classList.remove("copied"),1500)}}function QJ(Z){FJ.classList.toggle("hidden",!Z)}function x(Z){let $=document.createElement("div");return $.textContent=Z,$.innerHTML}if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",qJ);else qJ();
524
+ `,r.querySelectorAll(".page-btn").forEach(($)=>{$.addEventListener("click",()=>{let J=parseInt($.dataset.page||"1",10);if(J>0)JJ=J,L(),window.scrollTo(0,0)})})}async function HJ(Z,$){try{await navigator.clipboard.writeText(Z),$.classList.add("copied"),setTimeout(()=>$.classList.remove("copied"),1500)}catch{let J=document.createElement("input");J.value=Z,document.body.appendChild(J),J.select(),document.execCommand("copy"),document.body.removeChild(J),$.classList.add("copied"),setTimeout(()=>$.classList.remove("copied"),1500)}}function QJ(Z){FJ.classList.toggle("hidden",!Z)}function x(Z){let $=document.createElement("div");return $.textContent=Z,$.innerHTML}if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",qJ);else qJ();
525
525
 
526
526
  </script>
527
527
  </body>