@tscircuit/pcb-viewer 1.11.412 → 1.11.414

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/dist/index.js CHANGED
@@ -948,9 +948,6 @@ import { createContext, useContext } from "react";
948
948
  var RenderingEngineContext = createContext(null);
949
949
  var useRenderingEngine = () => useContext(RenderingEngineContext);
950
950
 
951
- // src/PCBViewer.tsx
952
- import { applyEditEvents } from "@tscircuit/core";
953
-
954
951
  // node_modules/@tscircuit/circuit-json-util/dist/index.js
955
952
  import * as Soup from "circuit-json";
956
953
  import * as Soup2 from "circuit-json";
@@ -8993,6 +8990,158 @@ var cjuIndexed = (soup, options = {}) => {
8993
8990
  return suIndexed;
8994
8991
  };
8995
8992
  cjuIndexed.unparsed = cjuIndexed;
8993
+ var getQuarterTurns = (angleRadians) => Math.round(angleRadians / (Math.PI / 2));
8994
+ var insertionDirectionToVec = (direction) => {
8995
+ switch (direction) {
8996
+ case "from_left":
8997
+ return { x: -1, y: 0 };
8998
+ case "from_right":
8999
+ return { x: 1, y: 0 };
9000
+ case "from_top":
9001
+ return { x: 0, y: 1 };
9002
+ case "from_bottom":
9003
+ return { x: 0, y: -1 };
9004
+ }
9005
+ };
9006
+ var vecToInsertionDirection = ({
9007
+ x,
9008
+ y
9009
+ }) => {
9010
+ if (x > 0) return "from_right";
9011
+ if (x < 0) return "from_left";
9012
+ if (y > 0) return "from_top";
9013
+ return "from_bottom";
9014
+ };
9015
+ var transformInsertionDirection = (direction, opts) => {
9016
+ if (!direction) return direction;
9017
+ if (direction === "from_above" || direction === "from_below") {
9018
+ if (!opts.isFlipped) return direction;
9019
+ return direction === "from_above" ? "from_below" : "from_above";
9020
+ }
9021
+ let { x, y } = insertionDirectionToVec(direction);
9022
+ let quarterTurns = Math.round(opts.rotationDegrees / 90);
9023
+ while (quarterTurns > 0) {
9024
+ ;
9025
+ [x, y] = [-y, x];
9026
+ quarterTurns--;
9027
+ }
9028
+ while (quarterTurns < 0) {
9029
+ ;
9030
+ [x, y] = [y, -x];
9031
+ quarterTurns++;
9032
+ }
9033
+ if (opts.isFlipped) {
9034
+ y = -y;
9035
+ }
9036
+ return vecToInsertionDirection({ x, y });
9037
+ };
9038
+ var transformPCBElement = (elm, matrix2) => {
9039
+ const tsr = decomposeTSR(matrix2);
9040
+ const flipPadWidthHeight = Math.abs(getQuarterTurns(tsr.rotation.angle)) % 2 === 1;
9041
+ const rotationDegrees = tsr.rotation.angle / Math.PI * 180;
9042
+ const isFlipped = tsr.scale.sy < 0;
9043
+ if (elm.type === "pcb_plated_hole" || elm.type === "pcb_hole" || elm.type === "pcb_via" || elm.type === "pcb_smtpad" || elm.type === "pcb_solder_paste" || elm.type === "pcb_port") {
9044
+ const { x, y } = applyToPoint(matrix2, {
9045
+ x: Number(elm.x),
9046
+ y: Number(elm.y)
9047
+ });
9048
+ elm.x = x;
9049
+ elm.y = y;
9050
+ if (elm.type === "pcb_smtpad" && elm.shape === "polygon" && Array.isArray(elm.points)) {
9051
+ elm.points = elm.points.map((point2) => {
9052
+ const tp = applyToPoint(matrix2, { x: point2.x, y: point2.y });
9053
+ return {
9054
+ x: tp.x,
9055
+ y: tp.y
9056
+ };
9057
+ });
9058
+ }
9059
+ } else if (elm.type === "pcb_keepout" || elm.type === "pcb_board") {
9060
+ elm.center = applyToPoint(matrix2, elm.center);
9061
+ } else if (elm.type === "pcb_silkscreen_text" || elm.type === "pcb_fabrication_note_text" || elm.type === "pcb_note_text") {
9062
+ elm.anchor_position = applyToPoint(matrix2, elm.anchor_position);
9063
+ } else if (elm.type === "pcb_copper_text") {
9064
+ if (elm.anchor_position) {
9065
+ elm.anchor_position = applyToPoint(matrix2, elm.anchor_position);
9066
+ }
9067
+ } else if (elm.type === "pcb_courtyard_rect") {
9068
+ elm.center = applyToPoint(matrix2, elm.center);
9069
+ elm.ccw_rotation = ((elm.ccw_rotation ?? 0) + rotationDegrees) % 360;
9070
+ } else if (elm.type === "pcb_silkscreen_circle" || elm.type === "pcb_silkscreen_rect" || elm.type === "pcb_silkscreen_pill" || elm.type === "pcb_silkscreen_oval" || elm.type === "pcb_note_rect" || elm.type === "pcb_courtyard_circle") {
9071
+ elm.center = applyToPoint(matrix2, elm.center);
9072
+ } else if (elm.type === "pcb_component") {
9073
+ elm.center = applyToPoint(matrix2, elm.center);
9074
+ elm.rotation = elm.rotation + rotationDegrees;
9075
+ elm.rotation = elm.rotation % 360;
9076
+ if (elm.cable_insertion_center) {
9077
+ elm.cable_insertion_center = applyToPoint(
9078
+ matrix2,
9079
+ elm.cable_insertion_center
9080
+ );
9081
+ }
9082
+ elm.insertion_direction = transformInsertionDirection(
9083
+ elm.insertion_direction,
9084
+ {
9085
+ rotationDegrees,
9086
+ isFlipped
9087
+ }
9088
+ );
9089
+ if (flipPadWidthHeight) {
9090
+ ;
9091
+ [elm.width, elm.height] = [elm.height, elm.width];
9092
+ }
9093
+ } else if (elm.type === "pcb_courtyard_outline") {
9094
+ elm.outline = elm.outline.map((p) => {
9095
+ const tp = applyToPoint(matrix2, p);
9096
+ p.x = tp.x;
9097
+ p.y = tp.y;
9098
+ return p;
9099
+ });
9100
+ } else if (elm.type === "pcb_courtyard_polygon") {
9101
+ elm.points = elm.points.map((p) => {
9102
+ const tp = applyToPoint(matrix2, p);
9103
+ p.x = tp.x;
9104
+ p.y = tp.y;
9105
+ return p;
9106
+ });
9107
+ } else if (elm.type === "pcb_trace") {
9108
+ elm.route = elm.route.map((rp) => {
9109
+ if (!("x" in rp)) {
9110
+ rp.start = applyToPoint(matrix2, rp.start);
9111
+ rp.end = applyToPoint(matrix2, rp.end);
9112
+ return rp;
9113
+ }
9114
+ const tp = applyToPoint(matrix2, rp);
9115
+ rp.x = tp.x;
9116
+ rp.y = tp.y;
9117
+ return rp;
9118
+ });
9119
+ } else if (elm.type === "pcb_silkscreen_path" || elm.type === "pcb_trace_hint" || elm.type === "pcb_fabrication_note_path" || elm.type === "pcb_note_path") {
9120
+ elm.route = elm.route.map((rp) => {
9121
+ const tp = applyToPoint(matrix2, rp);
9122
+ rp.x = tp.x;
9123
+ rp.y = tp.y;
9124
+ return rp;
9125
+ });
9126
+ } else if (elm.type === "pcb_silkscreen_line" || elm.type === "pcb_note_line") {
9127
+ const p1 = { x: elm.x1, y: elm.y1 };
9128
+ const p2 = { x: elm.x2, y: elm.y2 };
9129
+ const p1t = applyToPoint(matrix2, p1);
9130
+ const p2t = applyToPoint(matrix2, p2);
9131
+ elm.x1 = p1t.x;
9132
+ elm.y1 = p1t.y;
9133
+ elm.x2 = p2t.x;
9134
+ elm.y2 = p2t.y;
9135
+ } else if (elm.type === "cad_component") {
9136
+ const newPos = applyToPoint(matrix2, {
9137
+ x: elm.position.x,
9138
+ y: elm.position.y
9139
+ });
9140
+ elm.position.x = newPos.x;
9141
+ elm.position.y = newPos.y;
9142
+ }
9143
+ return elm;
9144
+ };
8996
9145
  var getElementId = (elm) => {
8997
9146
  const type = elm.type;
8998
9147
  const id = elm[`${type}_id`];
@@ -9806,7 +9955,7 @@ var useMeasure_default = isBrowser && typeof window.ResizeObserver !== "undefine
9806
9955
  };
