mirror of
https://github.com/hanzoai/shadcn.git
synced 2026-11-03 03:18:52 +00:00
- Updated build-registry.mts to include all component types (ui, ai, 3d, animation, code, etc.) - Fixed missing components issue by processing all components that start with 'components:' - Now properly handles components that only exist in one theme variant - Registry now contains 363 components (up from 117) - AI, 3D, animation, and code components are now accessible via the registry
15 lines
16 KiB
JSON
15 lines
16 KiB
JSON
{
|
|
"name": "3d-scene",
|
|
"description": "Complete 3D scene component",
|
|
"dependencies": [
|
|
"three",
|
|
"@react-three/fiber"
|
|
],
|
|
"files": [
|
|
{
|
|
"name": "3d-scene.tsx",
|
|
"content": "\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\n// Basic types for 3D primitives\ninterface Vector3 {\n x: number\n y: number\n z: number\n}\n\ninterface Color {\n r: number\n g: number\n b: number\n a?: number\n}\n\ninterface Material {\n color: Color\n emissive?: Color\n metalness?: number\n roughness?: number\n opacity?: number\n}\n\ninterface Light {\n type: \"directional\" | \"point\" | \"ambient\"\n position?: Vector3\n direction?: Vector3\n color: Color\n intensity: number\n}\n\ninterface Camera {\n position: Vector3\n target: Vector3\n fov: number\n near: number\n far: number\n}\n\ninterface Geometry {\n type: \"box\" | \"sphere\" | \"plane\" | \"cylinder\" | \"torus\"\n parameters: Record<string, number>\n}\n\ninterface Mesh {\n id: string\n geometry: Geometry\n material: Material\n position: Vector3\n rotation: Vector3\n scale: Vector3\n visible?: boolean\n}\n\ninterface Scene3DProps extends React.HTMLAttributes<HTMLCanvasElement> {\n width?: number\n height?: number\n meshes?: Mesh[]\n lights?: Light[]\n camera?: Camera\n backgroundColor?: Color\n enableOrbitControls?: boolean\n enableAutoRotate?: boolean\n autoRotateSpeed?: number\n onMeshClick?: (mesh: Mesh) => void\n onMeshHover?: (mesh: Mesh | null) => void\n className?: string\n}\n\n// Simple software renderer for basic shapes\nclass SimpleRenderer {\n private canvas: HTMLCanvasElement\n private ctx: CanvasRenderingContext2D\n private camera: Camera\n private meshes: Mesh[] = []\n private lights: Light[] = []\n private backgroundColor: Color = { r: 0.1, g: 0.1, b: 0.1, a: 1 }\n private animationFrame: number | null = null\n private mousePosition = { x: 0, y: 0 }\n private isDragging = false\n private lastMousePosition = { x: 0, y: 0 }\n private cameraRotation = { x: 0, y: 0 }\n private autoRotate = false\n private autoRotateSpeed = 1\n\n constructor(canvas: HTMLCanvasElement) {\n this.canvas = canvas\n const ctx = canvas.getContext(\"2d\")\n if (!ctx) throw new Error(\"Could not get 2D context\")\n this.ctx = ctx\n\n this.camera = {\n position: { x: 0, y: 0, z: 5 },\n target: { x: 0, y: 0, z: 0 },\n fov: 75,\n near: 0.1,\n far: 1000,\n }\n\n this.setupEventListeners()\n }\n\n private setupEventListeners() {\n this.canvas.addEventListener(\"mousedown\", this.handleMouseDown.bind(this))\n this.canvas.addEventListener(\"mousemove\", this.handleMouseMove.bind(this))\n this.canvas.addEventListener(\"mouseup\", this.handleMouseUp.bind(this))\n this.canvas.addEventListener(\"wheel\", this.handleWheel.bind(this))\n }\n\n private handleMouseDown(e: MouseEvent) {\n this.isDragging = true\n this.lastMousePosition = { x: e.clientX, y: e.clientY }\n }\n\n private handleMouseMove(e: MouseEvent) {\n this.mousePosition = { x: e.clientX, y: e.clientY }\n\n if (this.isDragging) {\n const deltaX = e.clientX - this.lastMousePosition.x\n const deltaY = e.clientY - this.lastMousePosition.y\n\n this.cameraRotation.y += deltaX * 0.01\n this.cameraRotation.x += deltaY * 0.01\n\n this.cameraRotation.x = Math.max(\n -Math.PI / 2,\n Math.min(Math.PI / 2, this.cameraRotation.x)\n )\n\n this.updateCameraPosition()\n this.lastMousePosition = { x: e.clientX, y: e.clientY }\n }\n }\n\n private handleMouseUp() {\n this.isDragging = false\n }\n\n private handleWheel(e: WheelEvent) {\n e.preventDefault()\n const distance = Math.sqrt(\n this.camera.position.x ** 2 +\n this.camera.position.y ** 2 +\n this.camera.position.z ** 2\n )\n const newDistance = Math.max(1, Math.min(20, distance + e.deltaY * 0.01))\n\n const factor = newDistance / distance\n this.camera.position.x *= factor\n this.camera.position.y *= factor\n this.camera.position.z *= factor\n }\n\n private updateCameraPosition() {\n const distance = 5\n this.camera.position.x =\n distance *\n Math.sin(this.cameraRotation.y) *\n Math.cos(this.cameraRotation.x)\n this.camera.position.y = distance * Math.sin(this.cameraRotation.x)\n this.camera.position.z =\n distance *\n Math.cos(this.cameraRotation.y) *\n Math.cos(this.cameraRotation.x)\n }\n\n private project(point: Vector3): { x: number; y: number; z: number } {\n const { width, height } = this.canvas\n\n // Simple perspective projection\n const dx = point.x - this.camera.position.x\n const dy = point.y - this.camera.position.y\n const dz = point.z - this.camera.position.z\n\n const distance = Math.sqrt(dx * dx + dy * dy + dz * dz)\n const scale =\n (this.camera.far - this.camera.near) / (distance + this.camera.near)\n\n return {\n x: width / 2 + (dx * scale * width) / 4,\n y: height / 2 - (dy * scale * height) / 4,\n z: distance,\n }\n }\n\n private drawMesh(mesh: Mesh) {\n if (!mesh.visible) return\n\n const { geometry, material, position, rotation, scale } = mesh\n this.ctx.save()\n\n // Apply transformations\n const projected = this.project(position)\n\n this.ctx.translate(projected.x, projected.y)\n this.ctx.scale(scale.x, scale.y)\n this.ctx.rotate(rotation.z)\n\n // Set material properties\n this.ctx.fillStyle = `rgba(${Math.floor(material.color.r * 255)}, ${Math.floor(material.color.g * 255)}, ${Math.floor(material.color.b * 255)}, ${material.opacity || 1})`\n this.ctx.strokeStyle = this.ctx.fillStyle\n\n // Draw based on geometry type\n switch (geometry.type) {\n case \"box\":\n this.drawBox(geometry.parameters)\n break\n case \"sphere\":\n this.drawSphere(geometry.parameters)\n break\n case \"plane\":\n this.drawPlane(geometry.parameters)\n break\n case \"cylinder\":\n this.drawCylinder(geometry.parameters)\n break\n case \"torus\":\n this.drawTorus(geometry.parameters)\n break\n }\n\n this.ctx.restore()\n }\n\n private drawBox(params: Record<string, number>) {\n const width = params.width || 1\n const height = params.height || 1\n\n this.ctx.fillRect(-width / 2, -height / 2, width, height)\n this.ctx.strokeRect(-width / 2, -height / 2, width, height)\n }\n\n private drawSphere(params: Record<string, number>) {\n const radius = params.radius || 0.5\n\n this.ctx.beginPath()\n this.ctx.arc(0, 0, radius * 50, 0, Math.PI * 2)\n this.ctx.fill()\n this.ctx.stroke()\n }\n\n private drawPlane(params: Record<string, number>) {\n const width = params.width || 1\n const height = params.height || 1\n\n this.ctx.fillRect(-width / 2, -height / 2, width, height)\n }\n\n private drawCylinder(params: Record<string, number>) {\n const radius = params.radius || 0.5\n const height = params.height || 1\n\n // Draw as rectangle with rounded ends\n this.ctx.fillRect(-radius * 50, -height * 25, radius * 100, height * 50)\n\n this.ctx.beginPath()\n this.ctx.arc(-radius * 50, 0, height * 25, Math.PI / 2, -Math.PI / 2)\n this.ctx.arc(radius * 50, 0, height * 25, -Math.PI / 2, Math.PI / 2)\n this.ctx.fill()\n }\n\n private drawTorus(params: Record<string, number>) {\n const radius = params.radius || 0.5\n const tube = params.tube || 0.2\n\n this.ctx.beginPath()\n this.ctx.arc(0, 0, radius * 50, 0, Math.PI * 2)\n this.ctx.lineWidth = tube * 50\n this.ctx.stroke()\n this.ctx.lineWidth = 1\n }\n\n private clear() {\n const { width, height } = this.canvas\n this.ctx.fillStyle = `rgba(${Math.floor(this.backgroundColor.r * 255)}, ${Math.floor(this.backgroundColor.g * 255)}, ${Math.floor(this.backgroundColor.b * 255)}, ${this.backgroundColor.a || 1})`\n this.ctx.fillRect(0, 0, width, height)\n }\n\n setCamera(camera: Camera) {\n this.camera = { ...camera }\n }\n\n setMeshes(meshes: Mesh[]) {\n this.meshes = [...meshes]\n }\n\n setLights(lights: Light[]) {\n this.lights = [...lights]\n }\n\n setBackgroundColor(color: Color) {\n this.backgroundColor = { ...color }\n }\n\n setAutoRotate(enabled: boolean, speed = 1) {\n this.autoRotate = enabled\n this.autoRotateSpeed = speed\n }\n\n render() {\n this.clear()\n\n if (this.autoRotate) {\n this.cameraRotation.y += this.autoRotateSpeed * 0.01\n this.updateCameraPosition()\n }\n\n // Sort meshes by distance (simple depth sorting)\n const sortedMeshes = [...this.meshes].sort((a, b) => {\n const distA = Math.sqrt(\n (a.position.x - this.camera.position.x) ** 2 +\n (a.position.y - this.camera.position.y) ** 2 +\n (a.position.z - this.camera.position.z) ** 2\n )\n const distB = Math.sqrt(\n (b.position.x - this.camera.position.x) ** 2 +\n (b.position.y - this.camera.position.y) ** 2 +\n (b.position.z - this.camera.position.z) ** 2\n )\n return distB - distA\n })\n\n sortedMeshes.forEach((mesh) => this.drawMesh(mesh))\n }\n\n startAnimation() {\n const animate = () => {\n this.render()\n this.animationFrame = requestAnimationFrame(animate)\n }\n animate()\n }\n\n stopAnimation() {\n if (this.animationFrame) {\n cancelAnimationFrame(this.animationFrame)\n this.animationFrame = null\n }\n }\n\n destroy() {\n this.stopAnimation()\n }\n}\n\nconst Scene3D = React.forwardRef<HTMLCanvasElement, Scene3DProps>(\n (\n {\n width = 800,\n height = 600,\n meshes = [],\n lights = [],\n camera,\n backgroundColor = { r: 0.1, g: 0.1, b: 0.1, a: 1 },\n enableOrbitControls = true,\n enableAutoRotate = false,\n autoRotateSpeed = 1,\n onMeshClick,\n onMeshHover,\n className,\n ...props\n },\n ref\n ) => {\n const canvasRef = React.useRef<HTMLCanvasElement>(null)\n const rendererRef = React.useRef<SimpleRenderer | null>(null)\n\n // Initialize renderer\n React.useEffect(() => {\n if (!canvasRef.current) return\n\n const renderer = new SimpleRenderer(canvasRef.current)\n rendererRef.current = renderer\n\n renderer.startAnimation()\n\n return () => {\n renderer.destroy()\n }\n }, [])\n\n // Update camera\n React.useEffect(() => {\n if (rendererRef.current && camera) {\n rendererRef.current.setCamera(camera)\n }\n }, [camera])\n\n // Update meshes\n React.useEffect(() => {\n if (rendererRef.current) {\n rendererRef.current.setMeshes(meshes)\n }\n }, [meshes])\n\n // Update lights\n React.useEffect(() => {\n if (rendererRef.current) {\n rendererRef.current.setLights(lights)\n }\n }, [lights])\n\n // Update background color\n React.useEffect(() => {\n if (rendererRef.current) {\n rendererRef.current.setBackgroundColor(backgroundColor)\n }\n }, [backgroundColor])\n\n // Update auto rotate\n React.useEffect(() => {\n if (rendererRef.current) {\n rendererRef.current.setAutoRotate(enableAutoRotate, autoRotateSpeed)\n }\n }, [enableAutoRotate, autoRotateSpeed])\n\n // Update canvas size\n React.useEffect(() => {\n if (canvasRef.current) {\n canvasRef.current.width = width\n canvasRef.current.height = height\n }\n }, [width, height])\n\n React.useImperativeHandle(ref, () => canvasRef.current!)\n\n return (\n <canvas\n ref={canvasRef}\n width={width}\n height={height}\n className={cn(\n \"border rounded-lg bg-black/5 cursor-grab active:cursor-grabbing\",\n className\n )}\n {...props}\n />\n )\n }\n)\nScene3D.displayName = \"Scene3D\"\n\n// Helper functions to create common geometries\nconst createBox = (width = 1, height = 1, depth = 1): Geometry => ({\n type: \"box\",\n parameters: { width, height, depth },\n})\n\nconst createSphere = (radius = 0.5): Geometry => ({\n type: \"sphere\",\n parameters: { radius },\n})\n\nconst createPlane = (width = 1, height = 1): Geometry => ({\n type: \"plane\",\n parameters: { width, height },\n})\n\nconst createCylinder = (radius = 0.5, height = 1): Geometry => ({\n type: \"cylinder\",\n parameters: { radius, height },\n})\n\nconst createTorus = (radius = 0.5, tube = 0.2): Geometry => ({\n type: \"torus\",\n parameters: { radius, tube },\n})\n\n// Helper functions to create materials\nconst createMaterial = (\n color: Color,\n options: Partial<Material> = {}\n): Material => ({\n color,\n metalness: 0,\n roughness: 0.5,\n opacity: 1,\n ...options,\n})\n\n// Helper functions to create lights\nconst createDirectionalLight = (\n direction: Vector3,\n color: Color = { r: 1, g: 1, b: 1 },\n intensity = 1\n): Light => ({\n type: \"directional\",\n direction,\n color,\n intensity,\n})\n\nconst createPointLight = (\n position: Vector3,\n color: Color = { r: 1, g: 1, b: 1 },\n intensity = 1\n): Light => ({\n type: \"point\",\n position,\n color,\n intensity,\n})\n\nconst createAmbientLight = (\n color: Color = { r: 0.2, g: 0.2, b: 0.2 },\n intensity = 0.5\n): Light => ({\n type: \"ambient\",\n color,\n intensity,\n})\n\n// Preset scenes\nconst Scene3DPreset = {\n Basic: React.forwardRef<\n HTMLCanvasElement,\n Omit<Scene3DProps, \"meshes\" | \"lights\">\n >((props, ref) => {\n const meshes: Mesh[] = [\n {\n id: \"cube\",\n geometry: createBox(1, 1, 1),\n material: createMaterial({ r: 0.5, g: 0.7, b: 1 }),\n position: { x: 0, y: 0, z: 0 },\n rotation: { x: 0, y: 0, z: 0 },\n scale: { x: 1, y: 1, z: 1 },\n visible: true,\n },\n ]\n\n const lights: Light[] = [\n createDirectionalLight({ x: 1, y: 1, z: 1 }),\n createAmbientLight(),\n ]\n\n return (\n <Scene3D\n ref={ref}\n meshes={meshes}\n lights={lights}\n enableOrbitControls\n {...props}\n />\n )\n }),\n\n Primitives: React.forwardRef<\n HTMLCanvasElement,\n Omit<Scene3DProps, \"meshes\" | \"lights\">\n >((props, ref) => {\n const meshes: Mesh[] = [\n {\n id: \"cube\",\n geometry: createBox(0.8, 0.8, 0.8),\n material: createMaterial({ r: 1, g: 0.3, b: 0.3 }),\n position: { x: -2, y: 0, z: 0 },\n rotation: { x: 0, y: 0, z: 0 },\n scale: { x: 1, y: 1, z: 1 },\n visible: true,\n },\n {\n id: \"sphere\",\n geometry: createSphere(0.5),\n material: createMaterial({ r: 0.3, g: 1, b: 0.3 }),\n position: { x: 0, y: 0, z: 0 },\n rotation: { x: 0, y: 0, z: 0 },\n scale: { x: 1, y: 1, z: 1 },\n visible: true,\n },\n {\n id: \"cylinder\",\n geometry: createCylinder(0.4, 1),\n material: createMaterial({ r: 0.3, g: 0.3, b: 1 }),\n position: { x: 2, y: 0, z: 0 },\n rotation: { x: 0, y: 0, z: 0 },\n scale: { x: 1, y: 1, z: 1 },\n visible: true,\n },\n ]\n\n const lights: Light[] = [\n createDirectionalLight({ x: 1, y: 1, z: 1 }),\n createAmbientLight({ r: 0.3, g: 0.3, b: 0.3 }, 0.3),\n ]\n\n return (\n <Scene3D\n ref={ref}\n meshes={meshes}\n lights={lights}\n enableOrbitControls\n enableAutoRotate\n autoRotateSpeed={0.5}\n {...props}\n />\n )\n }),\n}\n\n// Assign display names\nScene3DPreset.Basic.displayName = \"Scene3DPreset.Basic\"\nScene3DPreset.Primitives.displayName = \"Scene3DPreset.Primitives\"\n\nexport {\n Scene3D,\n Scene3DPreset,\n createBox,\n createSphere,\n createPlane,\n createCylinder,\n createTorus,\n createMaterial,\n createDirectionalLight,\n createPointLight,\n createAmbientLight,\n type Scene3DProps,\n type Vector3,\n type Color,\n type Material,\n type Light,\n type Camera,\n type Geometry,\n type Mesh,\n}\n"
|
|
}
|
|
],
|
|
"type": "components:3d"
|
|
} |