// lib/convert-easyeda-json-to-tscircuit-soup-json.ts
import {
  any_source_component,
  pcb_smtpad,
  pcb_silkscreen_path,
  pcb_plated_hole,
  pcb_hole,
  pcb_via
} from "circuit-json";
import * as Soup from "circuit-json";

// lib/math/arc-utils.ts
function generateArcFromSweep(startX, startY, endX, endY, radius, largeArcFlag, sweepFlag) {
  const start = { x: startX, y: startY };
  const end = { x: endX, y: endY };
  const midX = (startX + endX) / 2;
  const midY = (startY + endY) / 2;
  const dx = endX - startX;
  const dy = endY - startY;
  const distance = Math.sqrt(dx * dx + dy * dy);
  if (distance === 0 || radius < distance / 2) {
    return [start, end];
  }
  const h = Math.sqrt(radius * radius - distance * distance / 4);
  const angle = Math.atan2(dy, dx);
  const centerX = midX + h * Math.sin(angle) * (sweepFlag ? -1 : 1);
  const centerY = midY - h * Math.cos(angle) * (sweepFlag ? -1 : 1);
  const startAngle = Math.atan2(startY - centerY, startX - centerX);
  let endAngle = Math.atan2(endY - centerY, endX - centerX);
  if (sweepFlag && endAngle > startAngle) {
    endAngle -= 2 * Math.PI;
  } else if (!sweepFlag && endAngle < startAngle) {
    endAngle += 2 * Math.PI;
  }
  if (!largeArcFlag && Math.abs(endAngle - startAngle) > Math.PI || largeArcFlag && Math.abs(endAngle - startAngle) < Math.PI) {
    if (endAngle > startAngle) {
      endAngle -= 2 * Math.PI;
    } else {
      endAngle += 2 * Math.PI;
    }
  }
  const numPoints = Math.max(
    2,
    Math.ceil(Math.abs(endAngle - startAngle) * radius)
  );
  const path2 = [];
  for (let i = 0; i <= numPoints; i++) {
    const t = i / numPoints;
    const angle2 = startAngle + t * (endAngle - startAngle);
    const x = centerX + radius * Math.cos(angle2);
    const y = centerY + radius * Math.sin(angle2);
    path2.push({ x, y });
  }
  return path2;
}

// lib/convert-easyeda-json-to-tscircuit-soup-json.ts
import {
  findBoundsAndCenter,
  transformPCBElements
} from "@tscircuit/circuit-json-util";
import { compose, scale, translate, applyToPoint } from "transformation-matrix";
import { mm } from "@tscircuit/mm";

// lib/utils/easyeda-unit-to-mm.ts
var mil10ToMm = (value) => value * 10 * 0.0254;

// lib/convert-easyeda-json-to-tscircuit-soup-json.ts
import { normalizePinLabels } from "@tscircuit/core";

// lib/constants.ts
var DEFAULT_PCB_THICKNESS_MM = 1.6;

// lib/utils/normalize-symbol-name.ts
var normalizeSymbolName = (name) => {
  const trimmedName = name.trim();
  if (trimmedName === "+") return "_POS";
  if (trimmedName === "-") return "_NEG";
  return trimmedName;
};