9807
9956
 
9808
9957
  // src/PCBViewer.tsx
9809
- import { compose as compose9, scale as scale6, translate as translate13 } from "transformation-matrix";
9958
+ import { compose as compose9, scale as scale6, translate as translate14 } from "transformation-matrix";
9810
9959
 
9811
9960
  // node_modules/use-mouse-matrix-transform/dist/chunk-TGYMZPTI.js
9812
9961
  import { compose as compose2, translate as translate6, scale } from "transformation-matrix";
@@ -11633,8 +11782,229 @@ var createWebGpuWorker = async () => {
11633
11782
  };
11634
11783
 
11635
11784
  // src/components/WebGpuElementsRenderer.tsx
11636
- import { useEffect as useEffect6, useMemo as useMemo3, useRef as useRef4, useState as useState4 } from "react";
11637
- import { SuperGrid as SuperGrid2, toMMSI as toMMSI2 } from "react-supergrid";
11785
+ import { useEffect as useEffect5, useMemo as useMemo3, useRef as useRef3, useState as useState4 } from "react";
11786
+ import { SuperGrid, toMMSI } from "react-supergrid";
11787
+ import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
11788
+ function WebGpuElementsRenderer(props) {
11789
+ const { elements, primitives, transform, width = 500, height = 500 } = props;
11790
+ const holder = useRef3(null);
11791
+ const workerRef = useRef3(void 0);
11792
+ const ready = useRef3(false);
11793
+ const [supportsXRayNet, setSupportsXRayNet] = useState4(null);
11794
+ const [failure, setFailure] = useState4();
11795
+ const selectedLayer = useGlobalStore((s) => s.selected_layer);
11796
+ const hiddenLayerOpacity = useGlobalStore((s) => s.hidden_layer_opacity);
11797
+ const showCopperPours = useGlobalStore((s) => s.is_showing_copper_pours);
11798
+ const showSolderMask = useGlobalStore((s) => s.is_showing_solder_mask);
11799
+ const showSilkscreen = useGlobalStore((s) => s.is_showing_silkscreen);
11800
+ const showFabricationNotes = useGlobalStore(
11801
+ (s) => s.is_showing_fabrication_notes
11802
+ );
11803
+ const showPcbNotes = useGlobalStore((s) => s.is_showing_pcb_notes);
11804
+ const showCourtyards = useGlobalStore((s) => s.is_showing_courtyards);
11805
+ const highlightedElementIds = useMemo3(
11806
+ () => [
11807
+ ...new Set(
11808
+ primitives.flatMap((p) => {
11809
+ if (!p.is_mouse_over && !p.is_in_highlighted_net) return [];
11810
+ const element = p._element;
11811
+ const id = element?.[`${element.type}_id`];
11812
+ return id ? [id] : [];
11813
+ })
11814
+ )
11815
+ ],
11816
+ [primitives]
11817
+ );
11818
+ const xRayElementIds = useMemo3(
11819
+ () => (props.xRayElements ?? []).map(
11820
+ (element) => element[`${element.type}_id`]
11821
+ ),
11822
+ [props.xRayElements]
11823
+ );
11824
+ const options = useMemo3(
11825
+ () => ({
11826
+ selectedLayer,
11827
+ hiddenLayerOpacity,
11828
+ showCopperPours,
11829
+ showSolderMask,
11830
+ showSilkscreen,
11831
+ showFabricationNotes,
11832
+ showPcbNotes,
11833
+ showCourtyards,
11834
+ highlightedElementIds,
11835
+ xRayElementIds
11836
+ }),
11837
+ [
11838
+ selectedLayer,
11839
+ hiddenLayerOpacity,
11840
+ showCopperPours,
11841
+ showSolderMask,
11842
+ showSilkscreen,
11843
+ showFabricationNotes,
11844
+ showPcbNotes,
11845
+ showCourtyards,
11846
+ highlightedElementIds,
11847
+ xRayElementIds
11848
+ ]
11849
+ );
11850
+ const sceneRef = useRef3(elements);
11851
+ const viewRef = useRef3(void 0);
11852
+ sceneRef.current = elements;
11853
+ if (transform) {
11854
+ const ratio = typeof window === "undefined" ? 1 : window.devicePixelRatio || 1;
11855
+ viewRef.current = {
11856
+ type: "view",
11857
+ width: Math.max(1, Math.round(width * ratio)),
11858
+ height: Math.max(1, Math.round(height * ratio)),
11859
+ transform: {
11860
+ a: transform.a * ratio,
11861
+ b: transform.b * ratio,
11862
+ c: transform.c * ratio,
11863
+ d: transform.d * ratio,
11864
+ e: transform.e * ratio,
11865
+ f: transform.f * ratio
11866
+ },
11867
+ options
11868
+ };
11869
+ }
11870
+ useEffect5(() => {
11871
+ let disposed = false, failed = false, worker;
11872
+ const canvas = document.createElement("canvas");
11873
+ canvas.className = "pcb-webgpu-canvas";
11874
+ canvas.style.cssText = "position:absolute;inset:0;width:100%;height:100%;pointer-events:none";
11875
+ holder.current?.append(canvas);
11876
+ const fail = (message) => {
11877
+ if (disposed || failed) return;
11878
+ failed = true;
11879
+ clearTimeout(timeout);
11880
+ canvas.remove();
11881
+ ready.current = false;
11882
+ worker?.terminate();
11883
+ worker = void 0;
11884
+ workerRef.current = void 0;
11885
+ if (holder.current) holder.current.dataset.xRayNetActive = "false";
11886
+ setFailure(message);
11887
+ };
11888
+ const timeout = setTimeout(
11889
+ () => fail("WebGPU worker startup timed out"),
11890
+ 15e3
11891
+ );
11892
+ void (async () => {
11893
+ try {
11894
+ if (!navigator.gpu || typeof Worker === "undefined" || !canvas.transferControlToOffscreen)
11895
+ throw new Error("WebGPU workers are unavailable");
11896
+ worker = await createWebGpuWorker();
11897
+ if (disposed || failed) {
11898
+ worker.terminate();
11899
+ return;
11900
+ }
11901
+ workerRef.current = worker;
11902
+ worker.onmessage = ({ data }) => {
11903
+ if (disposed || failed) return;
11904
+ if (data.type === "rendered") {
11905
+ if (holder.current)
11906
+ holder.current.dataset.xRayNetActive = String(data.xRayActive);
11907
+ } else if (data.type === "error") {
11908
+ fail(data.message);
11909
+ } else if (data.type === "ready") {
11910
+ clearTimeout(timeout);
11911
+ setSupportsXRayNet(data.supportsXRayNet);
11912
+ ready.current = true;
11913
+ worker.postMessage({
11914
+ type: "scene",
11915
+ elements: sceneRef.current
11916
+ });
11917
+ if (viewRef.current) worker.postMessage(viewRef.current);
11918
+ }
11919
+ };
11920
+ worker.onerror = (event) => {
11921
+ event.preventDefault();
11922
+ fail(event.message || "WebGPU worker failed");
11923
+ };
11924
+ worker.addEventListener("messageerror", () => {
11925
+ fail("WebGPU worker communication failed");
11926
+ });
11927
+ const offscreen = canvas.transferControlToOffscreen();
11928
+ worker.postMessage(
11929
+ { type: "init", canvas: offscreen },
11930
+ [offscreen]
11931
+ );
11932
+ } catch (error) {
11933
+ fail(String(error));
11934
+ }
11935
+ })();
11936
+ return () => {
11937
+ disposed = true;
11938
+ clearTimeout(timeout);
11939
+ ready.current = false;
11940
+ workerRef.current = void 0;
11941
+ worker?.terminate();
11942
+ canvas.remove();
11943
+ };
11944
+ }, []);
11945
+ useEffect5(() => {
11946
+ if (ready.current)
11947
+ workerRef.current?.postMessage({
11948
+ type: "scene",
11949
+ elements
11950
+ });
11951
+ }, [elements]);
11952
+ useEffect5(() => {
11953
+ if (ready.current && viewRef.current)
11954
+ workerRef.current?.postMessage(viewRef.current);
11955
+ }, [transform, width, height, options]);
11956
+ const xRayUnavailable = Boolean(
11957
+ props.xRayElements && supportsXRayNet === false
11958
+ );
11959
+ return /* @__PURE__ */ jsx3(
11960
+ "div",
11961
+ {
11962
+ ref: holder,
11963
+ "data-pcb-renderer": "webgpu",
11964
+ "data-webgpu-error": failure,
11965
+ style: {
11966
+ width,
11967
+ height,
11968
+ background: "black",
11969
+ position: "relative",
11970
+ overflow: "hidden"
11971
+ },
11972
+ children: failure ? /* @__PURE__ */ jsxs(
11973
+ "div",
11974
+ {
11975
+ role: "alert",
11976
+ style: { padding: 16, color: "white", position: "relative" },
11977
+ children: [
11978
+ /* @__PURE__ */ jsx3("strong", { children: "WebGPU rendering failed" }),
11979
+ /* @__PURE__ */ jsx3("div", { children: failure })
11980
+ ]
11981
+ }
11982
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
11983
+ /* @__PURE__ */ jsx3(
11984
+ SuperGrid,
11985
+ {
11986
+ textColor: "rgba(0,255,0,0.8)",
11987
+ majorColor: "rgba(0,255,0,0.4)",
11988
+ minorColor: "rgba(0,255,0,0.2)",
11989
+ screenSpaceCellSize: 200,
11990
+ width,
11991
+ height,
11992
+ transform,
11993
+ stringifyCoord: (x, y, z) => `${toMMSI(x, z)}, ${toMMSI(y, z)}`
11994
+ }
11995
+ ),
11996
+ xRayUnavailable && /* @__PURE__ */ jsx3(
11997
+ "div",
11998
+ {
11999
+ role: "status",
12000
+ style: { padding: 16, color: "white", position: "relative" },
12001
+ children: "X-Ray is unavailable in this WebGPU renderer."
12002
+ }
12003
+ )
12004
+ ] })
12005
+ }
12006
+ );
12007
+ }
11638
12008
 
