@tscircuit/pcb-viewer 1.11.439 → 1.11.441

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
@@ -9394,6 +9394,53 @@ var getBoundsOfPcbElements = (elements) => {
9394
9394
  }
9395
9395
  return bounds;
9396
9396
  };
9397
+ var getBoardBounds = (board) => {
9398
+ if (board.width && board.height && board.center) {
9399
+ const halfWidth = board.width / 2;
9400
+ const halfHeight = board.height / 2;
9401
+ return {
9402
+ minX: board.center.x - halfWidth,
9403
+ minY: board.center.y - halfHeight,
9404
+ maxX: board.center.x + halfWidth,
9405
+ maxY: board.center.y + halfHeight,
9406
+ width: board.width,
9407
+ height: board.height,
9408
+ center: {
9409
+ x: board.center.x,
9410
+ y: board.center.y
9411
+ }
9412
+ };
9413
+ }
9414
+ if (!board.outline || board.outline.length === 0) {
9415
+ throw new Error(
9416
+ "Unable to compute board bounds. pcb_board must include width/height/center or a non-empty outline."
9417
+ );
9418
+ }
9419
+ let minX = Number.POSITIVE_INFINITY;
9420
+ let minY = Number.POSITIVE_INFINITY;
9421
+ let maxX = Number.NEGATIVE_INFINITY;
9422
+ let maxY = Number.NEGATIVE_INFINITY;
9423
+ for (const point2 of board.outline) {
9424
+ minX = Math.min(minX, point2.x);
9425
+ minY = Math.min(minY, point2.y);
9426
+ maxX = Math.max(maxX, point2.x);
9427
+ maxY = Math.max(maxY, point2.y);
9428
+ }
9429
+ const width = maxX - minX;
9430
+ const height = maxY - minY;
9431
+ return {
9432
+ minX,
9433
+ minY,
9434
+ maxX,
9435
+ maxY,
9436
+ width,
9437
+ height,
9438
+ center: {
9439
+ x: minX + width / 2,
9440
+ y: minY + height / 2
9441
+ }
9442
+ };
9443
+ };
9397
9444
  function stringHash(str) {
9398
9445
  let hash = 0;
9399
9446
  if (str.length == 0) return hash;
@@ -9717,6 +9764,27 @@ var getPlatedHolePolygon = (pad, componentRotation = 0) => {
9717
9764
  );
9718
9765
  };
9719
9766
 
9767
+ // src/lib/get-pcb-viewport-bounds.ts
9768
+ function getPcbViewportBounds(elements) {
9769
+ const pcbElements = elements.filter(
9770
+ (element) => element.type.startsWith("pcb_")
9771
+ );
9772
+ if (pcbElements.length === 0) {
9773
+ return { center: { x: 0, y: 0 }, width: 1e-3, height: 1e-3 };
9774
+ }
9775
+ const boardsAndPanels = pcbElements.filter(
9776
+ (element) => element.type === "pcb_board" || element.type === "pcb_panel"
9777
+ ).map((element) => {
9778
+ if (element.type !== "pcb_board" || !element.outline?.length)
9779
+ return element;
9780
+ const { center, width, height } = getBoardBounds(element);
9781
+ return { ...element, center, width, height };
9782
+ });
9783
+ return findBoundsAndCenter(
9784
+ boardsAndPanels.length > 0 ? boardsAndPanels : pcbElements
9785
+ );
9786
+ }
9787
+
9720
9788
  // src/lib/apply-edit-events.ts
9721
9789
  import { translate as translate6 } from "transformation-matrix";