// lib/convert-easyeda-json-to-tscircuit-soup-json.ts
var mil2mm = (mil) => {
  if (typeof mil === "number") return mm(`${mil}mil`);
  if (mil.match(/^\d+$/)) return mm(`${mil}mil`);
  return mm(mil);
};
var milx10 = (mil10) => {
  if (typeof mil10 === "number") return mil2mm(mil10) * 10;
  if (mil10.match(/^\d+$/)) return mil2mm(mil10) * 10;
  return mil2mm(mil10);
};
var parseCadOffsetsFromSvgNode = (svgNode) => {
  const attrs = svgNode?.svgData?.attrs ?? {};
  const [cx, cy] = String(attrs.c_origin ?? "0,0").split(",").map((s) => Number(s.trim()));
  const zStr = attrs.z ?? 0;
  const z_mm = typeof zStr === "string" && /[a-z]/i.test(zStr) ? mm(zStr) : mm(`${Number(zStr) || 0}mil`);
  return {
    position: {
      x: mil10ToMm(Number.isNaN(cx) ? 0 : cx),
      y: mil10ToMm(Number.isNaN(cy) ? 0 : cy),
      z: Math.max(0, -z_mm)
      // EasyEDA uses negative up; make it positive up
    },
    rotation: (() => {
      const [rx, ry, rz] = (attrs.c_rotation ?? "0,0,0").split(",").map(Number);
      return { x: rx || 0, y: ry || 0, z: rz || 0 };
    })()
  };
};
var readModelHeightMm = (raw) => {
  const fallback = 3.5;
  if (raw == null) return fallback;
  const n = Number(raw);
  if (!Number.isFinite(n)) return fallback;
  const mmFromMil10 = mil10ToMm(n);
  const mmFromMil = mm(`${n}mil`);
  const upper = 12;
  const lower = 0.1;
  let chosen = Math.max(mmFromMil10, mmFromMil);
  if (chosen > upper || chosen < lower) chosen = fallback;
  return chosen;
};
var handleSilkscreenPath = (track, index) => {
  return pcb_silkscreen_path.parse({
    type: "pcb_silkscreen_path",
    pcb_silkscreen_path_id: `pcb_silkscreen_path_${index + 1}`,
    pcb_component_id: "pcb_component_1",
    layer: "top",
    // Assuming all silkscreen is on top layer
    route: track.points.map((point) => ({
      x: milx10(point.x),
      y: milx10(point.y)
    })),
    stroke_width: mil10ToMm(track.width)
  });
};
var handleSilkscreenArc = (arc, index) => {
  const arcPath = generateArcFromSweep(
    arc.start.x,
    arc.start.y,
    arc.end.x,
    arc.end.y,
    arc.radiusX,
    arc.largeArc,
    arc.sweepDirection === "CW"
  );
  return pcb_silkscreen_path.parse({
    type: "pcb_silkscreen_path",
    pcb_silkscreen_path_id: `pcb_silkscreen_arc_${index + 1}`,
    pcb_component_id: "pcb_component_1",
    layer: "top",
    // Assuming all silkscreen is on top layer
    route: arcPath.map((p) => ({
      x: milx10(p.x),
      y: milx10(p.y)
    })),
    stroke_width: mil10ToMm(arc.width)
  });
};
var handleHole = (hole, index) => {
  return pcb_hole.parse({
    type: "pcb_hole",
    x: milx10(hole.center.x),
    y: milx10(hole.center.y),
    hole_diameter: milx10(hole.radius) * 2,
    hole_shape: "circle",
    pcb_hole_id: `pcb_hole_${index + 1}`
  });
};
var handleHoleCutout = (hole, index) => {
  return Soup.pcb_cutout.parse({
    type: "pcb_cutout",
    pcb_cutout_id: `pcb_cutout_from_hole_${index + 1}`,
    shape: "circle",
    center: { x: milx10(hole.center.x), y: milx10(hole.center.y) },
    radius: milx10(hole.radius)
  });
};
var handleVia = (via, index) => {
  return pcb_via.parse({
    type: "pcb_via",
    pcb_via_id: `pcb_via_${index + 1}`,
    x: milx10(via.center.x),
    y: milx10(via.center.y),
    outer_diameter: milx10(via.outerDiameter),
    hole_diameter: milx10(via.holeDiameter),
    layers: ["top", "bottom"]
  });
};
var handleCutout = (solidRegion, index) => {
  return Soup.pcb_cutout.parse({
    type: "pcb_cutout",
    pcb_cutout_id: `pcb_cutout_${index + 1}`,
    shape: "polygon",
    points: solidRegion.points.map((p) => ({
      x: milx10(p.x),
      y: milx10(p.y)
    }))
  });
};
var convertEasyEdaJsonToCircuitJson = (easyEdaJson, { useModelCdn, shouldRecenter = true } = {}) => {
  const circuitElements = [];
  const source_component = any_source_component.parse({
    type: "source_component",
    source_component_id: "source_component_1",
    name: "U1",
    ftype: "simple_chip"
  });
  const pcb_component2 = Soup.pcb_component.parse({
    type: "pcb_component",
    pcb_component_id: "pcb_component_1",
    source_component_id: "source_component_1",
    name: "U1",
    ftype: "simple_chip",
    width: 0,
    // we update this at the end
    height: 0,
    // we update this at the end
    rotation: 0,
    center: { x: 0, y: 0 },
    layer: "top"
  });
  circuitElements.push(source_component, pcb_component2);
  const pads = easyEdaJson.packageDetail.dataStr.shape.filter(
    (shape) => shape.type === "PAD"
  );
  const pins = easyEdaJson.dataStr.shape.filter((shape) => shape.type === "PIN");
  const pinLabelSets = pads.map((pad) => {
    const labels = [];
    if (pad.number) labels.push(pad.number.toString());
    const pin = pins.find((p) => p.pinNumber === pad.number);
    if (pin) labels.push(normalizeSymbolName(pin.label));
    return labels;
  });
  const normalizedPinLabels = normalizePinLabels(pinLabelSets);
  pads.forEach((pad, index) => {
    const portHints = normalizedPinLabels[index];
    const pinNumber = Number.parseInt(
      portHints.find((hint) => hint.match(/pin\d+/)).replace("pin", "")
    );
    circuitElements.push({
      type: "source_port",
      source_port_id: `source_port_${index + 1}`,
      source_component_id: "source_component_1",
      name: `pin${pinNumber}`,
      pin_number: pinNumber,
      port_hints: portHints.filter((hint) => hint !== `pin${pinNumber}`)
    });
    if (pad.holeRadius !== void 0 && mil2mm(pad.holeRadius) !== 0) {
      const platedHoleId = pad.shape === "RECT" && pad.rotation !== void 0 ? `pcb_plated_hole_${index + 1}_rot${pad.rotation}` : `pcb_plated_hole_${index + 1}`;
      const commonPlatedHoleProps = {
        type: "pcb_plated_hole",
        pcb_plated_hole_id: platedHoleId,
        x: mil2mm(pad.center.x),
        y: mil2mm(pad.center.y),
        layers: ["top"],
        port_hints: [`pin${pinNumber}`],
        pcb_component_id: "pcb_component_1",
        pcb_port_id: `pcb_port_${index + 1}`
      };
      let additionalPlatedHoleProps;
      if (pad.shape === "OVAL") {
        const largestOuterDimensionName = mil2mm(pad.width) > mil2mm(pad.height) ? "width" : "height";
        const smallestOuterDimension = Math.min(
          mil2mm(pad.width),
          mil2mm(pad.height)
        );
        const largestOuterDimension = Math.max(
          mil2mm(pad.width),
          mil2mm(pad.height)
        );
        const distanceFromOuterPlatingToHole = smallestOuterDimension / 2 - mil2mm(pad.holeRadius);
        const largestInnerDimension = largestOuterDimension - distanceFromOuterPlatingToHole * 2;
        const smallestInnerDimension = mil2mm(pad.holeRadius) * 2;
        const innerWidth = largestOuterDimensionName === "width" ? largestInnerDimension : smallestInnerDimension;
        const innerHeight = largestOuterDimensionName === "height" ? largestInnerDimension : smallestInnerDimension;
        additionalPlatedHoleProps = {
          shape: "pill_hole_with_rect_pad",
          hole_shape: "pill",
          pad_shape: "rect",
          hole_width: innerWidth,
          hole_height: innerHeight,
          rect_pad_width: mil2mm(pad.width),
          rect_pad_height: mil2mm(pad.height),
          hole_offset_x: 0,
          hole_offset_y: 0
        };
      } else if (pad.shape === "RECT") {
        const padWidth = mil2mm(pad.width);
        const padHeight = mil2mm(pad.height);
        const holeRadius = mil2mm(pad.holeRadius);
        const holeDiameter = holeRadius * 2;
        const aspectRatio = Math.max(padWidth, padHeight) / Math.min(padWidth, padHeight);
        const isSignificantlyRectangular = aspectRatio > 1.5;
        if (isSignificantlyRectangular) {
          const baseWidth = holeDiameter;
          const pillHeight = baseWidth * 2.6;
          const holeWidth = padWidth > padHeight ? pillHeight : baseWidth;
          const holeHeight = padHeight > padWidth ? pillHeight : baseWidth;
          additionalPlatedHoleProps = {
            shape: "rotated_pill_hole_with_rect_pad",
            hole_shape: "rotated_pill",
            pad_shape: "rect",
            hole_width: holeWidth,
            hole_height: holeHeight,
            hole_ccw_rotation: pad.rotation || 0,
            rect_ccw_rotation: pad.rotation || 0,
            rect_pad_width: padWidth,
            rect_pad_height: padHeight
          };
        } else {
          additionalPlatedHoleProps = {
            shape: "circle",
            hole_diameter: holeDiameter,
            outer_diameter: Math.max(padWidth, padHeight),
            radius: holeDiameter / 2
          };
        }
      } else {
        additionalPlatedHoleProps = {
          shape: "circle",
          hole_diameter: mil2mm(pad.holeRadius) * 2,
          outer_diameter: mil2mm(pad.width),
          radius: mil2mm(pad.holeRadius)
        };
      }
      circuitElements.push(
        pcb_plated_hole.parse({
          ...commonPlatedHoleProps,
          ...additionalPlatedHoleProps
        })
      );
    } else {
      let soupShape;
      if (pad.shape === "RECT") {
        soupShape = "rect";
      } else if (pad.shape === "ELLIPSE") {
        soupShape = "rect";
      } else if (pad.shape === "OVAL") {
        soupShape = "rect";
      } else if (pad.shape === "POLYGON") {
        soupShape = "polygon";
      }
      if (!soupShape) {
        throw new Error(`unknown pad.shape: "${pad.shape}"`);
      }
      const rectSize = { width: mil2mm(pad.width), height: mil2mm(pad.height) };
      if (pad.rotation === 90 || pad.rotation === 270) {
        rectSize.width = mil2mm(pad.height);
        rectSize.height = mil2mm(pad.width);
      }
      const parsedPcbSmtpad = pcb_smtpad.parse({
        type: "pcb_smtpad",
        pcb_smtpad_id: `pcb_smtpad_${index + 1}`,
        shape: soupShape,
        ...soupShape !== "polygon" && {
          x: mil2mm(pad.center.x),
          y: mil2mm(pad.center.y)
        },
        ...soupShape === "rect" ? rectSize : soupShape === "polygon" && pad.points ? {
          points: pad.points.map((p) => ({
            x: milx10(p.x),
            y: milx10(p.y)
          }))
        } : { radius: Math.min(mil2mm(pad.width), mil2mm(pad.height)) / 2 },
        layer: "top",
        port_hints: [`pin${pinNumber}`],
        pcb_component_id: "pcb_component_1",
        pcb_port_id: `pcb_port_${index + 1}`
      });
      circuitElements.push(parsedPcbSmtpad);
    }
  });
  easyEdaJson.packageDetail.dataStr.shape.filter(
    (shape) => shape.type === "HOLE"
  ).forEach((h, index) => {
    circuitElements.push(handleHole(h, index));
    circuitElements.push(handleHoleCutout(h, index));
  });
  easyEdaJson.packageDetail.dataStr.shape.filter((shape) => shape.type === "VIA").forEach((v, index) => {
    circuitElements.push(handleVia(v, index));
  });
  easyEdaJson.packageDetail.dataStr.shape.filter(
    (shape) => shape.type === "SOLIDREGION" && shape.fillStyle === "cutout"
  ).forEach((sr, index) => {
    circuitElements.push(handleCutout(sr, index));
  });
  easyEdaJson.packageDetail.dataStr.shape.forEach((shape, index) => {
    if (shape.type === "TRACK") {
      circuitElements.push(handleSilkscreenPath(shape, index));
    } else if (shape.type === "ARC") {
      circuitElements.push(handleSilkscreenArc(shape, index));
    } else if (shape.type === "TEXT") {
      circuitElements.push(
        Soup.pcb_silkscreen_text.parse({
          type: "pcb_silkscreen_text",
          pcb_silkscreen_text_id: `pcb_silkscreen_text_${index + 1}`,
          pcb_component_id: "pcb_component_1",
          text: normalizeSymbolName(shape.text),
          anchor_position: {
            x: mil2mm(shape.x),
            y: mil2mm(shape.y)
          },
          anchor_alignment: {
            L: "bottom_left",
            C: "center",
            R: "bottom_right"
          }[shape.textAnchor ?? "L"],
          font_size: shape.size_mm ? shape.size_mm : void 0,
          layer: "top"
        })
      );
    }
  });
  const pcbElements = circuitElements.filter(
    (e) => e.type === "pcb_smtpad" || e.type === "pcb_plated_hole" || e.type === "pcb_hole" || e.type === "pcb_via" || e.type === "pcb_silkscreen_path" || e.type === "pcb_silkscreen_text"
  );
  if (pcbElements.length > 0) {
    const bounds = findBoundsAndCenter(pcbElements);
    pcb_component2.width = bounds.width;
    pcb_component2.height = bounds.height;
  }
  const svgNode = easyEdaJson.packageDetail.dataStr.shape.find(
    (a) => Boolean(a.type === "SVGNODE" && a.svgData.attrs?.uuid)
  );
  const objFileUuid = svgNode?.svgData?.attrs?.uuid;
  const objFileUrl = objFileUuid ? useModelCdn ? `https://modelcdn.tscircuit.com/easyeda_models/download?uuid=${objFileUuid}&pn=${easyEdaJson.lcsc.number}` : `https://modules.easyeda.com/3dmodel/${objFileUuid}` : void 0;
  if (objFileUrl !== void 0) {
    const { position, rotation } = parseCadOffsetsFromSvgNode(svgNode);
    circuitElements.push(
      Soup.cad_component.parse({
        type: "cad_component",
        cad_component_id: "cad_component_1",
        source_component_id: "source_component_1",
        pcb_component_id: "pcb_component_1",
        position,
        rotation,
        model_obj_url: objFileUrl
      })
    );
  }
  if (shouldRecenter) {
    const elementsForBounds = circuitElements.filter(
      (e) => e.type !== "pcb_component" && e.type !== "cad_component"
    );
    const bounds = findBoundsAndCenter(elementsForBounds);
    if (Number.isFinite(bounds.center.x) && Number.isFinite(bounds.center.y)) {
      const matrix = compose(
        translate(-bounds.center.x, bounds.center.y),
        scale(1, -1)
      );
      const elementsForTransform = circuitElements.filter(
        (e) => !(e.type === "pcb_smtpad" && e.shape === "polygon") && e.type !== "cad_component"
      );
      transformPCBElements(elementsForTransform, matrix);
      for (const e of circuitElements) {
        if (e.type === "pcb_cutout") {
          if (e.shape === "polygon") {
            e.points = e.points.map((p) => applyToPoint(matrix, p));
          } else if (e.shape === "circle" || e.shape === "rect") {
            e.center = applyToPoint(matrix, e.center);
          } else if ("route" in e) {
            const cutoutPath = e;
            cutoutPath.route = cutoutPath.route.map(
              (p) => applyToPoint(matrix, p)
            );
          }
        } else if (e.type === "pcb_smtpad" && e.shape === "polygon") {
          e.points = e.points.map((p) => applyToPoint(matrix, p));
        }
      }
      const cad = circuitElements.find(
        (e) => e.type === "cad_component"
      );
      if (cad) {
        if (!cad.rotation) cad.rotation = { x: 0, y: 0, z: 0 };
        const p = applyToPoint(matrix, { x: cad.position.x, y: cad.position.y });
        cad.position.x = p.x;
        cad.position.y = p.y;
        const side = pcb_component2.layer ?? "top";
        const t = DEFAULT_PCB_THICKNESS_MM / 2;
        const attrs = svgNode?.svgData?.attrs ?? {};
        const modelHeight = readModelHeightMm(attrs.c_height);
        if (!cad.size) {
          cad.size = {
            x: pcb_component2.width,
            y: pcb_component2.height,
            z: modelHeight
          };
        }
        const ROTATE_X_FOR_YUP = 90;
        const originalZRotation = (cad.rotation.z ?? 0) % 360;
        if (Math.abs(originalZRotation - 0) < 1 || Math.abs(originalZRotation - 360) < 1) {
          cad.rotation.x = ((cad.rotation.x ?? 0) + 0 + 360) % 360;
        } else if (Math.abs(originalZRotation - 180) < 1) {
          cad.rotation.x = ((cad.rotation.x ?? 0) + 0 + 360) % 360;
        } else if (Math.abs(originalZRotation - 90) < 1) {
          cad.rotation.x = ((cad.rotation.x ?? 0) + 0 + 360) % 360;
        } else if (Math.abs(originalZRotation - 270) < 1) {
          cad.rotation.x = ((cad.rotation.x ?? 0) + ROTATE_X_FOR_YUP + 360) % 360;
          cad.rotation.y = ((cad.rotation.y ?? 0) + 90 + 360) % 360;
        } else {
          console.warn(
            `[3D] Unusual rotation angle: ${originalZRotation}\xB0 for component ${easyEdaJson.lcsc.number}`
          );
          cad.rotation.x = ((cad.rotation.x ?? 0) + ROTATE_X_FOR_YUP + 360) % 360;
        }
        if (side !== "top") {
          cad.rotation.x = ((cad.rotation.x ?? 0) + 180) % 360;
        }
        const USE_Z_OFFSET_FOR_180 = Math.abs(originalZRotation - 180) < 1;
        const zOffRaw = cad.position.z ?? 0;
        const zOff = USE_Z_OFFSET_FOR_180 ? -zOffRaw : 0;
        const rx = (cad.rotation.x % 360 + 360) % 360;
        let thicknessAlongWorldZ;
        const is180RotatedYUp = (Math.abs(originalZRotation - 180) < 1 || Math.abs(originalZRotation - 0) < 1 || Math.abs(originalZRotation - 360) < 1) && Math.abs(rx) < 1;
        if (is180RotatedYUp) {
          thicknessAlongWorldZ = cad.size.y;
        } else if (rx % 180 === 90) {
          thicknessAlongWorldZ = cad.size.y;
        } else {
          thicknessAlongWorldZ = cad.size.z;
        }
        let centerZ;
        if (is180RotatedYUp) {
          centerZ = side === "top" ? t - thicknessAlongWorldZ / 2 : -t + thicknessAlongWorldZ / 2;
        } else {
          centerZ = side === "top" ? t + zOff + thicknessAlongWorldZ / 2 : -t - zOff - thicknessAlongWorldZ / 2;
        }
        cad.position.z = centerZ;
      }
    }
    pcb_component2.center = { x: 0, y: 0 };
  }
  return circuitElements;
};
var convertEasyEdaJsonToTscircuitSoupJson = convertEasyEdaJsonToCircuitJson;

