2026-03-04 08:25:52 +07:00
/ * *
* Examples Page Component
*
* Displays the examples gallery
* /
import React from 'react' ;
import { useNavigate } from 'react-router-dom' ;
import { ExamplesGallery } from '../components/examples/ExamplesGallery' ;
2026-03-12 18:17:29 +07:00
import { AppHeader } from '../components/layout/AppHeader' ;
2026-03-23 23:32:15 +07:00
import { useSEO } from '../utils/useSEO' ;
2026-03-04 08:25:52 +07:00
import { useEditorStore } from '../store/useEditorStore' ;
import { useSimulatorStore } from '../store/useSimulatorStore' ;
2026-03-16 00:04:01 +07:00
import { useVfsStore } from '../store/useVfsStore' ;
2026-03-15 02:57:22 +07:00
import { isBoardComponent } from '../utils/boardPinMapping' ;
2026-03-04 08:25:52 +07:00
import type { ExampleProject } from '../data/examples' ;
2026-03-16 00:04:01 +07:00
import type { BoardKind } from '../types/board' ;
2026-03-04 08:25:52 +07:00
export const ExamplesPage : React.FC = ( ) = > {
2026-03-23 23:32:15 +07:00
useSEO ( {
title : 'Arduino Simulator Examples — Run 18+ Sketches Instantly | Velxio' ,
description :
'Explore 18+ interactive Arduino examples with LEDs, sensors, displays, and games. Runs entirely in your browser — free, no install, no account required.' ,
url : 'https://velxio.dev/examples' ,
} ) ;
2026-03-04 08:25:52 +07:00
const navigate = useNavigate ( ) ;
const { setCode } = useEditorStore ( ) ;
2026-03-16 00:04:01 +07:00
const { setComponents , setWires , setBoardType , activeBoardId , boards , addBoard , removeBoard , setActiveBoardId } = useSimulatorStore ( ) ;
2026-03-04 08:25:52 +07:00
const handleLoadExample = ( example : ExampleProject ) = > {
console . log ( 'Loading example:' , example . title ) ;
2026-03-16 00:04:01 +07:00
if ( example . boards && example . boards . length > 0 ) {
// ── Multi-board loading ───────────────────────────────────────────────
// 1. Remove all current boards
const currentIds = boards . map ( ( b ) = > b . id ) ;
currentIds . forEach ( ( id ) = > removeBoard ( id ) ) ;
// 2. Add each board from the example; addBoard returns deterministic IDs
example . boards . forEach ( ( eb ) = > {
addBoard ( eb . boardKind as BoardKind , eb . x , eb . y ) ;
} ) ;
// 3. Load code + VFS per board
const { boards : newBoards } = useSimulatorStore . getState ( ) ;
example . boards . forEach ( ( eb ) = > {
const boardId = eb . boardKind ; // predictable: first board of each kind = boardKind string
const board = newBoards . find ( ( b ) = > b . id === boardId ) ;
if ( ! board ) return ;
if ( eb . code ) {
const filename = boardId === 'arduino-uno' || boardId === 'arduino-nano' || boardId === 'arduino-mega'
? 'sketch.ino'
: 'main.cpp' ;
// loadFiles reads activeGroupId internally — switch to this board's group first
useEditorStore . getState ( ) . setActiveGroup ( board . activeFileGroupId ) ;
useEditorStore . getState ( ) . loadFiles ( [ { name : filename , content : eb.code } ] ) ;
}
if ( eb . vfsFiles && boardId === 'raspberry-pi-3' ) {
// Update VFS files by name (default tree has script.py and hello.sh)
const vfsState = useVfsStore . getState ( ) ;
const tree = vfsState . getTree ( boardId ) ;
for ( const [ nodeId , node ] of Object . entries ( tree ) ) {
if ( node . type === 'file' && eb . vfsFiles [ node . name ] !== undefined ) {
vfsState . setContent ( boardId , nodeId , eb . vfsFiles [ node . name ] ) ;
}
}
}
} ) ;
// 4. Set active board to the first non-Pi board (so editor shows Arduino code)
const firstArduino = example . boards . find ( ( eb ) = >
eb . boardKind !== 'raspberry-pi-3' && eb . boardKind !== 'esp32' &&
eb . boardKind !== 'esp32-s3' && eb . boardKind !== 'esp32-c3'
) ;
if ( firstArduino ) {
setActiveBoardId ( firstArduino . boardKind ) ;
}
// 5. Load components (filter out board components — they're placed via boards[])
const componentsWithoutBoard = example . components . filter (
( comp ) = >
! comp . type . includes ( 'arduino' ) &&
! comp . type . includes ( 'pico' ) &&
! comp . type . includes ( 'raspberry' ) &&
! comp . type . includes ( 'esp32' )
) ;
setComponents (
componentsWithoutBoard . map ( ( comp ) = > ( {
id : comp.id ,
metadataId : comp.type.replace ( 'wokwi-' , '' ) ,
x : comp.x ,
y : comp.y ,
properties : comp.properties ,
} ) )
) ;
// 6. Load wires — componentIds already match board instance IDs
setWires (
example . wires . map ( ( wire ) = > ( {
id : wire.id ,
start : { componentId : wire.start.componentId , pinName : wire.start.pinName , x : 0 , y : 0 } ,
end : { componentId : wire.end.componentId , pinName : wire.end.pinName , x : 0 , y : 0 } ,
color : wire.color ,
waypoints : [ ] ,
} ) )
) ;
} else {
// ── Single-board loading (original behaviour) ─────────────────────────
const targetBoard = example . boardType || 'arduino-uno' ;
setBoardType ( targetBoard ) ;
setCode ( example . code ) ;
const componentsWithoutBoard = example . components . filter (
( comp ) = >
! comp . type . includes ( 'arduino' ) &&
! comp . type . includes ( 'pico' ) &&
! comp . type . includes ( 'esp32' )
) ;
setComponents (
componentsWithoutBoard . map ( ( comp ) = > ( {
id : comp.id ,
metadataId : comp.type.replace ( 'wokwi-' , '' ) ,
x : comp.x ,
y : comp.y ,
properties : comp.properties ,
} ) )
) ;
const boardInstanceId = activeBoardId ? ? 'arduino-uno' ;
const remapBoardId = ( id : string ) = > isBoardComponent ( id ) ? boardInstanceId : id ;
setWires (
example . wires . map ( ( wire ) = > ( {
id : wire.id ,
start : { componentId : remapBoardId ( wire . start . componentId ) , pinName : wire.start.pinName , x : 0 , y : 0 } ,
end : { componentId : remapBoardId ( wire . end . componentId ) , pinName : wire.end.pinName , x : 0 , y : 0 } ,
color : wire.color ,
waypoints : [ ] ,
} ) )
) ;
}
2026-03-07 01:32:24 +07:00
navigate ( '/editor' ) ;
2026-03-04 08:25:52 +07:00
} ;
2026-03-12 18:17:29 +07:00
return (
< div style = { { display : 'flex' , flexDirection : 'column' , minHeight : '100vh' , background : '#1e1e1e' } } >
< AppHeader / >
< ExamplesGallery onLoadExample = { handleLoadExample } / >
< / div >
) ;
2026-03-04 08:25:52 +07:00
} ;