11639
12009
  // src/components/CanvasPrimitiveRenderer.tsx
11640
12010
  import { CircuitToCanvasDrawer as CircuitToCanvasDrawer17 } from "circuit-to-canvas";
@@ -13577,9 +13947,9 @@ var getPrimitivesForDrawer = ({
13577
13947
  ).filter((primitive) => primitive._element?.type !== "pcb_via").filter((primitive) => primitive._element?.type !== "pcb_trace").filter((primitive) => primitive._element?.type !== "pcb_copper_text");
13578
13948
 
13579
13949
  // src/components/CanvasPrimitiveRenderer.tsx
13580
- import { useEffect as useEffect5, useRef as useRef3 } from "react";
13581
- import { SuperGrid, toMMSI } from "react-supergrid";
13582
- import { jsx as jsx3, jsxs } from "react/jsx-runtime";
13950
+ import { useEffect as useEffect6, useRef as useRef4 } from "react";
13951
+ import { SuperGrid as SuperGrid2, toMMSI as toMMSI2 } from "react-supergrid";
13952
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
13583
13953
  var CanvasPrimitiveRenderer = ({
13584
13954
  primitives,
13585
13955
  elements,
@@ -13589,8 +13959,8 @@ var CanvasPrimitiveRenderer = ({
13589
13959
  width = 500,
13590
13960
  height = 500
13591
13961
  }) => {
13592
- const xRayCanvasRef = useRef3(null);
13593
- const canvasRefs = useRef3({});
13962
+ const xRayCanvasRef = useRef4(null);
13963
+ const canvasRefs = useRef4({});
13594
13964
  const hiddenLayerOpacity = useGlobalStore((s) => s.hidden_layer_opacity);
13595
13965
  const selectedLayer = useGlobalStore((s) => s.selected_layer);
13596
13966
  const isShowingCopperPours = useGlobalStore((s) => s.is_showing_copper_pours);
@@ -13601,7 +13971,7 @@ var CanvasPrimitiveRenderer = ({
13601
13971
  const isShowingPcbNotes = useGlobalStore((s) => s.is_showing_pcb_notes);
13602
13972
  const isShowingCourtyards = useGlobalStore((s) => s.is_showing_courtyards);
13603
13973
  const isShowingSilkscreen = useGlobalStore((s) => s.is_showing_silkscreen);
13604
- useEffect5(() => {
13974
+ useEffect6(() => {
13605
13975
  if (!canvasRefs.current) return;
13606
13976
  if (Object.keys(canvasRefs.current).length === 0) return;
13607
13977
  const availableCanvasRefs = Object.fromEntries(
@@ -13898,7 +14268,7 @@ var CanvasPrimitiveRenderer = ({
13898
14268
  isShowingCourtyards,
13899
14269
  isShowingSilkscreen
13900
14270
  ]);
13901
- return /* @__PURE__ */ jsxs(
14271
+ return /* @__PURE__ */ jsxs2(
13902
14272
  "div",
13903
14273
  {
13904
14274
  style: {
@@ -13908,8 +14278,8 @@ var CanvasPrimitiveRenderer = ({
13908
14278
  position: "relative"
13909
14279
  },
13910
14280
  children: [
13911
- /* @__PURE__ */ jsx3(
13912
- SuperGrid,
14281
+ /* @__PURE__ */ jsx4(
14282
+ SuperGrid2,
13913
14283
  {
13914
14284
  textColor: "rgba(0,255,0,0.8)",
13915
14285
  majorColor: "rgba(0,255,0,0.4)",
@@ -13918,10 +14288,10 @@ var CanvasPrimitiveRenderer = ({
13918
14288
  width,
13919
14289
  height,
13920
14290
  transform,
13921
- stringifyCoord: (x, y, z) => `${toMMSI(x, z)}, ${toMMSI(y, z)}`
14291
+ stringifyCoord: (x, y, z) => `${toMMSI2(x, z)}, ${toMMSI2(y, z)}`
13922
14292
  }
13923
14293
  ),
13924
- xRayElements && /* @__PURE__ */ jsx3(
14294
+ xRayElements && /* @__PURE__ */ jsx4(
13925
14295
  "canvas",
13926
14296
  {
13927
14297
  ref: xRayCanvasRef,
@@ -13941,7 +14311,7 @@ var CanvasPrimitiveRenderer = ({
13941
14311
  if (!isShowingSolderMask && layer.includes("soldermask")) return false;
13942
14312
  if (!isShowingSilkscreen && layer.includes("silkscreen")) return false;
13943
14313
  return true;
13944
- }).map((l) => l.replace(/-/g, "")).map((layer, i) => /* @__PURE__ */ jsx3(
14314
+ }).map((l) => l.replace(/-/g, "")).map((layer, i) => /* @__PURE__ */ jsx4(
13945
14315
  "canvas",
13946
14316
  {
13947
14317
  className: `pcb-layer-${layer}`,
@@ -13970,217 +14340,6 @@ var CanvasPrimitiveRenderer = ({
13970
14340
  );
13971
14341
  };
13972
14342
 
13973
- // src/components/WebGpuElementsRenderer.tsx
13974
- import { Fragment, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
13975
- function WebGpuElementsRenderer(props) {
13976
- const { elements, primitives, transform, width = 500, height = 500 } = props;
13977
- const holder = useRef4(null);
13978
- const workerRef = useRef4(void 0);
13979
- const ready = useRef4(false);
13980
- const [supportsXRayNet, setSupportsXRayNet] = useState4(false);
13981
- const [failure, setFailure] = useState4();
13982
- const selectedLayer = useGlobalStore((s) => s.selected_layer);
13983
- const hiddenLayerOpacity = useGlobalStore((s) => s.hidden_layer_opacity);
13984
- const showCopperPours = useGlobalStore((s) => s.is_showing_copper_pours);
13985
- const showSolderMask = useGlobalStore((s) => s.is_showing_solder_mask);
13986
- const showSilkscreen = useGlobalStore((s) => s.is_showing_silkscreen);
13987
- const showFabricationNotes = useGlobalStore(
13988
- (s) => s.is_showing_fabrication_notes
13989
- );
13990
- const showPcbNotes = useGlobalStore((s) => s.is_showing_pcb_notes);
13991
- const showCourtyards = useGlobalStore((s) => s.is_showing_courtyards);
13992
- const highlightedElementIds = useMemo3(
13993
- () => [
13994
- ...new Set(
13995
- primitives.flatMap((p) => {
13996
- if (!p.is_mouse_over && !p.is_in_highlighted_net) return [];
13997
- const element = p._element;
13998
- const id = element?.[`${element.type}_id`];
13999
- return id ? [id] : [];
14000
- })
14001
- )
14002
- ],
14003
- [primitives]
14004
- );
14005
- const xRayElementIds = useMemo3(
14006
- () => (props.xRayElements ?? []).map(
14007
- (element) => element[`${element.type}_id`]
14008
- ),
14009
- [props.xRayElements]
14010
- );
14011
- const options = useMemo3(
14012
- () => ({
14013
- selectedLayer,
14014
- hiddenLayerOpacity,
14015
- showCopperPours,
14016
- showSolderMask,
14017
- showSilkscreen,
14018
- showFabricationNotes,
14019
- showPcbNotes,
14020
- showCourtyards,
14021
- highlightedElementIds,
14022
- xRayElementIds
14023
- }),
14024
- [
14025
- selectedLayer,
14026
- hiddenLayerOpacity,
14027
- showCopperPours,
14028
- showSolderMask,
14029
- showSilkscreen,
14030
- showFabricationNotes,
14031
- showPcbNotes,
14032
- showCourtyards,
14033
- highlightedElementIds,
14034
- xRayElementIds
14035
- ]
14036
- );
14037
- const sceneRef = useRef4(elements);
14038
- const viewRef = useRef4(void 0);
14039
- sceneRef.current = elements;
14040
- if (transform) {
14041
- const ratio = typeof window === "undefined" ? 1 : window.devicePixelRatio || 1;
14042
- viewRef.current = {
14043
- type: "view",
14044
- width: Math.max(1, Math.round(width * ratio)),
14045
- height: Math.max(1, Math.round(height * ratio)),
14046
- transform: {
14047
- a: transform.a * ratio,
14048
- b: transform.b * ratio,
14049
- c: transform.c * ratio,
14050
- d: transform.d * ratio,
14051
- e: transform.e * ratio,
14052
- f: transform.f * ratio
14053
- },
14054
- options
14055
- };
14056
- }
14057
- useEffect6(() => {
14058
- let disposed = false, failed = false, worker;
14059
- const canvas = document.createElement("canvas");
14060
- canvas.className = "pcb-webgpu-canvas";
14061
- canvas.style.cssText = "position:absolute;inset:0;width:100%;height:100%;pointer-events:none";
14062
- holder.current?.append(canvas);
14063
- const fallback = (message) => {
14064
- if (disposed || failed) return;
14065
- failed = true;
14066
- canvas.remove();
14067
- ready.current = false;
14068
- worker?.terminate();
14069
- workerRef.current = void 0;
14070
- setFailure(message);
14071
- };
14072
- const timeout = setTimeout(
14073
- () => fallback("WebGPU worker startup timed out"),
14074
- 15e3
14075
- );
14076
- void (async () => {
14077
- try {
14078
- if (!navigator.gpu || typeof Worker === "undefined" || !canvas.transferControlToOffscreen)
14079
- throw new Error("WebGPU workers are unavailable");
14080
- worker = await createWebGpuWorker();
14081
- if (disposed || failed) {
14082
- worker.terminate();
14083
- return;
14084
- }
14085
- workerRef.current = worker;
14086
- worker.onmessage = ({ data }) => {
14087
- if (disposed || failed) return;
14088
- if (data.type === "rendered") {
14089
- if (holder.current)
14090
- holder.current.dataset.xRayNetActive = String(data.xRayActive);
14091
- } else if (data.type === "error") {
14092
- clearTimeout(timeout);
14093
- fallback(data.message);
14094
- } else if (data.type === "ready") {
14095
- clearTimeout(timeout);
14096
- setSupportsXRayNet(data.supportsXRayNet);
14097
- ready.current = true;
14098
- worker.postMessage({
14099
- type: "scene",
14100
- elements: sceneRef.current
14101
- });
14102
- if (viewRef.current) worker.postMessage(viewRef.current);
14103
- }
14104
- };
14105
- worker.onerror = (event) => {
14106
- event.preventDefault();
14107
- clearTimeout(timeout);
14108
- fallback(event.message || "WebGPU worker failed");
14109
- };
14110
- worker.onmessageerror = () => {
14111
- clearTimeout(timeout);
14112
- fallback("WebGPU worker communication failed");
14113
- };
14114
- const offscreen = canvas.transferControlToOffscreen();
14115
- worker.postMessage(
14116
- { type: "init", canvas: offscreen },
14117
- [offscreen]
14118
- );
14119
- } catch (error) {
14120
- clearTimeout(timeout);
14121
- fallback(String(error));
14122
- }
14123
- })();
14124
- return () => {
14125
- disposed = true;
14126
- clearTimeout(timeout);
14127
- ready.current = false;
14128
- workerRef.current = void 0;
14129
- worker?.terminate();
14130
- canvas.remove();
14131
- };
14132
- }, []);
14133
- useEffect6(() => {
14134
- if (ready.current)
14135
- workerRef.current?.postMessage({
14136
- type: "scene",
14137
- elements
14138
- });
14139
- }, [elements]);
14140
- useEffect6(() => {
14141
- if (ready.current && viewRef.current)
14142
- workerRef.current?.postMessage(viewRef.current);
14143
- }, [transform, width, height, options]);
14144
- const fallbackElements = useMemo3(
14145
- () => showCopperPours ? elements : elements.filter((e) => e.type !== "pcb_copper_pour"),
14146
- [elements, showCopperPours]
14147
- );
14148
- if (failure)
14149
- return /* @__PURE__ */ jsx4("div", { "data-pcb-renderer": "canvas", "data-webgpu-fallback": failure, children: /* @__PURE__ */ jsx4(CanvasPrimitiveRenderer, { ...props, elements: fallbackElements }) });
14150
- const useXRayFallback = Boolean(props.xRayElements && !supportsXRayNet);
14151
- return /* @__PURE__ */ jsxs2(Fragment, { children: [
14152
- useXRayFallback && /* @__PURE__ */ jsx4("div", { "data-x-ray-fallback": "unsupported-webgpu-version", children: /* @__PURE__ */ jsx4(CanvasPrimitiveRenderer, { ...props, elements: fallbackElements }) }),
14153
- /* @__PURE__ */ jsx4(
14154
- "div",
14155
- {
14156
- ref: holder,
14157
- "data-pcb-renderer": "webgpu",
14158
- style: {
14159
- width,
14160
- height,
14161
- background: "black",
14162
- position: "relative",
14163
- overflow: "hidden",
14164
- display: useXRayFallback ? "none" : void 0
14165
- },
14166
- children: /* @__PURE__ */ jsx4(
14167
- SuperGrid2,
14168
- {
14169
- textColor: "rgba(0,255,0,0.8)",
14170
- majorColor: "rgba(0,255,0,0.4)",
14171
- minorColor: "rgba(0,255,0,0.2)",
14172
- screenSpaceCellSize: 200,
14173
- width,
14174
- height,
14175
- transform,
14176
- stringifyCoord: (x, y, z) => `${toMMSI2(x, z)}, ${toMMSI2(y, z)}`
14177
- }
14178
- )
14179
- }
14180
- )
14181
- ] });
14182
- }
14183
-
14184
14343
  // node_modules/graphics-debug/dist/chunk-OHVOYMJ4.js
14185
14344
  import {
14186
14345
  glyphAdvanceRatio,
@@ -18359,7 +18518,7 @@ import { css as css3 } from "@emotion/css";
18359
18518
  // package.json
18360
18519
  var package_default = {
18361
18520
  name: "@tscircuit/pcb-viewer",
18362
- version: "1.11.411",
18521
+ version: "1.11.413",
18363
18522
  main: "dist/index.js",
18364
18523
  type: "module",
18365
18524
  repository: {
@@ -18391,6 +18550,7 @@ var package_default = {
18391
18550
  "@swc/core": "^1.4.12",
18392
18551
  "@tscircuit/circuit-json-util": "^0.0.116",
18393
18552
  "@tscircuit/circuit-json-webgpu": "github:tscircuit/circuit-json-webgpu#6c1414c7464a743190747c99bb8ec504f2ecf646",
18553
+ "@tscircuit/core": "^0.0.1701",
18394
18554
  "@tscircuit/eagle-xml-converter": "^1.0.0",
18395
18555
  "@types/bun": "latest",
18396
18556
  "@types/color": "^3.0.6",
@@ -18417,8 +18577,7 @@ var package_default = {
18417
18577
  },
18418
18578
  peerDependencies: {
18419
18579
  react: "*",
18420
- "react-dom": "*",
18421
- tscircuit: "*"
18580
+ "react-dom": "*"
18422
18581
  },
18423
18582
  dependencies: {
18424
18583
  "@emotion/css": "^11.11.2",
@@ -20637,9 +20796,65 @@ var calculateBoardSizeKey = (circuitJson) => {
20637
20796
  return `${round(board.width)}_${round(board.height)}`;
20638
20797
  };
20639
20798
 
20799
+ // src/lib/apply-edit-events.ts
20800
+ import { translate as translate13 } from "transformation-matrix";
20801
+ function applyEditEvents({
20802
+ circuitJson,
20803
+ editEvents
20804
+ }) {
20805
+ let editedCircuitJson = structuredClone(circuitJson);
20806
+ for (const editEvent of editEvents) {
20807
+ if (editEvent.edit_event_type === "edit_pcb_component_location") {
20808
+ const pcbComponent = su(editedCircuitJson).pcb_component.get(
20809
+ editEvent.pcb_component_id
20810
+ );
20811
+ const isAlreadyAtEditedCenter = pcbComponent?.center.x === editEvent.new_center.x && pcbComponent.center.y === editEvent.new_center.y;
20812
+ if (isAlreadyAtEditedCenter) continue;
20813
+ const pcbToEditedPcbTransform = translate13(
20814
+ editEvent.new_center.x - editEvent.original_center.x,
20815
+ editEvent.new_center.y - editEvent.original_center.y
20816
+ );
20817
+ editedCircuitJson = editedCircuitJson.map(
20818
+ (element) => "pcb_component_id" in element && element.pcb_component_id === editEvent.pcb_component_id ? transformPCBElement(element, pcbToEditedPcbTransform) : element
20819
+ );
20820
+ continue;
20821
+ }
20822
+ if (editEvent.edit_event_type === "edit_schematic_component_location") {
20823
+ editedCircuitJson = editedCircuitJson.map(
20824
+ (element) => element.type === "schematic_component" && element.schematic_component_id === editEvent.schematic_component_id ? { ...element, center: editEvent.new_center } : element
20825
+ );
20826
+ continue;
20827
+ }
20828
+ if (editEvent.edit_event_type !== "edit_pcb_trace_hint") continue;
20829
+ if (!editEvent.pcb_trace_hint_id) continue;
20830
+ const pcbTraceHint = su(editedCircuitJson).pcb_trace_hint.get(
20831
+ editEvent.pcb_trace_hint_id
20832
+ );
20833
+ if (pcbTraceHint) {
20834
+ editedCircuitJson = editedCircuitJson.map(
20835
+ (element) => element.type === "pcb_trace_hint" && element.pcb_trace_hint_id === editEvent.pcb_trace_hint_id ? { ...element, route: editEvent.route } : element
20836
+ );
20837
+ continue;
20838
+ }
20839
+ const pcbPort = su(editedCircuitJson).pcb_port.get(editEvent.pcb_port_id);
20840
+ if (!pcbPort?.pcb_component_id) continue;
20841
+ const newPcbTraceHint = {
20842
+ type: "pcb_trace_hint",
20843
+ pcb_trace_hint_id: editEvent.pcb_trace_hint_id,
20844
+ route: editEvent.route,
20845
+ pcb_port_id: editEvent.pcb_port_id,
20846
+ pcb_component_id: pcbPort.pcb_component_id
20847
+ };
20848
+ editedCircuitJson = editedCircuitJson.filter(
20849
+ (element) => element.type !== "pcb_trace_hint" || element.pcb_port_id !== editEvent.pcb_port_id
20850
+ ).concat(newPcbTraceHint);
20851
+ }
20852
+ return editedCircuitJson;
20853
+ }
20854
+
20640
20855
  // src/PCBViewer.tsx
20641
20856
  import { jsx as jsx26, jsxs as jsxs19 } from "react/jsx-runtime";
20642
- var defaultTransform = compose9(translate13(400, 300), scale6(40, -40));
20857
+ var defaultTransform = compose9(translate14(400, 300), scale6(40, -40));
20643
20858
  var PCBViewer = ({
20644
20859
  circuitJson,
20645
20860
  debugGraphics,
@@ -20719,9 +20934,9 @@ var PCBViewer = ({
20719
20934
  100
20720
20935
  ) * 0.75;
20721
20936
  const targetTransform = compose9(
20722
- translate13((elmBounds.width ?? 0) / 2, (elmBounds.height ?? 0) / 2),
20937
+ translate14((elmBounds.width ?? 0) / 2, (elmBounds.height ?? 0) / 2),
20723
20938
  scale6(scaleFactor, -scaleFactor, 0, 0),
20724
- translate13(-center.x, -center.y)
20939
+ translate14(-center.x, -center.y)
20725
20940
  );
20726
20941
  setTransform(targetTransform);
20727
20942
  return;