// lib/websafe/fetch-easyeda-json.ts
async function fetchEasyEDAComponent(jlcpcbPartNumber, { fetch: fetch2 = globalThis.fetch } = {}) {
  const searchUrl = "https://easyeda.com/api/components/search";
  const componentUrl = (uuid) => `https://easyeda.com/api/components/${uuid}?version=6.4.7&uuid=${uuid}&datastrid=`;
  const searchHeaders = {
    authority: "easyeda.com",
    pragma: "no-cache",
    "cache-control": "no-cache",
    accept: "application/json, text/javascript, */*; q=0.01",
    "x-requested-with": "XMLHttpRequest",
    "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36",
    "content-type": "application/x-www-form-urlencoded; charset=UTF-8",
    origin: "https://easyeda.com",
    "sec-fetch-site": "same-origin",
    "sec-fetch-mode": "cors",
    "sec-fetch-dest": "empty",
    referer: "https://easyeda.com/editor",
    "accept-language": "cs,en;q=0.9,sk;q=0.8,en-GB;q=0.7",
    cookie: "<PUT your cookies here>"
  };
  const searchData = `type=3&doctype%5B%5D=2&uid=0819f05c4eef4c71ace90d822a990e87&returnListStyle=classifyarr&wd=${jlcpcbPartNumber}&version=6.4.7`;
  const searchResponse = await fetch2(searchUrl, {
    method: "POST",
    headers: searchHeaders,
    body: searchData
  });
  if (!searchResponse.ok) {
    throw new Error("Failed to search for the component");
  }
  const searchResult = await searchResponse.json();
  if (!searchResult.success || !searchResult.result.lists.lcsc.length) {
    throw new Error("Component not found");
  }
  const bestMatchComponent = searchResult.result.lists.lcsc.find(
    (component) => component.dataStr.head.c_para["Supplier Part"] === jlcpcbPartNumber
  ) ?? searchResult.result.lists.lcsc[0];
  const componentUUID = bestMatchComponent.uuid;
  const componentResponse = await fetch2(componentUrl(componentUUID), {
    method: "GET",
    headers: {
      ...searchHeaders,
      referer: `https://easyeda.com/editor?uuid=${componentUUID}`
    }
  });
  if (!componentResponse.ok) {
    throw new Error("Failed to fetch the component details");
  }
  const componentResult = await componentResponse.json();
  return componentResult.result;
}

