import { useSimulatorStore } from '../../store/useSimulatorStore'; import { useEffect, useState } from 'react'; import { ArduinoUno } from '../components-wokwi/ArduinoUno'; import { LED } from '../components-wokwi/LED'; import { Resistor } from '../components-wokwi/Resistor'; import { Pushbutton } from '../components-wokwi/Pushbutton'; import { Potentiometer } from '../components-wokwi/Potentiometer'; import { ComponentPalette } from './ComponentPalette'; import { PinSelector } from './PinSelector'; import type { ComponentTemplate } from '../../types/components'; import './SimulatorCanvas.css'; export const SimulatorCanvas = () => { const { components, running, pinManager, initSimulator, updateComponentState, addComponent, removeComponent, updateComponent } = useSimulatorStore(); const [draggedTemplate, setDraggedTemplate] = useState(null); const [selectedComponentId, setSelectedComponentId] = useState(null); const [showPinSelector, setShowPinSelector] = useState(false); const [pinSelectorPos, setPinSelectorPos] = useState({ x: 0, y: 0 }); // Initialize simulator on mount useEffect(() => { initSimulator(); }, [initSimulator]); // Connect components to pin manager useEffect(() => { const unsubscribers: (() => void)[] = []; components.forEach((component) => { if (component.properties.pin !== undefined) { const unsubscribe = pinManager.onPinChange( component.properties.pin, (pin, state) => { // Update component state when pin changes updateComponentState(component.id, state); console.log(`Component ${component.id} on pin ${pin}: ${state ? 'HIGH' : 'LOW'}`); } ); unsubscribers.push(unsubscribe); } }); return () => { unsubscribers.forEach(unsub => unsub()); }; }, [components, pinManager, updateComponentState]); // Handle keyboard delete useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if ((e.key === 'Delete' || e.key === 'Backspace') && selectedComponentId) { removeComponent(selectedComponentId); setSelectedComponentId(null); } }; window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); }, [selectedComponentId, removeComponent]); // Drag & drop handlers const handleDragOver = (e: React.DragEvent) => { e.preventDefault(); e.dataTransfer.dropEffect = 'copy'; }; const handleDrop = (e: React.DragEvent) => { e.preventDefault(); if (!draggedTemplate) return; const canvasRect = e.currentTarget.getBoundingClientRect(); const x = e.clientX - canvasRect.left; const y = e.clientY - canvasRect.top; const newComponent = { id: `${draggedTemplate.type}-${Date.now()}`, type: draggedTemplate.type, x, y, properties: { ...draggedTemplate.defaultProperties, state: false, }, }; addComponent(newComponent as any); setDraggedTemplate(null); }; // Component selection const handleComponentClick = (componentId: string, event: React.MouseEvent) => { event.stopPropagation(); setSelectedComponentId(componentId); setPinSelectorPos({ x: event.clientX, y: event.clientY }); setShowPinSelector(true); }; // Pin assignment const handlePinSelect = (componentId: string, pin: number) => { updateComponent(componentId, { properties: { ...components.find((c) => c.id === componentId)?.properties, pin, }, } as any); }; // Render component const renderComponent = (component: any) => { const isSelected = selectedComponentId === component.id; const commonProps = { id: component.id, x: component.x, y: component.y, }; const wrapperStyle = { position: 'absolute' as const, left: `${component.x}px`, top: `${component.y}px`, cursor: 'pointer', border: isSelected ? '2px dashed #007acc' : '2px solid transparent', borderRadius: '4px', padding: '4px', }; return (
handleComponentClick(component.id, e)} > {component.type === 'led' && ( <>
{component.properties.pin !== undefined ? `Pin ${component.properties.pin}` : 'No pin'}
)} {component.type === 'resistor' && ( <>
{component.properties.value || 220}Ω
)} {component.type === 'pushbutton' && ( <>
{component.properties.pin !== undefined ? `Pin ${component.properties.pin}` : 'No pin'}
)} {component.type === 'potentiometer' && ( <>
{component.properties.pin !== undefined ? `Pin A${component.properties.pin - 14}` : 'No pin'}
)}
); }; return (
{/* Component Palette */} {/* Main Canvas */}

Arduino Simulator

{running ? 'Running' : 'Stopped'} {components.length} components
setSelectedComponentId(null)} > {/* Arduino Uno Board using wokwi-elements */} c.id === 'led-builtin')?.properties.state || false} /> {/* Components using wokwi-elements */}
{components.map(renderComponent)}
{/* Pin Selector Modal */} {showPinSelector && selectedComponentId && ( c.id === selectedComponentId)?.type || 'unknown' } currentPin={ components.find((c) => c.id === selectedComponentId)?.properties.pin } onPinSelect={handlePinSelect} onClose={() => setShowPinSelector(false)} position={pinSelectorPos} /> )}
); };