@tscircuit/pcb-viewer 1.11.407 → 1.11.408
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/README.md +23 -0
- package/dist/index.d.ts +12 -2
- package/dist/index.js +123 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -76,6 +76,7 @@ The PCBViewer component accepts these props:
|
|
|
76
76
|
- `editEvents`: Array of edit events to apply
|
|
77
77
|
- `onEditEventsChanged`: Callback when edit events change
|
|
78
78
|
- `onBoundsSelected`: Callback when the Bounds tool completes a rectangle selection. Receives `{ minX, minY, maxX, maxY }`.
|
|
79
|
+
- `onViewSchematicComponent`: Optional callback for the pad context menu action `↗︎ U1 on Schematic`. Receives `{ source_component_id, pcb_component_id, refdes }`. Omit it when the host schematic tab is disabled.
|
|
79
80
|
- `initialState`: Initial state for the viewer
|
|
80
81
|
|
|
81
82
|
### Features
|
|
@@ -123,3 +124,25 @@ The WebGPU renderer is installed as a bundled devDependency from
|
|
|
123
124
|
`https://jscdn.tscircuit.com/@tscircuit/circuit-json-webgpu/0.0.3.tgz`.
|
|
124
125
|
The renderer repository includes a TypeScript local render command and 597
|
|
125
126
|
SVG-left/WebGPU-right feature snapshot tests.
|
|
127
|
+
|
|
128
|
+
### Navigate from a pad to a schematic
|
|
129
|
+
|
|
130
|
+
Click or right-click an SMT pad or plated hole to see its component's manufacturer
|
|
131
|
+
part number in a disabled row at the bottom of the context menu, when available.
|
|
132
|
+
Passing `onViewSchematicComponent` also enables the schematic action:
|
|
133
|
+
|
|
134
|
+
```tsx
|
|
135
|
+
<PCBViewer
|
|
136
|
+
circuitJson={circuitJson}
|
|
137
|
+
onViewSchematicComponent={schematicTabEnabled ? ({ source_component_id }) => {
|
|
138
|
+
// The host switches tabs and focuses the matching schematic component.
|
|
139
|
+
showSchematicComponent(source_component_id)
|
|
140
|
+
} : undefined}
|
|
141
|
+
/>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The callback uses the source component ID so hosts can resolve the corresponding
|
|
145
|
+
schematic component even when reference designators repeat across subcircuits.
|
|
146
|
+
RunFrame and CircuitJsonViewer can forward this callback from their shared PCB
|
|
147
|
+
preview when the schematic tab is enabled. Tab switching and schematic zooming
|
|
148
|
+
are implemented by the host; PCBViewer does not require schematic elements.
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,13 @@ import { GraphicsObject } from 'graphics-debug';
|
|
|
4
4
|
import { ManualEditEvent } from '@tscircuit/props';
|
|
5
5
|
import { Matrix } from 'transformation-matrix';
|
|
6
6
|
|
|
7
|
+
/** Stable identity for hosts to locate the component in their schematic. */
|
|
8
|
+
interface ViewSchematicComponentEvent {
|
|
9
|
+
source_component_id: string;
|
|
10
|
+
pcb_component_id: string;
|
|
11
|
+
refdes: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
7
14
|
interface State {
|
|
8
15
|
hidden_layer_opacity: number;
|
|
9
16
|
setHiddenLayerOpacity: (opacity: number) => void;
|
|
@@ -81,13 +88,15 @@ type Props = {
|
|
|
81
88
|
editEvents?: ManualEditEvent[];
|
|
82
89
|
initialState?: Partial<StateProps>;
|
|
83
90
|
onEditEventsChanged?: (editEvents: ManualEditEvent[]) => void;
|
|
91
|
+
/** Omit when the host has no enabled schematic tab. */
|
|
92
|
+
onViewSchematicComponent?: (event: ViewSchematicComponentEvent) => void;
|
|
84
93
|
onBoundsSelected?: (bounds: BoundsSelection) => void;
|
|
85
94
|
focusOnHover?: boolean;
|
|
86
95
|
clickToInteractEnabled?: boolean;
|
|
87
96
|
debugGraphics?: GraphicsObject | null;
|
|
88
97
|
disablePcbGroups?: boolean;
|
|
89
98
|
};
|
|
90
|
-
declare const PCBViewer: ({ circuitJson, debugGraphics, height, renderer, initialState, allowEditing, editEvents: editEventsProp, onEditEventsChanged, onBoundsSelected, focusOnHover, clickToInteractEnabled, disablePcbGroups, }: Props) => react_jsx_runtime.JSX.Element;
|
|
99
|
+
declare const PCBViewer: ({ circuitJson, debugGraphics, height, renderer, initialState, allowEditing, editEvents: editEventsProp, onEditEventsChanged, onBoundsSelected, onViewSchematicComponent, focusOnHover, clickToInteractEnabled, disablePcbGroups, }: Props) => react_jsx_runtime.JSX.Element;
|
|
91
100
|
|
|
92
101
|
interface CanvasElementsRendererProps {
|
|
93
102
|
elements: AnyCircuitElement[];
|
|
@@ -100,6 +109,7 @@ interface CanvasElementsRendererProps {
|
|
|
100
109
|
grid?: GridConfig;
|
|
101
110
|
allowEditing: boolean;
|
|
102
111
|
focusOnHover?: boolean;
|
|
112
|
+
onViewSchematicComponent?: (event: ViewSchematicComponentEvent) => void;
|
|
103
113
|
onBoundsSelected?: (bounds: BoundsSelection) => void;
|
|
104
114
|
onContextMenuOpenChange?: (open: boolean) => void;
|
|
105
115
|
cancelPanDrag: () => void;
|
|
@@ -108,4 +118,4 @@ interface CanvasElementsRendererProps {
|
|
|
108
118
|
}
|
|
109
119
|
declare const CanvasElementsRenderer: (props: CanvasElementsRendererProps) => react_jsx_runtime.JSX.Element;
|
|
110
120
|
|
|
111
|
-
export { CanvasElementsRenderer, PCBViewer };
|
|
121
|
+
export { CanvasElementsRenderer, PCBViewer, type ViewSchematicComponentEvent };
|
package/dist/index.js
CHANGED
|
@@ -10100,6 +10100,25 @@ var useMouseMatrixTransform = (props = {}) => {
|
|
|
10100
10100
|
};
|
|
10101
10101
|
var src_default = useMouseMatrixTransform;
|
|
10102
10102
|
|
|
10103
|
+
// src/lib/get-pad-component.ts
|
|
10104
|
+
var getPadComponent = (pad, elements) => {
|
|
10105
|
+
if (pad?.type !== "pcb_smtpad" && pad?.type !== "pcb_plated_hole") return;
|
|
10106
|
+
const pcbComponent = elements.find(
|
|
10107
|
+
(element) => element.type === "pcb_component" && element.pcb_component_id === pad.pcb_component_id
|
|
10108
|
+
);
|
|
10109
|
+
if (pcbComponent?.type !== "pcb_component") return;
|
|
10110
|
+
const sourceComponent = elements.find(
|
|
10111
|
+
(element) => element.type === "source_component" && element.source_component_id === pcbComponent.source_component_id
|
|
10112
|
+
);
|
|
10113
|
+
if (sourceComponent?.type !== "source_component") return;
|
|
10114
|
+
return {
|
|
10115
|
+
source_component_id: sourceComponent.source_component_id,
|
|
10116
|
+
pcb_component_id: pcbComponent.pcb_component_id,
|
|
10117
|
+
refdes: sourceComponent.name,
|
|
10118
|
+
manufacturer_part_number: sourceComponent.manufacturer_part_number
|
|
10119
|
+
};
|
|
10120
|
+
};
|
|
10121
|
+
|
|
10103
10122
|
// node_modules/circuit-json-to-connectivity-map/dist/index.js
|
|
10104
10123
|
import { doesLineIntersectLine } from "@tscircuit/math-utils";
|
|
10105
10124
|
function findConnectedNetworks(connections) {
|
|
@@ -18323,7 +18342,7 @@ import { css as css3 } from "@emotion/css";
|
|
|
18323
18342
|
// package.json
|
|
18324
18343
|
var package_default = {
|
|
18325
18344
|
name: "@tscircuit/pcb-viewer",
|
|
18326
|
-
version: "1.11.
|
|
18345
|
+
version: "1.11.407",
|
|
18327
18346
|
main: "dist/index.js",
|
|
18328
18347
|
type: "module",
|
|
18329
18348
|
repository: {
|
|
@@ -18517,6 +18536,8 @@ var itemStyle = css2`
|
|
|
18517
18536
|
&:hover, &:focus-visible { background: #444; }
|
|
18518
18537
|
`;
|
|
18519
18538
|
var VisibilityContextMenu = ({
|
|
18539
|
+
component,
|
|
18540
|
+
onViewSchematicComponent,
|
|
18520
18541
|
position,
|
|
18521
18542
|
onClose,
|
|
18522
18543
|
onXRayNet,
|
|
@@ -18584,9 +18605,9 @@ var VisibilityContextMenu = ({
|
|
|
18584
18605
|
const button = event.target.closest("button");
|
|
18585
18606
|
if (!button) return;
|
|
18586
18607
|
const menu = button.closest('[role="menu"]');
|
|
18587
|
-
const items = Array.from(
|
|
18588
|
-
|
|
18589
|
-
);
|
|
18608
|
+
const items = Array.from(
|
|
18609
|
+
menu.querySelectorAll("button:not(:disabled)")
|
|
18610
|
+
).filter((item) => item.closest('[role="menu"]') === menu);
|
|
18590
18611
|
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
18591
18612
|
event.preventDefault();
|
|
18592
18613
|
const step = event.key === "ArrowDown" ? 1 : -1;
|
|
@@ -18829,6 +18850,65 @@ var VisibilityContextMenu = ({
|
|
|
18829
18850
|
)
|
|
18830
18851
|
]
|
|
18831
18852
|
}
|
|
18853
|
+
),
|
|
18854
|
+
component && onViewSchematicComponent && /* @__PURE__ */ jsxs15(
|
|
18855
|
+
"button",
|
|
18856
|
+
{
|
|
18857
|
+
type: "button",
|
|
18858
|
+
role: "menuitem",
|
|
18859
|
+
className: itemStyle,
|
|
18860
|
+
style: { whiteSpace: "normal", overflowWrap: "anywhere" },
|
|
18861
|
+
onClick: () => {
|
|
18862
|
+
onClose();
|
|
18863
|
+
onViewSchematicComponent({
|
|
18864
|
+
source_component_id: component.source_component_id,
|
|
18865
|
+
pcb_component_id: component.pcb_component_id,
|
|
18866
|
+
refdes: component.refdes
|
|
18867
|
+
});
|
|
18868
|
+
},
|
|
18869
|
+
children: [
|
|
18870
|
+
/* @__PURE__ */ jsx20(
|
|
18871
|
+
"svg",
|
|
18872
|
+
{
|
|
18873
|
+
"aria-hidden": "true",
|
|
18874
|
+
width: "12",
|
|
18875
|
+
height: "12",
|
|
18876
|
+
viewBox: "0 0 16 16",
|
|
18877
|
+
fill: "none",
|
|
18878
|
+
stroke: "currentColor",
|
|
18879
|
+
strokeWidth: "2.5",
|
|
18880
|
+
strokeLinecap: "round",
|
|
18881
|
+
strokeLinejoin: "round",
|
|
18882
|
+
style: { verticalAlign: "-1px", marginRight: 5 },
|
|
18883
|
+
children: /* @__PURE__ */ jsx20("path", { d: "M5 11 11 5M5 5h6v6" })
|
|
18884
|
+
}
|
|
18885
|
+
),
|
|
18886
|
+
component.refdes,
|
|
18887
|
+
" on Schematic"
|
|
18888
|
+
]
|
|
18889
|
+
}
|
|
18890
|
+
),
|
|
18891
|
+
component?.manufacturer_part_number && /* @__PURE__ */ jsxs15(
|
|
18892
|
+
"button",
|
|
18893
|
+
{
|
|
18894
|
+
type: "button",
|
|
18895
|
+
role: "menuitem",
|
|
18896
|
+
disabled: true,
|
|
18897
|
+
"aria-label": `Manufacturer part number: ${component.manufacturer_part_number}`,
|
|
18898
|
+
className: itemStyle,
|
|
18899
|
+
style: {
|
|
18900
|
+
marginTop: 4,
|
|
18901
|
+
background: "#292929",
|
|
18902
|
+
color: "#aaa",
|
|
18903
|
+
cursor: "default",
|
|
18904
|
+
whiteSpace: "normal",
|
|
18905
|
+
overflowWrap: "anywhere"
|
|
18906
|
+
},
|
|
18907
|
+
children: [
|
|
18908
|
+
"MPN: ",
|
|
18909
|
+
component.manufacturer_part_number
|
|
18910
|
+
]
|
|
18911
|
+
}
|
|
18832
18912
|
)
|
|
18833
18913
|
]
|
|
18834
18914
|
}
|
|
@@ -19433,6 +19513,8 @@ var ToolbarOverlay = ({
|
|
|
19433
19513
|
elements,
|
|
19434
19514
|
onContextMenuOpenChange,
|
|
19435
19515
|
getNetAtPoint,
|
|
19516
|
+
getComponentAtPoint,
|
|
19517
|
+
onViewSchematicComponent,
|
|
19436
19518
|
xRayNetIds = [],
|
|
19437
19519
|
onXRayNetsChange
|
|
19438
19520
|
}) => {
|
|
@@ -19501,6 +19583,14 @@ var ToolbarOverlay = ({
|
|
|
19501
19583
|
const [contextGroups, setContextGroups] = useState12([]);
|
|
19502
19584
|
const [contextNetId, setContextNetId] = useState12();
|
|
19503
19585
|
const [contextNetDisplayName, setContextNetDisplayName] = useState12("Net");
|
|
19586
|
+
const [contextComponent, setContextComponent] = useState12();
|
|
19587
|
+
const componentAtEvent = (event) => {
|
|
19588
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
19589
|
+
return getComponentAtPoint?.({
|
|
19590
|
+
x: event.clientX - rect.left,
|
|
19591
|
+
y: event.clientY - rect.top
|
|
19592
|
+
});
|
|
19593
|
+
};
|
|
19504
19594
|
const pointerStart = useRef14(null);
|
|
19505
19595
|
const inEditMode = useGlobalStore((s) => s.in_edit_mode);
|
|
19506
19596
|
const netAtEvent = (event) => {
|
|
@@ -19661,15 +19751,17 @@ var ToolbarOverlay = ({
|
|
|
19661
19751
|
if (!start || Math.hypot(event.clientX - start.x, event.clientY - start.y) > 4)
|
|
19662
19752
|
return;
|
|
19663
19753
|
const hit = netAtEvent(event);
|
|
19664
|
-
|
|
19665
|
-
|
|
19666
|
-
|
|
19754
|
+
const component = componentAtEvent(event);
|
|
19755
|
+
if (!hit && !component) return;
|
|
19756
|
+
const net = hit?.netId;
|
|
19757
|
+
if (net && xRayNetIds.includes(net)) {
|
|
19667
19758
|
onXRayNetsChange?.(xRayNetIds.filter((id) => id !== net));
|
|
19668
19759
|
closeContextMenu();
|
|
19669
19760
|
} else {
|
|
19670
19761
|
setContextNetId(net);
|
|
19671
|
-
setContextNetDisplayName(hit
|
|
19672
|
-
setContextGroups(hit
|
|
19762
|
+
setContextNetDisplayName(hit?.displayName ?? "Net");
|
|
19763
|
+
setContextGroups(hit?.groups ?? []);
|
|
19764
|
+
setContextComponent(component);
|
|
19673
19765
|
onContextMenuOpenChange?.(true);
|
|
19674
19766
|
setContextMenuPosition({ x: event.clientX, y: event.clientY });
|
|
19675
19767
|
}
|
|
@@ -19682,6 +19774,7 @@ var ToolbarOverlay = ({
|
|
|
19682
19774
|
event.preventDefault();
|
|
19683
19775
|
event.stopPropagation();
|
|
19684
19776
|
const hit = netAtEvent(event);
|
|
19777
|
+
setContextComponent(componentAtEvent(event));
|
|
19685
19778
|
setContextNetId(hit?.netId);
|
|
19686
19779
|
setContextNetDisplayName(hit?.displayName ?? "Net");
|
|
19687
19780
|
setContextGroups(hit?.groups ?? []);
|
|
@@ -19698,6 +19791,8 @@ var ToolbarOverlay = ({
|
|
|
19698
19791
|
VisibilityContextMenu,
|
|
19699
19792
|
{
|
|
19700
19793
|
position: contextMenuPosition,
|
|
19794
|
+
component: contextComponent,
|
|
19795
|
+
onViewSchematicComponent,
|
|
19701
19796
|
onClose: closeContextMenu,
|
|
19702
19797
|
xRayDisplayName: contextNetDisplayName,
|
|
19703
19798
|
xRayGroups: contextGroups.filter(
|
|
@@ -20197,6 +20292,21 @@ var CanvasElementsRenderer = (props) => {
|
|
|
20197
20292
|
(primitive) => (primitive._element?.type === "pcb_smtpad" || primitive._element?.type === "pcb_plated_hole") && selectedElements.has(primitive._element)
|
|
20198
20293
|
);
|
|
20199
20294
|
}, [primitivesWithoutInteractionMetadata, xRayElements]);
|
|
20295
|
+
const getComponentAtPoint = (point2) => {
|
|
20296
|
+
if (!transform) return;
|
|
20297
|
+
const hits = getPrimitivesUnderPoint(
|
|
20298
|
+
primitivesWithoutInteractionMetadata,
|
|
20299
|
+
applyToPoint19(inverse6(transform), point2),
|
|
20300
|
+
transform,
|
|
20301
|
+
selectedLayer
|
|
20302
|
+
).filter(
|
|
20303
|
+
(p) => p._element?.type === "pcb_smtpad" || p._element?.type === "pcb_plated_hole"
|
|
20304
|
+
);
|
|
20305
|
+
hits.sort(
|
|
20306
|
+
(a, b) => Number(b.layer === selectedLayer) - Number(a.layer === selectedLayer)
|
|
20307
|
+
);
|
|
20308
|
+
return getPadComponent(hits[0]?._element, elements);
|
|
20309
|
+
};
|
|
20200
20310
|
const getNetAtPoint = (point2) => {
|
|
20201
20311
|
if (!transform) return void 0;
|
|
20202
20312
|
const describeNet = (netId, element) => ({
|
|
@@ -20380,6 +20490,8 @@ var CanvasElementsRenderer = (props) => {
|
|
|
20380
20490
|
xRayNetIds,
|
|
20381
20491
|
onXRayNetsChange: setXRayNetIds,
|
|
20382
20492
|
onContextMenuOpenChange: props.onContextMenuOpenChange,
|
|
20493
|
+
getComponentAtPoint,
|
|
20494
|
+
onViewSchematicComponent: props.onViewSchematicComponent,
|
|
20383
20495
|
children: /* @__PURE__ */ jsx25(ErrorOverlay, { transform, elements, children: /* @__PURE__ */ jsx25(RatsNestOverlay, { transform, soup: elements, children: /* @__PURE__ */ jsx25(
|
|
20384
20496
|
PcbGroupOverlay,
|
|
20385
20497
|
{
|
|
@@ -20512,6 +20624,7 @@ var PCBViewer = ({
|
|
|
20512
20624
|
editEvents: editEventsProp,
|
|
20513
20625
|
onEditEventsChanged,
|
|
20514
20626
|
onBoundsSelected,
|
|
20627
|
+
onViewSchematicComponent,
|
|
20515
20628
|
focusOnHover = false,
|
|
20516
20629
|
clickToInteractEnabled = false,
|
|
20517
20630
|
disablePcbGroups = false
|
|
@@ -20654,6 +20767,7 @@ var PCBViewer = ({
|
|
|
20654
20767
|
allowEditing,
|
|
20655
20768
|
focusOnHover,
|
|
20656
20769
|
onBoundsSelected,
|
|
20770
|
+
onViewSchematicComponent,
|
|
20657
20771
|
cancelPanDrag,
|
|
20658
20772
|
onContextMenuOpenChange,
|
|
20659
20773
|
onCreateEditEvent,
|