// lib/schemas/easy-eda-json-schema.ts
import { z as z3 } from "zod";

// lib/schemas/package-detail-shape-schema.ts
import { z } from "zod";
var safeNumber = (defaultValue = 0) => z.union([z.number(), z.string()]).transform((val) => {
  const num = Number(val);
  return isNaN(num) ? defaultValue : num;
}).default(defaultValue);
var tenthmil = z.union([z.number(), z.string()]).optional().transform(
  (n) => typeof n === "string" && n.endsWith("mil") ? n : `${Number.parseFloat(n) * 10}mil`
).pipe(z.string());
var PointSchema = z.any().transform((p) => {
  if (Array.isArray(p)) {
    const [x, y] = p;
    return { x, y };
  }
  if (typeof p === "object") {
    return p;
  }
  throw new Error(`Invalid point: ${p}`);
}).pipe(
  z.object({
    x: z.number(),
    y: z.number()
  })
);
var BaseShapeSchema = z.object({
  type: z.string(),
  id: z.string().optional(),
  layer: z.coerce.number().optional()
});
var TrackSchema = BaseShapeSchema.extend({
  type: z.literal("TRACK"),
  width: z.coerce.number(),
  points: z.array(PointSchema)
});
var PadSchema = BaseShapeSchema.extend({
  type: z.literal("PAD"),
  shape: z.enum(["RECT", "ELLIPSE", "OVAL", "POLYGON"]),
  center: z.object({
    x: tenthmil,
    y: tenthmil
  }),
  width: tenthmil,
  height: tenthmil,
  layermask: z.number(),
  net: z.union([z.string(), z.number()]).optional(),
  number: z.union([z.string(), z.number()]),
  holeRadius: tenthmil,
  points: z.array(PointSchema).optional(),
  rotation: safeNumber(0),
  // Will default to 0 if NaN or missing
  plated: z.boolean()
});
var ArcSchema = BaseShapeSchema.extend({
  type: z.literal("ARC"),
  width: z.number(),
  start: PointSchema,
  end: PointSchema,
  radiusX: z.number(),
  radiusY: z.number(),
  largeArc: z.boolean(),
  sweepDirection: z.enum(["CW", "CCW"])
});
var CircleSchema = BaseShapeSchema.extend({
  type: z.literal("CIRCLE"),
  center: PointSchema,
  radius: z.number(),
  width: z.number()
});
var SolidRegionSchema = BaseShapeSchema.extend({
  type: z.literal("SOLIDREGION"),
  layermask: z.number(),
  points: z.array(PointSchema),
  fillStyle: z.string()
});
var SVGNodeSchema = BaseShapeSchema.extend({
  type: z.literal("SVGNODE"),
  svgData: z.object({
    gId: z.string(),
    nodeName: z.string(),
    nodeType: z.number(),
    layerid: z.string(),
    attrs: z.record(z.string(), z.string()),
    childNodes: z.array(z.unknown())
  })
});
var HoleSchema = BaseShapeSchema.extend({
  type: z.literal("HOLE"),
  center: PointSchema,
  radius: z.number()
});
var ViaSchema = BaseShapeSchema.extend({
  type: z.literal("VIA"),
  center: PointSchema,
  outerDiameter: z.number(),
  holeDiameter: z.number()
});
var RectSchema = BaseShapeSchema.extend({
  type: z.literal("RECT"),
  x: tenthmil,
  y: tenthmil,
  width: tenthmil,
  height: tenthmil,
  lineWidth: z.number(),
  fillStyle: z.string(),
  rotation: z.number().optional()
});
var TextSchema = BaseShapeSchema.extend({
  type: z.literal("TEXT"),
  text: z.string(),
  x: tenthmil,
  y: tenthmil,
  size_mm: z.number(),
  rotation: z.number().optional(),
  layer: z.number().optional(),
  textAnchor: z.enum(["L", "C", "R", ""]).optional().transform((val) => val === "" ? void 0 : val),
  font: z.string().optional()
});
var PackageDetailShapeSchema = z.discriminatedUnion("type", [
  TrackSchema,
  PadSchema,
  ArcSchema,
  CircleSchema,
  SolidRegionSchema,
  SVGNodeSchema,
  HoleSchema,
  ViaSchema,
  RectSchema,
  TextSchema
]);
var pairs = (arr) => {
  const pairs2 = [];
  for (let i = 0; i < arr.length; i += 2) {
    pairs2.push([arr[i], arr[i + 1]]);
  }
  return pairs2;
};
var parsePoints = (pointsStr) => pairs(
  pointsStr.trim().split(" ").map((n) => Number(n))
);
var ShapeItemSchema = z.object({
  type: z.string(),
  data: z.string()
}).transform((shape) => {
  switch (shape.type) {
    case "TRACK": {
      const [width, layer, _, pointsStr, id, _n] = shape.data.split("~");
      const points = parsePoints(pointsStr);
      return TrackSchema.parse({ type: "TRACK", width, layer, points, id });
    }
    case "PAD": {
      const [padShape, ...params] = shape.data.split("~");
      const [
        centerX,
        centerY,
        width,
        height,
        layermask,
        net,
        number,
        holeRadius,
        ...rest
      ] = params.map((p) => Number.isNaN(Number(p)) ? p : Number(p));
      const center = { x: centerX, y: centerY };
      let points;
      if (padShape === "RECT" || padShape === "POLYGON") {
        points = parsePoints(rest[0]);
      }
      const r = Number(rest[1]);
      const rotation = Number.isNaN(r) ? void 0 : r;
      const padInputParams = {
        type: "PAD",
        shape: padShape,
        center,
        width,
        height,
        layermask,
        net,
        number,
        holeRadius,
        points,
        rotation,
        plated: rest.includes("Y")
      };
      const pad = PadSchema.parse(padInputParams);
      return pad;
    }
    case "ARC": {
      const [width, layer, , arcData] = shape.data.split("~");
      const match = arcData.match(
        /M\s*([\d.-]+)(?:\s*,\s*|\s+)([\d.-]+)\s*A\s*([\d.-]+)(?:\s*,\s*|\s+)([\d.-]+)\s*([\d.-]+)\s*([\d.-]+)\s*([\d.-]+)\s*([\d.-]+)(?:\s*,\s*|\s+)([\d.-]+)/
      );
      if (!match) {
        throw new Error(`Invalid arc data: ${arcData}`);
      }
      const [
        ,
        startX,
        startY,
        radiusX,
        radiusY,
        xAxisRotation,
        largeArcFlag,
        sweepFlag,
        endX,
        endY
      ] = match;
      const start = [Number(startX), Number(startY)];
      const end = [Number(endX), Number(endY)];
      return ArcSchema.parse({
        type: "ARC",
        width: Number(width),
        layer: Number(layer),
        start,
        end,
        radiusX: Number(radiusX),
        radiusY: Number(radiusY),
        largeArc: largeArcFlag === "1",
        // sweepFlag=1 means clockwise (CW), sweepFlag=0 means counter-clockwise (CCW)
        sweepDirection: sweepFlag === "1" ? "CW" : "CCW"
      });
    }
    case "CIRCLE": {
      const [centerX, centerY, radius, width, layer, id] = shape.data.split("~");
      const center = [Number(centerX), Number(centerY)];
      return CircleSchema.parse({
        type: "CIRCLE",
        center,
        radius: Number(radius),
        width: Number(width),
        layer: Number(layer),
        id
      });
    }
    case "HOLE": {
      const [centerX, centerY, radius, id] = shape.data.split("~");
      const center = [Number(centerX), Number(centerY)];
      return HoleSchema.parse({
        type: "HOLE",
        center,
        radius: Number(radius),
        id
      });
    }
    case "VIA": {
      const [x, y, outerDiameter, , holeDiameter, id] = shape.data.split("~");
      const center = [Number(x), Number(y)];
      return ViaSchema.parse({
        type: "VIA",
        center,
        outerDiameter: Number(outerDiameter),
        holeDiameter: Number(holeDiameter),
        id
      });
    }
    case "SOLIDREGION": {
      const [layermask, , pathData, fillStyle, id] = shape.data.split("~");
      const points = pathData.match(/[ML] ?(-?[\d.]+)[ ,](-?[\d.]+)/g)?.map((point) => {
        const [, x, y] = point.match(/[ML]? ?(-?[\d.]+)[ ,](-?[\d.]+)/) || [];
        return [Number(x), Number(y)];
      }) || [];
      return SolidRegionSchema.parse({
        type: "SOLIDREGION",
        layermask: Number(layermask),
        points,
        fillStyle,
        id
      });
    }
    case "SVGNODE": {
      const svgData = JSON.parse(shape.data);
      return SVGNodeSchema.parse({ type: "SVGNODE", svgData });
    }
    case "RECT": {
      const [x, y, width, height, lineWidth, id, rotation, layer, fillStyle] = shape.data.split("~");
      const r = rotation ? Number(rotation) : void 0;
      return RectSchema.parse({
        type: "RECT",
        x,
        y,
        width,
        height,
        lineWidth: Number(lineWidth),
        id,
        rotation: Number.isNaN(r) ? void 0 : r,
        layer: layer ? Number(layer) : void 0,
        fillStyle: fillStyle || void 0
      });
    }
    case "TEXT": {
      const [textAnchor, x, y, size, layer, id, rotation, , font, text] = shape.data.split("~");
      const r = rotation ? Number(rotation) : void 0;
      return TextSchema.parse({
        type: "TEXT",
        text,
        x,
        y,
        size_mm: Number(size) * 2.54,
        // empirically this seems to match, C5248081 is a good test case
        layer: layer ? Number(layer) : void 0,
        id,
        rotation: Number.isNaN(r) ? void 0 : r,
        textAnchor,
        font: font || void 0
      });
    }
    default:
      throw new Error(`Unknown shape type: ${shape.type}`);
  }
}).pipe(PackageDetailShapeSchema);
var ShapesArraySchema = z.array(ShapeItemSchema);

