@tscircuit/pcb-viewer 1.11.401 → 1.11.403

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
@@ -4258,6 +4258,52 @@ var animateTransform = ({
4258
4258
  import { useCallback as useCallback11, useEffect as useEffect19, useMemo as useMemo10, useRef as useRef16, useState as useState13 } from "react";
4259
4259
  import { applyToPoint as applyToPoint19, inverse as inverse6 } from "transformation-matrix";
4260
4260
 
4261
+ // src/lib/get-trace-overlay-text.ts
4262
+ var getNonEmptyName = (name) => name?.trim() || null;
4263
+ function getAssociatedTraceName({
4264
+ primitiveElement,
4265
+ elements
4266
+ }) {
4267
+ const sourceElementId = primitiveElement.source_trace_id;
4268
+ if (!sourceElementId) return null;
4269
+ const circuitJson = su(elements);
4270
+ const sourceTrace = circuitJson.source_trace.get(sourceElementId);
4271
+ const sourceTraceName = getNonEmptyName(sourceTrace?.name);
4272
+ if (sourceTraceName) return sourceTraceName;
4273
+ const sourceNet = circuitJson.source_net.get(sourceElementId);
4274
+ const sourceNetName = getNonEmptyName(sourceNet?.name);
4275
+ if (sourceNetName) return sourceNetName;
4276
+ for (const sourceNetId of sourceTrace?.connected_source_net_ids ?? []) {
4277
+ const connectedSourceNetName = getNonEmptyName(
4278
+ circuitJson.source_net.get(sourceNetId)?.name
4279
+ );
4280
+ if (connectedSourceNetName) return connectedSourceNetName;
4281
+ }
4282
+ return null;
4283
+ }
4284
+ function getTraceOverlayInfo({
4285
+ primitiveElement,
4286
+ elements
4287
+ }) {
4288
+ const trace = su(elements).source_trace.get(
4289
+ primitiveElement?.source_trace_id
4290
+ );
4291
+ const traceLength = primitiveElement.trace_length;
4292
+ const hasTraceLength = typeof traceLength === "number";
4293
+ const maxLength = trace?.max_length;
4294
+ const text = hasTraceLength ? `${traceLength.toFixed(3)}${typeof maxLength === "number" ? ` / ${maxLength}` : ""} mm` : "";
4295
+ const name = getAssociatedTraceName({ primitiveElement, elements }) ?? getNonEmptyName(trace?.display_name);
4296
+ if (!text && !name) return null;
4297
+ const isOverLength = Boolean(
4298
+ hasTraceLength && typeof maxLength === "number" && traceLength > maxLength
4299
+ );
4300
+ return {
4301
+ text,
4302
+ name,
4303
+ isOverLength
4304
+ };
4305
+ }
4306
+
4261
4307
  // src/lib/x-ray-net.ts
4262
4308
  function getElementNetId(element, connectivityMap) {
4263
4309
  if (!element) return;
@@ -4282,6 +4328,32 @@ function isXRayCopper(element) {
4282
4328
  element.type
4283
4329
  );
4284
4330
  }
4331
+ function getXRayDisplayName(element, elements, connectivityMap) {
4332
+ const directName = element.type === "pcb_trace" ? getAssociatedTraceName({ primitiveElement: element, elements }) : null;
4333
+ if (directName) return directName;
4334
+ const netId = getElementNetId(element, connectivityMap);
4335
+ const connected = netId ? elements.filter(
4336
+ (candidate) => getElementNetId(candidate, connectivityMap) === netId
4337
+ ) : [];
4338
+ for (const type of ["source_net", "source_trace"]) {
4339
+ for (const candidate of connected) {
4340
+ if (candidate.type === type && "name" in candidate && typeof candidate.name === "string" && candidate.name.trim())
4341
+ return candidate.name.trim();
4342
+ }
4343
+ }
4344
+ if (element.type === "pcb_trace") {
4345
+ const label = getTraceOverlayInfo({
4346
+ primitiveElement: element,
4347
+ elements
4348
+ })?.name;
4349
+ if (label) return label;
4350
+ }
4351
+ for (const candidate of connected) {
4352
+ if (candidate.type === "source_trace" && candidate.display_name?.trim())
4353
+ return candidate.display_name.trim();
4354
+ }
4355
+ return "Net";
4356
+ }
4285
4357
 
4286
4358
  // src/lib/webgpu/create-worker.ts
4287
4359
  var createWebGpuWorker = async () => {
@@ -10073,52 +10145,6 @@ var PanelAnchorOffsetOverlay = ({
10073
10145
  );
10074
10146
  };
10075
10147
 
10076
- // src/lib/get-trace-overlay-text.ts
10077
- var getNonEmptyName = (name) => name?.trim() || null;
10078
- function getAssociatedTraceName({
10079
- primitiveElement,
10080
- elements
10081
- }) {
10082
- const sourceElementId = primitiveElement.source_trace_id;
10083
- if (!sourceElementId) return null;
10084
- const circuitJson = su(elements);
10085
- const sourceTrace = circuitJson.source_trace.get(sourceElementId);
10086
- const sourceTraceName = getNonEmptyName(sourceTrace?.name);
10087
- if (sourceTraceName) return sourceTraceName;
10088
- const sourceNet = circuitJson.source_net.get(sourceElementId);
10089
- const sourceNetName = getNonEmptyName(sourceNet?.name);
10090
- if (sourceNetName) return sourceNetName;
10091
- for (const sourceNetId of sourceTrace?.connected_source_net_ids ?? []) {
10092
- const connectedSourceNetName = getNonEmptyName(
10093
- circuitJson.source_net.get(sourceNetId)?.name
10094
- );
10095
- if (connectedSourceNetName) return connectedSourceNetName;
10096
- }
10097
- return null;
10098
- }
10099
- function getTraceOverlayInfo({
10100
- primitiveElement,
10101
- elements
10102
- }) {
10103
- const trace = su(elements).source_trace.get(
10104
- primitiveElement?.source_trace_id
10105
- );
10106
- const traceLength = primitiveElement.trace_length;
10107
- const hasTraceLength = typeof traceLength === "number";
10108
- const maxLength = trace?.max_length;
10109
- const text = hasTraceLength ? `${traceLength.toFixed(3)}${typeof maxLength === "number" ? ` / ${maxLength}` : ""} mm` : "";
10110
- const name = getAssociatedTraceName({ primitiveElement, elements }) ?? getNonEmptyName(trace?.display_name);
10111
- if (!text && !name) return null;
10112
- const isOverLength = Boolean(
10113
- hasTraceLength && typeof maxLength === "number" && traceLength > maxLength
10114
- );
10115
- return {
10116
- text,
10117
- name,
10118
- isOverLength
10119
- };
10120
- }
10121
-
10122
10148
  // src/lib/filter-traces-if-multiple.ts
10123
10149
  function filterTracesIfMultiple(filterTraces) {
10124
10150
  const { primitives, is_showing_multiple_traces_length, elements } = filterTraces;
@@ -11047,7 +11073,7 @@ import { css as css3 } from "@emotion/css";
11047
11073
  // package.json
11048
11074
  var package_default = {
11049
11075
  name: "@tscircuit/pcb-viewer",
11050
- version: "1.11.400",
11076
+ version: "1.11.402",
11051
11077
  main: "dist/index.js",
11052
11078
  type: "module",
11053
11079
  repository: {
@@ -11244,6 +11270,7 @@ var VisibilityContextMenu = ({
11244
11270
  position,
11245
11271
  onClose,
11246
11272
  onXRayNet,
11273
+ xRayDisplayName = "Net",
11247
11274
  onExitXRayNet
11248
11275
  }) => {
11249
11276
  const engine = useRenderingEngine();
@@ -11348,17 +11375,21 @@ var VisibilityContextMenu = ({
11348
11375
  ...stacked ? { overflowY: "auto" } : {}
11349
11376
  },
11350
11377
  children: [
11351
- onXRayNet && /* @__PURE__ */ jsx20(
11378
+ onXRayNet && /* @__PURE__ */ jsxs15(
11352
11379
  "button",
11353
11380
  {
11354
11381
  type: "button",
11355
11382
  role: "menuitem",
11356
11383
  className: itemStyle,
11384
+ style: { whiteSpace: "normal", overflowWrap: "anywhere" },
11357
11385
  onClick: () => {
11358
11386
  onXRayNet();
11359
11387
  onClose();
11360
11388
  },
11361
- children: "X-Ray Net"
11389
+ children: [
11390
+ "X-Ray ",
11391
+ xRayDisplayName
11392
+ ]
11362
11393
  }
11363
11394
  ),
11364
11395
  onExitXRayNet && /* @__PURE__ */ jsx20(
@@ -12132,8 +12163,8 @@ var ToolbarOverlay = ({
12132
12163
  elements,
12133
12164
  onContextMenuOpenChange,
12134
12165
  getNetAtPoint,
12135
- xRayNetId,
12136
- onXRayNetChange
12166
+ xRayNetIds = [],
12167
+ onXRayNetsChange
12137
12168
  }) => {
12138
12169
  const isSmallScreen = useIsSmallScreen();
12139
12170
  const {
@@ -12198,6 +12229,7 @@ var ToolbarOverlay = ({
12198
12229
  }));
12199
12230
  const [contextMenuPosition, setContextMenuPosition] = useState12(null);
12200
12231
  const [contextNetId, setContextNetId] = useState12();
12232
+ const [contextNetDisplayName, setContextNetDisplayName] = useState12("Net");
12201
12233
  const pointerStart = useRef14(null);
12202
12234
  const inEditMode = useGlobalStore((s) => s.in_edit_mode);
12203
12235
  const netAtEvent = (event) => {
@@ -12357,25 +12389,29 @@ var ToolbarOverlay = ({
12357
12389
  const start = pointerStart.current;
12358
12390
  if (!start || Math.hypot(event.clientX - start.x, event.clientY - start.y) > 4)
12359
12391
  return;
12360
- const net = netAtEvent(event);
12361
- if (!net) return;
12362
- if (net === xRayNetId) {
12363
- onXRayNetChange?.(null);
12392
+ const hit = netAtEvent(event);
12393
+ if (!hit) return;
12394
+ const net = hit.netId;
12395
+ if (xRayNetIds.includes(net)) {
12396
+ onXRayNetsChange?.(xRayNetIds.filter((id) => id !== net));
12364
12397
  closeContextMenu();
12365
12398
  } else {
12366
12399
  setContextNetId(net);
12400
+ setContextNetDisplayName(hit.displayName);
12367
12401
  onContextMenuOpenChange?.(true);
12368
12402
  setContextMenuPosition({ x: event.clientX, y: event.clientY });
12369
12403
  }
12370
12404
  },
12371
12405
  onDoubleClick: () => {
12372
- onXRayNetChange?.(null);
12406
+ onXRayNetsChange?.([]);
12373
12407
  closeContextMenu();
12374
12408
  },
12375
12409
  onContextMenu: (event) => {
12376
12410
  event.preventDefault();
12377
12411
  event.stopPropagation();
12378
- setContextNetId(netAtEvent(event));
12412
+ const hit = netAtEvent(event);
12413
+ setContextNetId(hit?.netId);
12414
+ setContextNetDisplayName(hit?.displayName ?? "Net");
12379
12415
  onContextMenuOpenChange?.(true);
12380
12416
  setContextMenuPosition({ x: event.clientX, y: event.clientY });
12381
12417
  },
@@ -12390,8 +12426,9 @@ var ToolbarOverlay = ({
12390
12426
  {
12391
12427
  position: contextMenuPosition,
12392
12428
  onClose: closeContextMenu,
12393
- onXRayNet: contextNetId && contextNetId !== xRayNetId ? () => onXRayNetChange?.(contextNetId) : void 0,
12394
- onExitXRayNet: xRayNetId ? () => onXRayNetChange?.(null) : void 0
12429
+ xRayDisplayName: contextNetDisplayName,
12430
+ onXRayNet: contextNetId && !xRayNetIds.includes(contextNetId) ? () => onXRayNetsChange?.([...xRayNetIds, contextNetId]) : void 0,
12431
+ onExitXRayNet: xRayNetIds.length > 0 ? () => onXRayNetsChange?.([]) : void 0
12395
12432
  }
12396
12433
  ),
12397
12434
  /* @__PURE__ */ jsxs17(
@@ -12866,36 +12903,43 @@ var CanvasElementsRenderer = (props) => {
12866
12903
  () => getFullConnectivityMapFromCircuitJson(elements),
12867
12904
  [elements]
12868
12905
  );
12869
- const [xRayNetId, setXRayNetId] = useState13(null);
12870
- useEffect19(() => setXRayNetId(null), [connectivityMap]);
12906
+ const [xRayNetIds, setXRayNetIds] = useState13([]);
12907
+ useEffect19(() => setXRayNetIds([]), [connectivityMap]);
12871
12908
  const xRayElements = useMemo10(
12872
- () => xRayNetId ? elementsToRender.filter(
12873
- (element) => isXRayCopper(element) && getElementNetId(element, connectivityMap) === xRayNetId
12909
+ () => xRayNetIds.length > 0 ? elementsToRender.filter(
12910
+ (element) => isXRayCopper(element) && xRayNetIds.includes(
12911
+ getElementNetId(element, connectivityMap) ?? ""
12912
+ )
12874
12913
  ) : void 0,
12875
- [elementsToRender, connectivityMap, xRayNetId]
12914
+ [elementsToRender, connectivityMap, xRayNetIds]
12876
12915
  );
12877
12916
  const getNetAtPoint = (point) => {
12878
12917
  if (!transform) return void 0;
12918
+ const describeNet = (netId, element) => ({
12919
+ netId,
12920
+ displayName: getXRayDisplayName(element, elements, connectivityMap)
12921
+ });
12879
12922
  const hits = getPrimitivesUnderPoint(
12880
12923
  primitivesWithoutInteractionMetadata,
12881
12924
  applyToPoint19(inverse6(transform), point),
12882
12925
  transform,
12883
12926
  selectedLayer,
12884
- Boolean(xRayNetId)
12927
+ xRayNetIds.length > 0
12885
12928
  );
12886
12929
  const copper = hits.filter(
12887
12930
  (p) => isXRayCopper(p._element) && p.layer !== "drill"
12888
12931
  );
12889
- if (xRayNetId && copper.some(
12890
- (p) => getElementNetId(p._element, connectivityMap) === xRayNetId
12891
- ))
12892
- return xRayNetId;
12893
12932
  copper.sort(
12894
12933
  (a, b) => Number(b.layer === selectedLayer) - Number(a.layer === selectedLayer)
12895
12934
  );
12896
12935
  for (const primitive of copper) {
12897
12936
  const net = getElementNetId(primitive._element, connectivityMap);
12898
- if (net) return net;
12937
+ if (net && xRayNetIds.includes(net))
12938
+ return describeNet(net, primitive._element);
12939
+ }
12940
+ for (const primitive of copper) {
12941
+ const net = getElementNetId(primitive._element, connectivityMap);
12942
+ if (net) return describeNet(net, primitive._element);
12899
12943
  }
12900
12944
  };
12901
12945
  const [hoverState, setHoverState] = useState13({
@@ -12969,7 +13013,7 @@ var CanvasElementsRenderer = (props) => {
12969
13013
  props.width
12970
13014
  ]);
12971
13015
  const primitives = useMemo10(() => {
12972
- if (xRayNetId) return primitivesWithoutInteractionMetadata;
13016
+ if (xRayNetIds.length > 0) return primitivesWithoutInteractionMetadata;
12973
13017
  const combinedPrimitiveIds = [
12974
13018
  ...hoverState.primitiveIdsInMousedOverNet,
12975
13019
  ...errorRelatedIds
@@ -12983,7 +13027,7 @@ var CanvasElementsRenderer = (props) => {
12983
13027
  primitivesWithoutInteractionMetadata,
12984
13028
  hoverState,
12985
13029
  errorRelatedIds,
12986
- xRayNetId
13030
+ xRayNetIds
12987
13031
  ]);
12988
13032
  const onMouseOverPrimitives = useCallback11(
12989
13033
  (primitivesHoveredOver) => {
@@ -13025,7 +13069,7 @@ var CanvasElementsRenderer = (props) => {
13025
13069
  transform,
13026
13070
  primitives: primitivesWithoutInteractionMetadata,
13027
13071
  selectedLayer,
13028
- disableHoverHighlights: Boolean(xRayNetId),
13072
+ disableHoverHighlights: xRayNetIds.length > 0,
13029
13073
  onMouseHoverOverPrimitives: onMouseOverPrimitives,
13030
13074
  children: /* @__PURE__ */ jsx25(
13031
13075
  EditPlacementOverlay,
@@ -13049,8 +13093,8 @@ var CanvasElementsRenderer = (props) => {
13049
13093
  {
13050
13094
  elements,
13051
13095
  getNetAtPoint,
13052
- xRayNetId,
13053
- onXRayNetChange: setXRayNetId,
13096
+ xRayNetIds,
13097
+ onXRayNetsChange: setXRayNetIds,
13054
13098
  onContextMenuOpenChange: props.onContextMenuOpenChange,
13055
13099
  children: /* @__PURE__ */ jsx25(ErrorOverlay, { transform, elements, children: /* @__PURE__ */ jsx25(RatsNestOverlay, { transform, soup: elements, children: /* @__PURE__ */ jsx25(
13056
13100
  PcbGroupOverlay,