diff --git a/src/CadViewer.tsx b/src/CadViewer.tsx
index 9accec9..0ba6dce 100644
--- a/src/CadViewer.tsx
+++ b/src/CadViewer.tsx
@@ -1,23 +1,10 @@
import type { AnySoupElement } from "@tscircuit/soup"
import type * as React from "react"
import type * as THREE from "three"
-import { useConvertChildrenToSoup } from "./hooks/use-convert-children-to-soup"
import { su } from "@tscircuit/soup-util"
-import { useEffect, useMemo, useState, forwardRef } from "react"
-import { createBoardGeomFromSoup } from "./soup-to-3d"
-import { useStlsFromGeom } from "./hooks/use-stls-from-geom"
-import { STLModel } from "./three-components/STLModel"
+import { useState, forwardRef } from "react"
import { CadViewerContainer } from "./CadViewerContainer"
-import { MixedStlModel } from "./three-components/MixedStlModel"
-import { Euler } from "three"
-import { JscadModel } from "./three-components/JscadModel"
-import { Footprinter3d } from "jscad-electronics"
-import { FootprinterModel } from "./three-components/FootprinterModel"
-import { tuple } from "./utils/tuple"
-import { AnyCadComponent } from "./AnyCadComponent"
-import { Text } from "@react-three/drei"
-import { ThreeErrorBoundary } from "./three-components/ThreeErrorBoundary"
-import { Error3d } from "./three-components/Error3d"
+import { Pcb3D } from "./Pcb3D"
interface Props {
soup?: AnySoupElement[]
@@ -32,57 +19,26 @@ export const CadViewer = forwardRef<
name: string
point: THREE.Vector3
}>(null)
- soup ??= useConvertChildrenToSoup(children, soup) as any
-
- if (!soup) return null
-
- const boardGeom = useMemo(() => {
- if (!soup.some((e) => e.type === "pcb_board")) return null
- return createBoardGeomFromSoup(soup)
- }, [soup])
-
- const { stls: boardStls, loading } = useStlsFromGeom(boardGeom)
-
- const cad_components = su(soup).cad_component.list()
return (
- {boardStls.map(({ stlUrl, color }, index) => (
-
- ))}
- {cad_components.map((cad_component) => (
- (
-
- )}
- >
- {
- const componentName = su(soup as any).source_component.getUsing({
- source_component_id: cad_component.source_component_id,
- })?.name
- setHoveredComponent({
- cad_component_id: cad_component.cad_component_id,
- name: componentName ?? "",
- point: e.point,
- })
- }}
- onUnhover={() => setHoveredComponent(null)}
- cad_component={cad_component}
- isHovered={
- hoveredComponent?.cad_component_id ===
- cad_component.cad_component_id
- }
- />
-
- ))}
+ {
+ const componentName = su(soup as any).source_component.getUsing({
+ source_component_id: e.cad_component.source_component_id,
+ })?.name
+ setHoveredComponent({
+ cad_component_id: e.cad_component.cad_component_id,
+ name: componentName ?? "",
+ point: e.point,
+ })
+ }}
+ onUnhover={() => setHoveredComponent(null)}
+ hoverAt={hoveredComponent?.cad_component_id}
+ >
+ {children}
+
)
})
diff --git a/src/Pcb3D.tsx b/src/Pcb3D.tsx
new file mode 100644
index 0000000..f8f31af
--- /dev/null
+++ b/src/Pcb3D.tsx
@@ -0,0 +1,86 @@
+import type * as React from "react"
+import type { AnySoupElement } from "@tscircuit/soup"
+import { useMemo } from "react"
+import { su, type SoupUtilObjects } from "@tscircuit/soup-util"
+import { useStlsFromGeom } from "./hooks/use-stls-from-geom"
+import { createBoardGeomFromSoup } from "./soup-to-3d"
+import { useConvertChildrenToSoup } from "./hooks/use-convert-children-to-soup"
+import { STLModel } from "./three-components/STLModel"
+import { ThreeErrorBoundary } from "./three-components/ThreeErrorBoundary"
+import { AnyCadComponent, type tooltip } from "./AnyCadComponent"
+import { Error3d } from "./three-components/Error3d"
+
+export interface RaycastEvent extends tooltip.RaycastEvent {
+ source_component: SoupUtilObjects["source_component"]
+ cad_component: ReturnType[0]
+}
+
+export interface HoverProps
+ extends Omit, "isHovered"> {
+ hoverAt?: RaycastEvent["cad_component"]["cad_component_id"]
+}
+
+export function Pcb3D({
+ soup,
+ children,
+ onHover,
+ onUnhover,
+ hoverAt,
+}: React.PropsWithChildren) {
+ soup ??= useConvertChildrenToSoup(children, soup) as any
+
+ if (!soup) return null
+
+ const boardGeom = useMemo(() => {
+ if (!soup.some((e) => e.type === "pcb_board")) return null
+ return createBoardGeomFromSoup(soup)
+ }, [soup])
+
+ const { stls: boardStls } = useStlsFromGeom(boardGeom)
+
+ const soupUtil = su(soup)
+ const cad_components = soupUtil.cad_component.list()
+
+ return [
+ ...boardStls.map(({ stlUrl, color }, index) => (
+
+ )),
+ ...cad_components.map((cad_component) => (
+ (
+
+ )}
+ >
+
+ onHover({
+ source_component: soupUtil.source_component,
+ cad_component,
+ ...e,
+ }))
+ }
+ onUnhover={
+ onUnhover &&
+ ((e) =>
+ onUnhover({
+ source_component: soupUtil.source_component,
+ cad_component,
+ ...e,
+ }))
+ }
+ cad_component={cad_component}
+ isHovered={hoverAt === cad_component.cad_component_id}
+ />
+
+ )),
+ ]
+}