9722
9790
  function applyEditEvents({
@@ -9777,13 +9845,19 @@ function applyEditEvents({
9777
9845
  // src/lib/calculate-board-size-key.ts
9778
9846
  var calculateBoardSizeKey = (circuitJson) => {
9779
9847
  if (!circuitJson) return "empty";
9780
- const board = circuitJson.find((e) => e.type === "pcb_board");
9781
- if (!board) return "no-board";
9782
- const round = (n) => Math.round(n * 1e3) / 1e3;
9783
- if (board.outline) {
9784
- return board.outline.map((o) => `${round(o.x)}_${round(o.y)}`).join(",");
9785
- }
9786
- return `${round(board.width)}_${round(board.height)}`;
9848
+ const boardSizeKeys = circuitJson.filter(
9849
+ (element) => element.type === "pcb_board" || element.type === "pcb_panel"
9850
+ ).map(
9851
+ (element) => JSON.stringify([
9852
+ element.type,
9853
+ element.center.x,
9854
+ element.center.y,
9855
+ element.width,
9856
+ element.height,
9857
+ element.type === "pcb_board" ? element.outline?.map(({ x, y }) => [x, y]) : void 0
9858
+ ])
9859
+ ).sort();
9860
+ return boardSizeKeys.length > 0 ? boardSizeKeys.join(";") : "no-board";
9787
9861
  };
9788
9862
 
9789
9863
  // src/lib/calculate-circuit-json-key.ts
@@ -17373,21 +17447,33 @@ var FocusedPcbComponentOverlay = ({
17373
17447
  return /* @__PURE__ */ jsx10(
17374
17448
  "div",
17375
17449
  {
17376
- "data-focused-pcb-component-id": componentId,
17450
+ "data-pcb-focus-viewport": true,
17377
17451
  style: {
17378
17452
  position: "absolute",
17379
- left: center.x - width / 2,
17380
- top: center.y - height / 2,
17381
- width,
17382
- height,
17383
- transform: `rotate(${rotation}rad)`,
17384
- transformOrigin: "center",
17385
- border: "2px solid #3399ff",
17386
- borderRadius: 4,
17387
- boxSizing: "border-box",
17453
+ inset: 0,
17454
+ overflow: "clip",
17388
17455
  pointerEvents: "none",
17389
17456
  zIndex: zIndexMap.elementOverlay
17390
- }
17457
+ },
17458
+ children: /* @__PURE__ */ jsx10(
17459
+ "div",
17460
+ {
17461
+ "data-focused-pcb-component-id": componentId,
17462
+ style: {
17463
+ position: "absolute",
17464
+ left: center.x - width / 2,
17465
+ top: center.y - height / 2,
17466
+ width,
17467
+ height,
17468
+ transform: `rotate(${rotation}rad)`,
17469
+ transformOrigin: "center",
17470
+ border: "2px solid #3399ff",
17471
+ borderRadius: 4,
17472
+ boxSizing: "border-box",
17473
+ pointerEvents: "none"
17474
+ }
17475
+ }
17476
+ )
17391
17477
  }
17392
17478
  );
17393
17479
  };
@@ -19125,7 +19211,7 @@ import {
19125
19211
  // package.json
19126
19212
  var package_default = {
19127
19213
  name: "@tscircuit/pcb-viewer",
19128
- version: "1.11.438",
19214
+ version: "1.11.440",
19129
19215
  main: "dist/index.js",
19130
19216
  type: "module",
19131
19217
  repository: {
@@ -19210,7 +19296,48 @@ var package_default = {
19210
19296
  // src/components/ComponentSearch.tsx
19211
19297
  import { css as css2 } from "@emotion/css";
19212
19298
  import { useEffect as useEffect15, useId, useMemo as useMemo11, useRef as useRef11, useState as useState9 } from "react";
19213
- import { jsx as jsx20, jsxs as jsxs14 } from "react/jsx-runtime";
19299
+
19300
+ // src/components/ToolbarButton.tsx
19301
+ import { jsx as jsx20 } from "react/jsx-runtime";
19302
+ var getToolbarButtonStyle = (isSmallScreen) => ({
19303
+ backgroundColor: "#1F1F1F",
19304
+ border: "1px solid #666",
19305
+ margin: 0,
19306
+ padding: 4,
19307
+ paddingLeft: isSmallScreen ? 8 : 6,
19308
+ paddingRight: isSmallScreen ? 8 : 6,
19309
+ borderRadius: 2,
19310
+ color: "#eee",
19311
+ cursor: "pointer",
19312
+ fontSize: 12,
19313
+ height: "fit-content",
19314
+ userSelect: "none",
19315
+ whiteSpace: "nowrap"
19316
+ });
19317
+ var ToolbarButton = ({
19318
+ children,
19319
+ isSmallScreen,
19320
+ onClick,
19321
+ ...props
19322
+ }) => {
19323
+ const { style: touchStyle, ...touchHandlers } = useMobileTouch(onClick);
19324
+ return /* @__PURE__ */ jsx20(
19325
+ "div",
19326
+ {
19327
+ ...props,
19328
+ ...touchHandlers,
19329
+ style: {
19330
+ ...getToolbarButtonStyle(isSmallScreen),
19331
+ ...touchStyle,
19332
+ ...props.style
19333
+ },
19334
+ children
19335
+ }
19336
+ );
19337
+ };
19338
+
19339
+ // src/components/ComponentSearch.tsx
19340
+ import { jsx as jsx21, jsxs as jsxs14 } from "react/jsx-runtime";
19214
19341
  var buttonClass = css2`
19215
19342
  background: #1f1f1f;
19216
19343
  border: 1px solid #666;
@@ -19225,6 +19352,7 @@ var ComponentSearch = ({
19225
19352
  elements,
19226
19353
  onSelect
19227
19354
  }) => {
19355
+ const isSmallScreen = useIsSmallScreen();
19228
19356
  const [open, setOpen] = useState9(false);
19229
19357
  const [query, setQuery] = useState9("");
19230
19358
  const [activeComponentId, setActiveComponentId] = useState9(
@@ -19297,7 +19425,7 @@ var ComponentSearch = ({
19297
19425
  }
19298
19426
  },
19299
19427
  children: [
19300
- /* @__PURE__ */ jsx20(
19428
+ /* @__PURE__ */ jsx21(
19301
19429
  "button",
19302
19430
  {
19303
19431
  ref: toggle,
@@ -19305,15 +19433,16 @@ var ComponentSearch = ({
19305
19433
  className: buttonClass,
19306
19434
  "aria-label": "Search components",
19307
19435
  title: "Search components",
19308
- style: { display: "flex", alignItems: "center", padding: "4px 6px" },
19436
+ style: getToolbarButtonStyle(isSmallScreen),
19309
19437
  "aria-expanded": open,
19310
19438
  "aria-controls": panelId,
19311
19439
  onClick: () => setOpen(!open),
19312
19440
  children: /* @__PURE__ */ jsxs14(
19313
19441
  "svg",
19314
19442
  {
19315
- width: "14",
19316
- height: "14",
19443
+ width: "1em",
19444
+ height: "1em",
19445
+ style: { verticalAlign: "middle" },
19317
19446
  viewBox: "0 0 24 24",
19318
19447
  fill: "none",
19319
19448
  stroke: "currentColor",
@@ -19321,8 +19450,8 @@ var ComponentSearch = ({
19321
19450
  strokeLinecap: "round",
19322
19451
  "aria-hidden": "true",
19323
19452
  children: [
19324
- /* @__PURE__ */ jsx20("circle", { cx: "10.5", cy: "10.5", r: "6.5" }),
19325
- /* @__PURE__ */ jsx20("path", { d: "m16 16 5 5" })
19453
+ /* @__PURE__ */ jsx21("circle", { cx: "10.5", cy: "10.5", r: "6.5" }),
19454
+ /* @__PURE__ */ jsx21("path", { d: "m16 16 5 5" })
19326
19455
  ]
19327
19456
  }
19328
19457
  )
@@ -19346,7 +19475,7 @@ var ComponentSearch = ({
19346
19475
  boxSizing: "border-box"
19347
19476
  },
19348
19477
  children: [
19349
- /* @__PURE__ */ jsx20(
19478
+ /* @__PURE__ */ jsx21(
19350
19479
  "input",
19351
19480
  {
19352
19481
  ref: input,
@@ -19379,7 +19508,7 @@ var ComponentSearch = ({
19379
19508
  }
19380
19509
  }
19381
19510
  ),
19382
- !normalizedQuery ? /* @__PURE__ */ jsx20("output", { style: { display: "block", marginTop: 8 }, children: "Type a component name." }) : matches.length === 0 ? /* @__PURE__ */ jsx20("output", { style: { display: "block", marginTop: 8 }, children: "No matching components." }) : /* @__PURE__ */ jsx20(
19511
+ !normalizedQuery ? /* @__PURE__ */ jsx21("output", { style: { display: "block", marginTop: 8 }, children: "Type a component name." }) : matches.length === 0 ? /* @__PURE__ */ jsx21("output", { style: { display: "block", marginTop: 8 }, children: "No matching components." }) : /* @__PURE__ */ jsx21(
19383
19512
  "div",
19384
19513
  {
19385
19514
  id: `${panelId}-results`,
@@ -19395,7 +19524,7 @@ var ComponentSearch = ({
19395
19524
  maxHeight: 220,
19396
19525
  overflowY: "auto"
19397
19526
  },
19398
- children: matches.map((component, index) => /* @__PURE__ */ jsx20("div", { role: "presentation", children: /* @__PURE__ */ jsxs14(
19527
+ children: matches.map((component, index) => /* @__PURE__ */ jsx21("div", { role: "presentation", children: /* @__PURE__ */ jsxs14(
19399
19528
  "button",
19400
19529
  {
19401
19530
  type: "button",
@@ -19417,8 +19546,8 @@ var ComponentSearch = ({
19417
19546
  },
19418
19547
  onClick: () => select(component.id),
19419
19548
  children: [
19420
- /* @__PURE__ */ jsx20("span", { children: component.name }),
19421
- /* @__PURE__ */ jsx20("span", { style: { color: "#aaa" }, children: component.layer })
19549
+ /* @__PURE__ */ jsx21("span", { children: component.name }),
19550
+ /* @__PURE__ */ jsx21("span", { style: { color: "#aaa" }, children: component.layer })
19422
19551
  ]
19423
19552
  }
19424
19553
  ) }, component.id))
@@ -19432,42 +19561,6 @@ var ComponentSearch = ({
19432
19561
  );
19433
19562
  };
19434
19563
 
19435
- // src/components/ToolbarButton.tsx
19436
- import { jsx as jsx21 } from "react/jsx-runtime";
19437
- var ToolbarButton = ({
19438
- children,
19439
- isSmallScreen,
19440
- onClick,
19441
- ...props
19442
- }) => {
19443
- const { style: touchStyle, ...touchHandlers } = useMobileTouch(onClick);
19444
- return /* @__PURE__ */ jsx21(
19445
- "div",
19446
- {
19447
- ...props,
19448
- ...touchHandlers,
19449
- style: {
19450
- backgroundColor: "#1F1F1F",
19451
- border: "1px solid #666",
19452
- margin: 0,
19453
- padding: 4,
19454
- paddingLeft: isSmallScreen ? 8 : 6,
19455
- paddingRight: isSmallScreen ? 8 : 6,
19456
- borderRadius: 2,
19457
- color: "#eee",
19458
- cursor: "pointer",
19459
- fontSize: 12,
19460
- height: "fit-content",
19461
- userSelect: "none",
19462
- whiteSpace: "nowrap",
19463
- ...touchStyle,
19464
- ...props.style
19465
- },
19466
- children
19467
- }
19468
- );
19469
- };
19470
-
19471
19564
  // src/components/ToolbarErrorDropdown.tsx
19472
19565
  import { useCallback as useCallback9, useMemo as useMemo12, useState as useState10 } from "react";
19473
19566
 
@@ -22106,14 +22199,13 @@ var PCBViewer = ({
22106
22199
  () => calculateCircuitJsonKey(circuitJson),
22107
22200
  [circuitJson]
22108
22201
  );
22109
- const boardSizeKey = calculateBoardSizeKey(circuitJson);
22202
+ const boardSizeKey = useMemo17(
22203
+ () => calculateBoardSizeKey(circuitJson),
22204
+ [circuitJson]
22205
+ );
22110
22206
  const resetTransform = () => {
22111
22207
  const elmBounds = refDimensions?.width > 0 ? refDimensions : { width: 500, height: 500 };
22112
- const { center, width, height: height2 } = elements.some(
22113
- (e) => e.type.startsWith("pcb_")
22114
- ) ? findBoundsAndCenter(
22115
- elements.filter((e) => e.type.startsWith("pcb_"))
22116
- ) : { center: { x: 0, y: 0 }, width: 1e-3, height: 1e-3 };
22208
+ const { center, width, height: height2 } = getPcbViewportBounds(elements);
22117
22209
  const scaleFactor = Math.min(
22118
22210
  (elmBounds.width ?? 0) / width,
22119
22211
  (elmBounds.height ?? 0) / height2,