// lib/schemas/single-letter-shape-schema.ts
import { z as z2 } from "zod";
var PointSchema2 = z2.object({
  x: z2.number(),
  y: z2.number()
});
var RectangleShapeOutputSchema = z2.object({
  type: z2.literal("RECTANGLE"),
  position: PointSchema2,
  width: z2.number(),
  height: z2.number(),
  color: z2.string(),
  lineWidth: z2.number(),
  id: z2.string()
});
var parseRectangle = (str) => {
  const [, x, y, , , width, height, color, lineWidth, , , id] = str.split("~");
  return {
    type: "RECTANGLE",
    position: { x: Number(x), y: Number(y) },
    width: Number(width),
    height: Number(height),
    color,
    lineWidth: Number(lineWidth),
    id
  };
};
var RectangleShapeSchema = z2.string().startsWith("R~").transform(parseRectangle).pipe(RectangleShapeOutputSchema);
var EllipseShapeOutputSchema = z2.object({
  type: z2.literal("ELLIPSE"),
  center: PointSchema2,
  radiusX: z2.number(),
  radiusY: z2.number(),
  color: z2.string(),
  lineWidth: z2.number(),
  id: z2.string()
});
var parseEllipse = (str) => {
  const [, x, y, radiusX, radiusY, color, lineWidth, , , id] = str.split("~");
  return {
    type: "ELLIPSE",
    center: { x: Number(x), y: Number(y) },
    radiusX: Number(radiusX),
    radiusY: Number(radiusY),
    color,
    lineWidth: Number(lineWidth),
    id
  };
};
var EllipseShapeSchema = z2.string().startsWith("E~").transform(parseEllipse).pipe(EllipseShapeOutputSchema);
var ArcShapeOutputSchema = z2.object({
  type: z2.literal("ARC"),
  start: PointSchema2,
  end: PointSchema2,
  radius: z2.number(),
  sweepFlag: z2.boolean(),
  color: z2.string(),
  lineWidth: z2.number(),
  id: z2.string()
});
var parseArc = (str) => {
  const [, pathData, color, lineWidth, , , id] = str.split("~");
  const parts = pathData.split(" ");
  const x1 = Number(parts[1]) || 0;
  const y1 = Number(parts[2]) || 0;
  const radius = Number(parts[4]) || 0;
  const sweepFlag = parts[7] === "1";
  const x2 = Number(parts[8]) || 0;
  const y2 = Number(parts[9]) || 0;
  const parsedLineWidth = Number(lineWidth);
  const finalLineWidth = Number.isNaN(parsedLineWidth) ? 1 : parsedLineWidth;
  return {
    type: "ARC",
    start: { x: x1, y: y1 },
    end: { x: x2, y: y2 },
    radius,
    sweepFlag,
    color: color || "#880000",
    // Default color
    lineWidth: finalLineWidth,
    id: id || "gge1"
  };
};
var ArcShapeSchema = z2.string().startsWith("A~").transform(parseArc).pipe(ArcShapeOutputSchema);
var PinShapeOutputSchema = z2.object({
  type: z2.literal("PIN"),
  visibility: z2.enum(["show", "hide", "none"]),
  pinNumber: z2.union([z2.string(), z2.number()]),
  x: z2.number(),
  y: z2.number(),
  rotation: z2.number(),
  id: z2.string(),
  label: z2.string(),
  labelColor: z2.string(),
  path: z2.string(),
  arrow: z2.string()
});
var parsePin = (pinString) => {
  const parts = pinString.split("~");
  const [, visibility, , pinNumber, x, y, rotation, id] = parts;
  const nameMatch = pinString.match(/~([\w+-]+)~(start|end)~/);
  let label = nameMatch ? nameMatch[1] : "";
  if (label.endsWith("+")) label = label.slice(0, -1) + "_POS";
  if (label.endsWith("-")) label = label.slice(0, -1) + "_NEG";
  if (/^\+\d+(?:\.\d+)?V$/i.test(label)) label = `V${label.slice(1, -1)}`;
  const colorMatch = pinString.match(/#[0-9A-F]{6}/);
  const labelColor = colorMatch ? colorMatch[0] : "";
  const pathMatch = pinString.match(/\^\^([^~]+)/);
  const path2 = pathMatch ? pathMatch[1] : "";
  const arrowMatch = pinString.match(/\^\^0~(.+)$/);
  const arrow = arrowMatch ? arrowMatch[1] : "";
  const r = Number.parseFloat(rotation);
  return {
    type: "PIN",
    visibility,
    id,
    pinNumber: Number.isNaN(Number(pinNumber)) ? pinNumber : Number(pinNumber),
    x: Number.parseFloat(x),
    y: Number.parseFloat(y),
    rotation: Number.isNaN(r) ? 0 : r,
    label,
    labelColor,
    path: path2,
    arrow
  };
};
var PinShapeSchema = z2.string().startsWith("P~").transform(parsePin).pipe(PinShapeOutputSchema);
var PolylineShapeOutputSchema = z2.object({
  type: z2.literal("POLYLINE"),
  points: z2.array(PointSchema2),
  color: z2.string(),
  lineWidth: z2.number(),
  id: z2.string()
});
var parsePoints2 = (pointsStr) => {
  return pointsStr.split(" ").reduce((acc, value, index) => {
    if (index % 2 === 0) {
      acc.push({ x: Number(value), y: 0 });
    } else {
      acc[acc.length - 1].y = Number(value);
    }
    return acc;
  }, []);
};
var parsePolyline = (str) => {
  const [, ...rest] = str.split("~");
  const [pointsStr, color, lineWidth, , , id] = rest;
  return {
    type: "POLYLINE",
    points: parsePoints2(pointsStr),
    color,
    lineWidth: Number(lineWidth),
    id
  };
};
var PolylineShapeSchema = z2.string().startsWith("PL~").transform(parsePolyline).pipe(PolylineShapeOutputSchema);
var PolygonShapeOutputSchema = z2.object({
  type: z2.literal("POLYGON"),
  points: z2.array(PointSchema2),
  fillColor: z2.string(),
  lineWidth: z2.number(),
  lineColor: z2.string(),
  id: z2.string()
});
var parsePolygon = (str) => {
  const [, ...rest] = str.split("~");
  const [pointsStr, fillColor, lineWidth, lineColor, , id] = rest;
  return {
    type: "POLYGON",
    points: parsePoints2(pointsStr),
    fillColor,
    lineWidth: Number(lineWidth),
    lineColor,
    id
  };
};
var PolygonShapeSchema = z2.string().startsWith("PG~").transform(parsePolygon).pipe(PolygonShapeOutputSchema);
var PathShapeOutputSchema = z2.object({
  type: z2.literal("PATH"),
  pathData: z2.string(),
  fillColor: z2.string(),
  strokeWidth: z2.number(),
  strokeColor: z2.string(),
  id: z2.string()
});
var parsePath = (str) => {
  const [, pathData, fillColor, strokeWidth, strokeColor, , id] = str.split("~");
  return {
    type: "PATH",
    pathData,
    fillColor,
    strokeWidth: mil10ToMm(Number(strokeWidth)),
    strokeColor,
    id
  };
};
var PathShapeSchema = z2.string().startsWith("PT~").transform(parsePath).pipe(PathShapeOutputSchema);
var TextShapeOutputSchema = z2.object({
  type: z2.literal("TEXT"),
  alignment: z2.enum(["L", "C", "R"]),
  x: z2.number(),
  y: z2.number(),
  rotation: z2.number(),
  fontColor: z2.string(),
  backgroundColor: z2.string().optional(),
  fontSize: z2.string(),
  fontWeight: z2.string().optional().default("normal"),
  fontStyle: z2.enum(["normal", "italic"]).optional().default("normal"),
  fontDecoration: z2.string().optional().default(""),
  content: z2.string(),
  textType: z2.string(),
  visibility: z2.enum(["0", "1"]),
  mirror: z2.string(),
  id: z2.string()
});
var parseText = (str) => {
  const [
    ,
    alignment,
    x,
    y,
    rotation,
    fontColor,
    backgroundColor,
    fontSize,
    fontWeight,
    fontStyle,
    fontDecoration,
    content,
    textType,
    visibility,
    mirror,
    id
  ] = str.split("~");
  return {
    type: "TEXT",
    alignment,
    x: Number(x),
    y: Number(y),
    rotation: Number(rotation),
    fontColor,
    backgroundColor: backgroundColor || void 0,
    fontSize,
    fontWeight: fontWeight || "normal",
    fontStyle: fontStyle || "normal",
    fontDecoration: fontDecoration || "",
    content,
    textType,
    visibility,
    mirror,
    id
  };
};
var TextShapeSchema = z2.string().startsWith("T~").transform(parseText).pipe(TextShapeOutputSchema);
var SingleLetterShapeSchema = z2.string().transform((x) => {
  if (x.startsWith("R~")) return RectangleShapeSchema.parse(x);
  if (x.startsWith("E~")) return EllipseShapeSchema.parse(x);
  if (x.startsWith("P~")) return PinShapeSchema.parse(x);
  if (x.startsWith("PL~")) return PolylineShapeSchema.parse(x);
  if (x.startsWith("PG~")) return PolygonShapeSchema.parse(x);
  if (x.startsWith("PT~")) return PathShapeSchema.parse(x);
  if (x.startsWith("T~")) return TextShapeSchema.parse(x);
  if (x.startsWith("A~")) return ArcShapeSchema.parse(x);
  throw new Error(`Invalid shape type: ${x}`);
}).pipe(
  z2.union([
    RectangleShapeOutputSchema,
    EllipseShapeOutputSchema,
    PinShapeOutputSchema,
    PolylineShapeOutputSchema,
    PolygonShapeOutputSchema,
    PathShapeOutputSchema,
    TextShapeOutputSchema,
    ArcShapeOutputSchema
  ])
);

// lib/schemas/easy-eda-json-schema.ts
var maybeNumber = z3.any().transform((k) => k === "nan" || Number.isNaN(k) ? null : k).pipe(z3.number().nullable().optional());
var SzlcscSchema = z3.object({
  id: z3.number(),
  number: z3.string(),
  step: z3.number().optional(),
  min: z3.number().optional(),
  price: z3.number().optional(),
  stock: z3.number().optional(),
  url: z3.string().url().optional(),
  image: z3.string().optional().optional()
});
var LcscSchema = z3.object({
  id: z3.number(),
  number: z3.string(),
  step: z3.number().optional(),
  min: z3.number().optional(),
  price: z3.number().optional(),
  stock: z3.number().optional(),
  url: z3.string().url().optional()
});
var OwnerSchema = z3.object({
  uuid: z3.string(),
  username: z3.string(),
  nickname: z3.string(),
  avatar: z3.string()
});
var HeadSchema = z3.object({
  docType: z3.preprocess((val) => val == null ? val : String(val), z3.string()),
  editorVersion: z3.string(),
  c_para: z3.record(z3.string(), z3.string().nullable()),
  x: z3.number(),
  y: z3.number(),
  puuid: z3.string().optional(),
  uuid: z3.string(),
  utime: z3.preprocess((val) => {
    if (val === "") return 0;
    if (typeof val === "string") return Number(val);
    return val;
  }, z3.number()),
  importFlag: z3.number().optional(),
  c_spiceCmd: z3.any().optional(),
  hasIdFlag: z3.boolean()
});
var BBoxSchema = z3.object({
  x: z3.number(),
  y: z3.number(),
  width: z3.number(),
  height: z3.number()
});
var LayerItemSchema = z3.object({
  name: z3.string(),
  color: z3.string(),
  visible: z3.boolean(),
  active: z3.boolean(),
  config: z3.boolean(),
  transparency: z3.boolean()
});
var ObjectItemSchema = z3.object({
  name: z3.string(),
  visible: z3.boolean(),
  locked: z3.boolean()
});
var DataStrSchema = z3.object({
  head: HeadSchema,
  canvas: z3.string(),
  shape: z3.array(SingleLetterShapeSchema),
  BBox: BBoxSchema,
  colors: z3.union([z3.array(z3.string()), z3.record(z3.string())])
});
var PackageDetailDataStrSchema = z3.object({
  head: HeadSchema,
  canvas: z3.string(),
  shape: z3.array(z3.string()).transform(
    (shapes) => shapes.map((shape) => {
      const [type, ...data] = shape.split("~");
      return ShapeItemSchema.parse({ type, data: data.join("~") });
    })
  ).pipe(z3.array(PackageDetailShapeSchema)),
  layers: z3.array(z3.string()).transform(
    (layers) => layers.map((layer) => {
      const [name, color, visible, active, config, transparency] = layer.split("~");
      return LayerItemSchema.parse({
        name,
        color,
        visible: visible === "true",
        active: active === "true",
        config: config === "true",
        transparency: transparency === "true"
      });
    })
  ),
  objects: z3.array(z3.string()).transform(
    (objects) => objects.map((obj) => {
      const [name, visible, locked] = obj.split("~");
      return ObjectItemSchema.parse({
        name,
        visible: visible === "true",
        locked: locked === "true"
      });
    })
  ),
  BBox: BBoxSchema,
  netColors: z3.array(z3.unknown()).optional()
});
var PackageDetailSchema = z3.object({
  uuid: z3.string(),
  title: z3.string(),
  docType: z3.number(),
  updateTime: z3.number(),
  owner: OwnerSchema,
  datastrid: z3.string(),
  writable: z3.boolean(),
  dataStr: PackageDetailDataStrSchema
});
var EasyEdaJsonSchema = z3.object({
  uuid: z3.string(),
  title: z3.string(),
  description: z3.string(),
  docType: z3.number(),
  type: z3.number(),
  szlcsc: SzlcscSchema,
  lcsc: LcscSchema,
  owner: OwnerSchema,
  tags: z3.array(z3.string()),
  updateTime: z3.number(),
  updated_at: z3.string(),
  dataStr: DataStrSchema,
  verify: z3.boolean(),
  SMT: z3.boolean().optional(),
  datastrid: z3.string(),
  jlcOnSale: z3.number().optional(),
  writable: z3.boolean(),
  isFavorite: z3.boolean(),
  packageDetail: PackageDetailSchema
});

// lib/websafe/convert-to-typescript-component/index.tsx
import { su as su2 } from "@tscircuit/circuit-json-util";

// lib/websafe/generate-footprint-tsx.ts
import { mmStr } from "@tscircuit/mm";
import { su } from "@tscircuit/circuit-json-util";
var generateFootprintTsx = (circuitJson) => {
  const holes = su(circuitJson).pcb_hole.list();
  const platedHoles = su(circuitJson).pcb_plated_hole.list();
  const smtPads = su(circuitJson).pcb_smtpad.list();
  const vias = su(circuitJson).pcb_via.list();
  const silkscreenPaths = su(circuitJson).pcb_silkscreen_path.list();
  const silkscreenTexts = su(circuitJson).pcb_silkscreen_text.list();
  const elementStrings = [];
  for (const hole of holes) {
    if (hole.hole_shape === "circle") {
      elementStrings.push(
        `<hole pcbX="${mmStr(hole.x)}" pcbY="${mmStr(hole.y)}" diameter="${mmStr(hole.hole_diameter)}" />`
      );
    } else if (hole.hole_shape === "oval") {
      console.warn("Unhandled oval hole in conversion (needs implementation)");
    }
  }
  for (const platedHole of platedHoles) {
    if (platedHole.shape === "pill_hole_with_rect_pad") {
      elementStrings.push(
        `<platedhole  portHints={${JSON.stringify(platedHole.port_hints)}} pcbX="${mmStr(platedHole.x)}" pcbY="${mmStr(platedHole.y)}" holeWidth="${mmStr(platedHole.hole_width)}" holeHeight="${mmStr(platedHole.hole_height)}" outerWidth="${mmStr(platedHole.rect_pad_width)}" outerHeight="${mmStr(platedHole.rect_pad_height)}" shape="pill" />`
      );
    } else if (platedHole.shape === "circle") {
      elementStrings.push(
        `<platedhole  portHints={${JSON.stringify(platedHole.port_hints)}} pcbX="${mmStr(platedHole.x)}" pcbY="${mmStr(platedHole.y)}" outerDiameter="${mmStr(platedHole.outer_diameter)}" holeDiameter="${mmStr(platedHole.hole_diameter)}" shape="circle" />`
      );
    } else if (platedHole.shape === "rotated_pill_hole_with_rect_pad") {
      const rotation = platedHole.hole_ccw_rotation || 0;
      elementStrings.push(
        `<platedhole  portHints={${JSON.stringify(platedHole.port_hints)}} pcbX="${mmStr(platedHole.x)}" pcbY="${mmStr(platedHole.y)}" holeWidth="${mmStr(platedHole.hole_width)}" holeHeight="${mmStr(platedHole.hole_height)}" outerWidth="${mmStr(platedHole.rect_pad_width)}" outerHeight="${mmStr(platedHole.rect_pad_height)}" rectPad={true} pcbRotation="${rotation}deg" shape="pill" />`
      );
    }
  }
  for (const smtPad of smtPads) {
    if (smtPad.shape === "circle") {
      elementStrings.push(
        `<smtpad portHints={${JSON.stringify(smtPad.port_hints)}} pcbX="${mmStr(smtPad.x)}" pcbY="${mmStr(smtPad.y)}" radius="${mmStr(smtPad.radius)}" shape="circle" />`
      );
    } else if (smtPad.shape === "rect") {
      elementStrings.push(
        `<smtpad portHints={${JSON.stringify(smtPad.port_hints)}} pcbX="${mmStr(smtPad.x)}" pcbY="${mmStr(smtPad.y)}" width="${mmStr(smtPad.width)}" height="${mmStr(smtPad.height)}" shape="rect" />`
      );
    } else if (smtPad.shape === "polygon") {
      const pointsStr = smtPad.points.map((p) => `{x: "${mmStr(p.x)}", y: "${mmStr(p.y)}"}`).join(", ");
      elementStrings.push(
        `<smtpad portHints={${JSON.stringify(smtPad.port_hints)}} points={[${pointsStr}]} shape="polygon" />`
      );
    }
  }
  for (const via of vias) {
    elementStrings.push(
      `<via pcbX="${mmStr(via.x)}" pcbY="${mmStr(via.y)}" outerDiameter="${mmStr(via.outer_diameter)}" holeDiameter="${mmStr(via.hole_diameter)}" layers={${JSON.stringify(via.layers)}} />`
    );
  }
  for (const silkscreenPath of silkscreenPaths) {
    elementStrings.push(
      `<silkscreenpath route={${JSON.stringify(silkscreenPath.route)}} />`
    );
  }
  for (const silkscreenText of silkscreenTexts) {
    elementStrings.push(
      `<silkscreentext text="${silkscreenText.text}" pcbX="${mmStr(silkscreenText.anchor_position.x)}" pcbY="${mmStr(silkscreenText.anchor_position.y)}" anchorAlignment="${silkscreenText.anchor_alignment}" ${silkscreenText.font_size ? `fontSize="${mmStr(silkscreenText.font_size)}"` : ""} />`
    );
  }
  return `
      <footprint>
        ${elementStrings.join("\n")}
      </footprint>
  `.trim();
};

// lib/websafe/convert-to-typescript-component/generate-typescript-component.ts
var generateTypescriptComponent = ({
  pinLabels,
  componentName,
  objUrl,
  circuitJson,
  supplierPartNumbers,
  manufacturerPartNumber
}) => {
  const safePinLabels = pinLabels ?? {};
  const cadComponent = circuitJson.find((item) => item.type === "cad_component");
  const footprintTsx = generateFootprintTsx(circuitJson);
  const simplifiedPinLabels = Object.fromEntries(
    Object.entries(safePinLabels).map(([pin, labels]) => {
      if (Array.isArray(labels) && labels.length > 1) {
        return [pin, [labels[1]]];
      }
      return [pin, labels];
    })
  );
  const pinLabelsString = Object.entries(simplifiedPinLabels).map(([pin, labels]) => `  ${pin}: ${JSON.stringify(labels)}`).join(",\n");
  return `
import type { ChipProps } from "@tscircuit/props"

const pinLabels = {
${pinLabelsString}
} as const

export const ${componentName} = (props: ChipProps<typeof pinLabels>) => {
  return (
    <chip
      pinLabels={pinLabels}
      supplierPartNumbers={${JSON.stringify(supplierPartNumbers, null, "  ")}}
      manufacturerPartNumber="${manufacturerPartNumber}"
      footprint={${footprintTsx}}
      ${objUrl ? `cadModel={{
        objUrl: "${objUrl}",
        rotationOffset: { x: ${cadComponent?.rotation?.x ?? 0}, y: ${cadComponent?.rotation?.y ?? 0}, z: ${cadComponent?.rotation?.z ?? 0} },
        positionOffset: { x: ${cadComponent?.position?.x ?? 0}, y: ${cadComponent?.position?.y ?? 0}, z: ${cadComponent?.position?.z ?? 0} },
      }}` : ""}
      {...props}
    />
  )
}
`.trim();
};

// lib/utils/normalize-manufacturer-part-number.ts
function normalizeManufacturerPartNumber(partNumber) {
  let normalized = partNumber.replace(/[-]/g, "_").replace(/[^a-zA-Z0-9_$]/g, "_");
  if (/^\d/.test(normalized)) {
    normalized = "A_" + normalized;
  }
  return normalized;
}

// lib/websafe/convert-to-typescript-component/index.tsx
var convertRawEasyToTsx = async (rawEasy) => {
  const betterEasy = EasyEdaJsonSchema.parse(rawEasy);
  const result = await convertBetterEasyToTsx({
    betterEasy
  });
  return result;
};
var convertBetterEasyToTsx = async ({
  betterEasy
}) => {
  const circuitJson = convertEasyEdaJsonToCircuitJson(betterEasy, {
    useModelCdn: true,
    shouldRecenter: true
  });
  const rawPn = betterEasy.dataStr.head.c_para["Manufacturer Part"];
  const pn = rawPn ? normalizeManufacturerPartNumber(rawPn) : "unknown";
  const sourcePorts = su2(circuitJson).source_port.list();
  const pinLabels = {};
  const sortedPorts = sourcePorts.sort((a, b) => {
    const aNum = Number.parseInt(a.name.replace("pin", ""));
    const bNum = Number.parseInt(b.name.replace("pin", ""));
    return aNum - bNum;
  });
  for (const sourcePort of sortedPorts) {
    pinLabels[sourcePort.name] = [
      sourcePort.name,
      ...sourcePort.port_hints ?? []
    ];
  }
  const [cadComponent] = su2(circuitJson).cad_component.list();
  let modelObjUrl;
  if (cadComponent?.model_obj_url) {
    const isValidUrl = await checkModelObjUrlValidity(
      cadComponent.model_obj_url
    );
    if (isValidUrl) {
      modelObjUrl = cadComponent.model_obj_url;
    }
  }
  const supplierPartNumbers = {
    jlcpcb: [betterEasy.lcsc.number]
  };
  return generateTypescriptComponent({
    componentName: pn,
    manufacturerPartNumber: pn,
    pinLabels,
    objUrl: modelObjUrl,
    circuitJson,
    supplierPartNumbers
  });
};
var checkModelObjUrlValidity = async (url) => {
  try {
    const response = await fetch(url, { method: "HEAD" });
    return response.status === 200;
  } catch (error) {
    console.error(`Error checking model object URL ${url}:`, error);
    return false;
  }
};

// lib/convert-easyeda-json-to-various-formats.ts
import fs from "fs/promises";
import * as path from "path";
var convertEasyEdaJsonToVariousFormats = async ({
  jlcpcbPartNumberOrFilepath,
  outputFilename,
  outputFormat
}) => {
  let rawEasyEdaJson;
  if (jlcpcbPartNumberOrFilepath.includes(".") || jlcpcbPartNumberOrFilepath.includes("/")) {
    rawEasyEdaJson = JSON.parse(
      await fs.readFile(jlcpcbPartNumberOrFilepath, "utf-8")
    );
  } else {
    rawEasyEdaJson = await fetchEasyEDAComponent(jlcpcbPartNumberOrFilepath);
  }
  const tsxExtension = "tsx";
  if (outputFormat === "ts") outputFormat = tsxExtension;
  if (!outputFilename && outputFormat) {
    let filename = path.basename(jlcpcbPartNumberOrFilepath).split(".")[0];
    if (outputFormat === tsxExtension) {
      const {
        dataStr: {
          head: {
            c_para: { "Manufacturer Part": manufacturerPartNumber }
          }
        }
      } = rawEasyEdaJson;
      filename = normalizeManufacturerPartNumber(manufacturerPartNumber);
    }
    outputFilename = `${filename}.${outputFormat}`;
  }
  if (!outputFilename) {
    console.log("specify --output file (-o) or --output-format");
    process.exit(1);
  }
  if (outputFilename.endsWith(".raweasy.json")) {
    await fs.writeFile(outputFilename, JSON.stringify(rawEasyEdaJson, null, 2));
    console.log(`Saved raw EasyEDA JSON: ${outputFilename}`);
    return;
  }
  try {
    const betterEasy = EasyEdaJsonSchema.parse(rawEasyEdaJson);
    const tscircuitSoup = convertEasyEdaJsonToTscircuitSoupJson(betterEasy);
    if (outputFilename.endsWith(".soup.json") || outputFilename.endsWith(".circuit.json")) {
      await fs.writeFile(outputFilename, JSON.stringify(tscircuitSoup, null, 2));
      console.log(
        `[${jlcpcbPartNumberOrFilepath}]  Converted to circuit json: ${outputFilename}`
      );
    } else if (outputFilename.endsWith(".kicad_mod")) {
      console.log("Conversion to KiCad footprint not yet implemented");
    } else if (outputFilename.endsWith(".bettereasy.json")) {
      await fs.writeFile(outputFilename, JSON.stringify(betterEasy, null, 2));
      console.log(
        `[${jlcpcbPartNumberOrFilepath}]  Saved better EasyEDA JSON: ${outputFilename}`
      );
    } else if (outputFilename.endsWith(".tsx") || outputFilename.endsWith(".ts")) {
      const tsComp = await convertRawEasyToTsx(rawEasyEdaJson);
      await fs.writeFile(outputFilename, tsComp);
      console.log(
        `[${jlcpcbPartNumberOrFilepath}] Saved TypeScript component: ${outputFilename}`
      );
    } else {
      console.error("Unsupported output format");
    }
  } catch (error) {
    console.error("Error:", error.message);
    throw error;
  }
};

// lib/index.ts
import { normalizePinLabels as normalizePinLabels2 } from "@tscircuit/core";
export {
  BBoxSchema,
  DataStrSchema,
  EasyEdaJsonSchema,
  HeadSchema,
  LayerItemSchema,
  LcscSchema,
  ObjectItemSchema,
  OwnerSchema,
  PackageDetailDataStrSchema,
  PackageDetailSchema,
  SzlcscSchema,
  convertEasyEdaJsonToCircuitJson,
  convertEasyEdaJsonToTscircuitSoupJson,
  convertEasyEdaJsonToVariousFormats,
  convertRawEasyToTsx as convertRawEasyEdaToTs,
  fetchEasyEDAComponent,
  maybeNumber,
  normalizeManufacturerPartNumber,
  normalizePinLabels2 as normalizePinLabels
};
//# sourceMappingURL=index.js.map