import React, { useState, useEffect } from 'react'; import { Link, useParams, useNavigate } from 'react-router-dom'; import { useAuthStore } from '../store/useAuthStore'; import './DocsPage.css'; const GITHUB_URL = 'https://github.com/davidmonterocrespo24/velxio'; const BASE_URL = 'https://velxio.dev'; const AUTHOR = { '@type': 'Person', name: 'David Montero Crespo', url: 'https://github.com/davidmonterocrespo24' } as const; /* ── Icons ─────────────────────────────────────────────── */ const IcoChip = () => ( ); const IcoGitHub = () => ( ); /* ── Doc sections ──────────────────────────────────────── */ type SectionId = | 'intro' | 'getting-started' | 'emulator' | 'components' | 'roadmap' | 'architecture' | 'wokwi-libs' | 'mcp' | 'setup'; const VALID_SECTIONS: SectionId[] = [ 'intro', 'getting-started', 'emulator', 'components', 'roadmap', 'architecture', 'wokwi-libs', 'mcp', 'setup', ]; interface NavItem { id: SectionId; label: string; } const NAV_ITEMS: NavItem[] = [ { id: 'intro', label: 'Introduction' }, { id: 'getting-started', label: 'Getting Started' }, { id: 'emulator', label: 'Emulator Architecture' }, { id: 'components', label: 'Components Reference' }, { id: 'architecture', label: 'Project Architecture' }, { id: 'wokwi-libs', label: 'Wokwi Libraries' }, { id: 'mcp', label: 'MCP Server' }, { id: 'setup', label: 'Project Status' }, { id: 'roadmap', label: 'Roadmap' }, ]; /* ── Per-section SEO metadata ──────────────────────────── */ interface SectionMeta { title: string; description: string; } const SECTION_META: Record = { 'intro': { title: 'Introduction — Velxio Documentation', description: 'Learn about Velxio, the free open-source Arduino emulator with real AVR8 and RP2040 CPU emulation and 48+ interactive electronic components.', }, 'getting-started': { title: 'Getting Started — Velxio Documentation', description: 'Get started with Velxio: use the hosted editor, self-host with Docker, or set up a local development environment. Simulate your first Arduino sketch in minutes.', }, 'emulator': { title: 'Emulator Architecture — Velxio Documentation', description: 'How Velxio emulates AVR8 (ATmega328p) and RP2040 CPUs. Covers the execution loop, peripherals (GPIO, Timers, USART, ADC, SPI, I2C), and pin mapping.', }, 'components': { title: 'Components Reference — Velxio Documentation', description: 'Full reference for all 48+ interactive electronic components in Velxio: LEDs, displays, sensors, buttons, potentiometers, and more. Includes wiring and property details.', }, 'roadmap': { title: 'Roadmap — Velxio Documentation', description: "Velxio's feature roadmap: what's implemented, what's in progress, and what's planned for future releases.", }, 'architecture': { title: 'Project Architecture — Velxio Documentation', description: 'Detailed overview of the Velxio system architecture: frontend, backend, AVR8 emulation pipeline, data flows, Zustand stores, and wire system.', }, 'wokwi-libs': { title: 'Wokwi Libraries — Velxio Documentation', description: 'How Velxio integrates the official Wokwi open-source libraries: avr8js, wokwi-elements, and rp2040js. Covers configuration, updates, and the 48 available components.', }, 'mcp': { title: 'MCP Server — Velxio Documentation', description: 'Velxio MCP Server reference: integrate AI agents (Claude, Cursor) with Velxio via Model Context Protocol. Covers tools, transports, circuit format, and example walkthroughs.', }, 'setup': { title: 'Project Status — Velxio Documentation', description: 'Complete status of all implemented Velxio features: AVR emulation, component system, wire system, code editor, example projects, and next steps.', }, }; /* ── Section content ───────────────────────────────────── */ const IntroSection: React.FC = () => (
// overview

Introduction

Velxio is a fully local, open-source Arduino emulator that runs entirely in your browser. Write Arduino C++ code, compile it with a real arduino-cli backend, and simulate it using true AVR8 / RP2040 CPU emulation — with 48+ interactive electronic components, all without installing any software on your machine.

Why Velxio?

Supported Boards

BoardCPUEmulator
Arduino UnoATmega328p @ 16 MHzavr8js
Arduino NanoATmega328p @ 16 MHzavr8js
Arduino MegaATmega2560 @ 16 MHzavr8js
Raspberry Pi PicoRP2040 @ 133 MHzrp2040js
Live Demo:{' '} velxio.dev {' '}— no installation needed, open the editor and start simulating immediately.
); const GettingStartedSection: React.FC = () => (
// setup

Getting Started

Follow these steps to simulate your first Arduino sketch.

Option 1: Use the Hosted Version

No installation needed — go to{' '} https://velxio.dev{' '} and start coding immediately.

Option 2: Self-Host with Docker

Run a single Docker command to start a fully local instance:

{`docker run -d \\
  --name velxio \\
  -p 3080:80 \\
  -v $(pwd)/data:/app/data \\
  ghcr.io/davidmonterocrespo24/velxio:master`}

Then open http://localhost:3080 in your browser.

Option 3: Manual Setup (Development)

Prerequisites: Node.js 18+, Python 3.12+, arduino-cli

1. Clone the repository

{`git clone https://github.com/davidmonterocrespo24/velxio.git
cd velxio`}

2. Start the backend

{`cd backend
python -m venv venv && source venv/bin/activate
pip install -r requirements.txt
uvicorn app.main:app --reload --port 8001`}

3. Start the frontend

{`cd frontend
npm install
npm run dev`}

Open http://localhost:5173.

4. Set up arduino-cli (first time)

{`arduino-cli core update-index
arduino-cli core install arduino:avr

# For Raspberry Pi Pico support:
arduino-cli config add board_manager.additional_urls \\
  https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
arduino-cli core install rp2040:rp2040`}

Your First Simulation

  1. Open the editor at velxio.dev/editor.
  2. Select a board from the toolbar (e.g., Arduino Uno).
  3. Write Arduino code in the Monaco editor, for example:
{`void setup() {
  pinMode(13, OUTPUT);
}

void loop() {
  digitalWrite(13, HIGH);
  delay(500);
  digitalWrite(13, LOW);
  delay(500);
}`}
  1. Click Compile — the backend calls arduino-cli and returns a .hex file.
  2. Click Run — the AVR8 emulator executes the compiled program.
  3. Add components using the component picker (click the + button on the canvas).
  4. Connect wires by clicking a component pin and then another pin.

Troubleshooting

ProblemSolution
arduino-cli: command not found Install arduino-cli and add it to your PATH.
LED doesn't blink Check the browser console for port listener errors; verify pin assignment in the component property dialog.
Serial Monitor is empty Ensure Serial.begin() is called inside setup() before any Serial.print().
Compilation errors Check the compilation console at the bottom of the editor for full arduino-cli output.
); const EmulatorSection: React.FC = () => (
// internals

Emulator Architecture

Velxio uses real CPU emulation rather than a simplified model. This document describes how each layer of the simulation works.

High-Level Data Flow

{`User Code (Monaco Editor)
        │
        ▼
   Zustand Store (useEditorStore)
        │
        ▼
  FastAPI Backend ──► arduino-cli ──► .hex / .uf2 file
        │
        ▼
  AVRSimulator / RP2040Simulator
        │ loadHex()
        ▼
  CPU execution loop (~60 FPS via requestAnimationFrame)
        │
        ▼
  Port listeners (PORTB / PORTC / PORTD)
        │
        ▼
  PinManager ──► Component state ──► React re-renders`}

AVR8 Emulation (Arduino Uno / Nano / Mega)

The AVR backend uses avr8js, which implements a complete ATmega328p / ATmega2560 processor.

Execution Loop

Each animation frame executes approximately 267,000 CPU cycles (16 MHz ÷ 60 FPS):

{`avrInstruction(cpu);  // decode and execute one AVR instruction
cpu.tick();           // advance peripheral timers and counters`}

Supported Peripherals

PeripheralDetails
GPIOPORTB (pins 8–13), PORTC (A0–A5), PORTD (pins 0–7)
Timer0 / Timer1 / Timer2millis(), delay(), PWM via analogWrite()
USARTFull transmit and receive — powers the Serial Monitor
ADC10-bit, 5 V reference on pins A0–A5
SPIHardware SPI (enables ILI9341, SD card, etc.)
I2C (TWI)Hardware I2C with virtual device bus

Pin Mapping

Arduino PinAVR PortBit
0–7PORTD0–7
8–13PORTB0–5
A0–A5PORTC0–5

RP2040 Emulation (Raspberry Pi Pico)

The RP2040 backend uses rp2040js.

HEX File Format

Arduino compilation produces Intel HEX format. The parser in hexParser.ts:

  1. Reads lines starting with :
  2. Extracts the address, record type, and data bytes
  3. Returns a Uint8Array of program bytes
  4. AVRSimulator converts this to a Uint16Array (16-bit words, little-endian)

Key Source Files

FilePurpose
frontend/src/simulation/AVRSimulator.tsAVR8 CPU emulator wrapper
frontend/src/simulation/PinManager.tsMaps Arduino pins to UI components
frontend/src/utils/hexParser.tsIntel HEX parser
frontend/src/components/simulator/SimulatorCanvas.tsxCanvas rendering
backend/app/services/arduino_cli.pyarduino-cli wrapper
backend/app/api/routes/compile.pyCompilation API endpoint
); const ComponentsSection: React.FC = () => (
// reference

Components Reference

Velxio ships with 48+ interactive electronic components powered by{' '} wokwi-elements. All components can be placed on the simulation canvas, connected with wires, and interact with your Arduino sketch in real time.

Adding Components

  1. Click the + button on the simulation canvas.
  2. Use search or browse by category in the component picker.
  3. Click a component to place it on the canvas.
  4. Drag to reposition; click to open the Property Dialog.

Connecting Components

  1. Click a pin on any component — a wire starts from that pin.
  2. Click a destination pin to complete the connection.
  3. Wires are color-coded by signal type:
ColorSignal Type
RedVCC (power)
BlackGND (ground)
BlueAnalog
GreenDigital
PurplePWM
GoldI2C (SDA/SCL)
OrangeSPI (MOSI/MISO/SCK)
CyanUSART (TX/RX)

Component Categories

Output

ComponentDescription
LEDSingle LED with configurable color
RGB LEDThree-color LED (red, green, blue channels)
7-Segment DisplaySingle digit numeric display
LCD 16×22-line character LCD (I2C or parallel)
LCD 20×44-line character LCD
ILI9341 TFT240×320 color TFT display (SPI)
BuzzerPassive piezo buzzer
NeoPixelIndividually addressable RGB LED strip

Input

ComponentDescription
Push ButtonMomentary push button
Slide SwitchSPDT slide switch
PotentiometerAnalog voltage divider (ADC input)
Rotary EncoderIncremental rotary encoder
Keypad 4×416-button matrix keypad
JoystickDual-axis analog joystick

Sensors

ComponentDescription
HC-SR04Ultrasonic distance sensor
DHT22Temperature and humidity sensor
PIR MotionPassive infrared motion sensor
PhotoresistorLight-dependent resistor (LDR)
IR Receiver38 kHz infrared receiver

Passive Components

ComponentDescription
ResistorStandard resistor (configurable value)
CapacitorElectrolytic capacitor
InductorCoil inductor

Component Properties

Each component has a Property Dialog accessible by clicking it on the canvas:

PropertyDescription
Arduino PinThe digital or analog pin this component is connected to
ColorVisual color (LEDs, wires)
ValueComponent value (e.g., resistance in Ω)
RotationRotate in 90° increments
DeleteRemove the component from the canvas
); const RoadmapSection: React.FC = () => (
// future

Roadmap

Features that are implemented, in progress, and planned for future releases of Velxio.

✅ Implemented

🔄 In Progress

🗓 Planned — Near-Term

🗓 Planned — Mid-Term

🗓 Planned — Long-Term

Want to contribute?{' '} Feature requests, bug reports, and pull requests are welcome at{' '} github.com/davidmonterocrespo24/velxio.
); /* ── Architecture Section ─────────────────────────────── */ const ArchitectureSection: React.FC = () => (
// system design

Project Architecture

Velxio is a fully local Arduino emulator using official Wokwi repositories for maximum compatibility. It features real AVR8 CPU emulation, 48+ interactive electronic components, a comprehensive wire system, and a build-time component discovery pipeline.

High-Level Overview

{`Browser (React + Vite)
  ├── Monaco Editor ──► useEditorStore (Zustand)
  ├── SimulatorCanvas ──► useSimulatorStore (Zustand)
  │     ├── AVRSimulator (avr8js)   16 MHz AVR8 CPU
  │     ├── RP2040Simulator (rp2040js)
  │     ├── PinManager              pin → component mapping
  │     ├── PartSimulationRegistry  16 interactive parts
  │     └── 48+ wokwi-elements      Lit Web Components
  └── HTTP (Axios) ──► FastAPI Backend (port 8001)
        └── ArduinoCLIService ──► arduino-cli subprocess`}

Data Flows

1. Compilation

{`Click "Compile"
  → EditorToolbar reads all workspace files
  → POST /api/compile/  { files, board_fqbn }
  → Backend: ArduinoCLIService writes temp dir
  → arduino-cli compile --fqbn  --output-dir build/
  → Returns hex_content (Intel HEX string)
  → useSimulatorStore.setCompiledHex() → loadHex()`}

2. Simulation Loop

{`Click "Run"
  → AVRSimulator.start()
  → requestAnimationFrame loop @ ~60 FPS
  → Each frame: Math.floor(267 000 × speed) cycles
    ├── avrInstruction(cpu)   — decode + execute one AVR instruction
    └── cpu.tick()            — advance Timer0/1/2, USART, ADC
  → PORTB/C/D write listeners fire
  → PinManager.updatePort() → per-pin callbacks
  → PartSimulationRegistry.onPinStateChange()
  → wokwi-elements update visually`}

3. Input Components

{`User presses button on canvas
  → wokwi web component fires 'button-press' event
  → DynamicComponent catches event
  → PartSimulationRegistry.attachEvents() handler
  → AVRSimulator.setPinState(arduinoPin, LOW)
  → AVRIOPort.setPin() injects external pin state
  → CPU reads pin in next instruction`}

Key Frontend Stores (Zustand)

StoreKey StatePurpose
useEditorStorefiles[], activeFileIdMulti-file Monaco workspace
useSimulatorStoresimulator, components, wires, runningSimulation + canvas state
useAuthStoreuser, tokenAuth (persisted localStorage)
useProjectStoreprojectId, slugCurrently open project

Backend Routes

RouteDescription
POST /api/compile/Compile sketch files → Intel HEX / UF2
GET /api/compile/boardsList available boards
GET/POST /api/auth/*Email/password + Google OAuth
GET/POST /api/projects/*CRUD project persistence (SQLite)
GET /api/libraries/*Arduino Library Manager integration
GET /healthHealth check endpoint

Wire System

Wires are stored as objects with start/end endpoints tied to component pin positions:

{`{
  id: string
  start: { componentId, pinName, x, y }
  end:   { componentId, pinName, x, y }
  color: string
  signalType: 'digital' | 'analog' | 'power-vcc' | 'power-gnd'
}`}
Full details:{' '} See{' '} docs/ARCHITECTURE.md {' '} in the repository.
); /* ── Wokwi Libraries Section ──────────────────────────── */ const WokwiLibsSection: React.FC = () => (
// open-source libs

Wokwi Libraries

Velxio uses official Wokwi open-source repositories cloned locally in wokwi-libs/. This gives you up-to-date, compatible emulation engines and visual components without npm registry dependencies.

Cloned Repositories

LibraryLocationPurpose
wokwi-elements wokwi-libs/wokwi-elements/ 48+ Lit Web Components (LEDs, LCDs, sensors, buttons…)
avr8js wokwi-libs/avr8js/ ATmega328p / ATmega2560 CPU emulator at 16 MHz
rp2040js wokwi-libs/rp2040js/ Raspberry Pi Pico (RP2040) emulator

Vite Configuration

frontend/vite.config.ts uses path aliases so imports resolve to the local builds:

{`resolve: {
  alias: {
    'avr8js':          '../wokwi-libs/avr8js/dist/esm',
    '@wokwi/elements': '../wokwi-libs/wokwi-elements/dist/esm',
  },
}`}

Updating the Libraries

All at once (recommended)

{`# Windows
update-wokwi-libs.bat`}

Manually

{`cd wokwi-libs/wokwi-elements
git pull origin main
npm install && npm run build

cd ../avr8js
git pull origin main
npm install && npm run build

cd ../rp2040js
git pull origin main
npm install && npm run build`}

After updating wokwi-elements

Regenerate component metadata so new components appear in the picker:

{`cd frontend
npx tsx ../scripts/generate-component-metadata.ts`}

Available Wokwi Components (48)

CategoryComponents
BoardsArduino Uno, Mega, Nano, ESP32 DevKit
SensorsDHT22, HC-SR04, PIR, Photoresistor, NTC, Joystick
DisplaysLCD 16×2, LCD 20×4, 7-Segment
InputPush button, 6mm button, Slide switch, DIP switch 8, Potentiometer
OutputLED, RGB LED, LED bar graph, Buzzer, NeoPixel
MotorsServo, Stepper motor
PassiveResistor, Slide potentiometer, LED ring, Matrix keypad
OtherIR receiver, DS1307 RTC, breadboards, etc.

How avr8js Powers the Simulation

{`import { CPU, avrInstruction, AVRTimer, AVRUSART, AVRADC, AVRIOPort } from 'avr8js';

const cpu   = new CPU(programMemory);          // ATmega328p at 16 MHz
const portB = new AVRIOPort(cpu, portBConfig); // digital pins 8-13
const portC = new AVRIOPort(cpu, portCConfig); // analog pins A0-A5
const portD = new AVRIOPort(cpu, portDConfig); // digital pins 0-7

function runFrame() {
  const cycles = Math.floor(267_000 * speed);
  for (let i = 0; i < cycles; i++) {
    avrInstruction(cpu); // execute one AVR instruction
    cpu.tick();          // advance timers + peripherals
  }
  requestAnimationFrame(runFrame);
}`}
Full details:{' '} See{' '} docs/WOKWI_LIBS.md {' '} in the repository.
); /* ── MCP Server Section ───────────────────────────────── */ const McpSection: React.FC = () => (
// AI integration

MCP Server

Velxio exposes a{' '} Model Context Protocol {' '} (MCP) server that lets AI agents (Claude, Cursor, and others) create circuits, generate code, and compile Arduino sketches directly.

Available Tools

ToolDescription
compile_projectCompile Arduino sketch files → Intel HEX / binary
run_projectCompile and mark artifact as simulation-ready
import_wokwi_jsonParse a Wokwi diagram.json → Velxio circuit
export_wokwi_jsonSerialise a Velxio circuit → Wokwi diagram.json
create_circuitCreate a new circuit definition
update_circuitMerge changes into an existing circuit
generate_code_filesGenerate starter .ino code from a circuit

Transport Options

1. stdio — Claude Desktop / CLI agents

{`cd backend
python mcp_server.py`}

Claude Desktop config (~/.claude/claude_desktop_config.json):

{`{
  "mcpServers": {
    "velxio": {
      "command": "python",
      "args": ["/absolute/path/to/velxio/backend/mcp_server.py"]
    }
  }
}`}

2. SSE / HTTP — Cursor IDE / web agents

{`cd backend
python mcp_sse_server.py --port 8002`}

MCP client config:

{`{
  "mcpServers": {
    "velxio": { "url": "http://localhost:8002/sse" }
  }
}`}

Circuit Data Format

Velxio circuits are plain JSON objects:

{`{
  "board_fqbn": "arduino:avr:uno",
  "version": 1,
  "components": [
    { "id": "led1", "type": "wokwi-led", "left": 200, "top": 100,
      "rotate": 0, "attrs": { "color": "red" } }
  ],
  "connections": [
    { "from_part": "uno", "from_pin": "13",
      "to_part": "led1", "to_pin": "A", "color": "green" }
  ]
}`}

Supported Board FQBNs

BoardFQBN
Arduino Unoarduino:avr:uno
Arduino Megaarduino:avr:mega
Arduino Nanoarduino:avr:nano
Raspberry Pi Picorp2040:rp2040:rpipico

Example — Blink LED from Scratch

{`// Step 1 — Create a circuit
{
  "tool": "create_circuit",
  "arguments": {
    "board_fqbn": "arduino:avr:uno",
    "components": [
      { "id": "led1", "type": "wokwi-led",
        "left": 150, "top": 100, "attrs": { "color": "red" } },
      { "id": "r1", "type": "wokwi-resistor",
        "left": 150, "top": 180, "attrs": { "value": "220" } }
    ],
    "connections": [
      { "from_part": "uno", "from_pin": "13",
        "to_part": "led1", "to_pin": "A", "color": "green" },
      { "from_part": "led1", "from_pin": "C",
        "to_part": "r1",   "to_pin": "1", "color": "black" },
      { "from_part": "r1",   "from_pin": "2",
        "to_part": "uno",  "to_pin": "GND.1", "color": "black" }
    ]
  }
}

// Step 2 — Generate code
{
  "tool": "generate_code_files",
  "arguments": {
    "circuit": "",
    "sketch_name": "blink",
    "extra_instructions": "Blink the red LED every 500ms"
  }
}

// Step 3 — Compile
{
  "tool": "compile_project",
  "arguments": {
    "files": [
      {
        "name": "blink.ino",
        "content": "void setup(){pinMode(13,OUTPUT);}\\nvoid loop(){digitalWrite(13,HIGH);delay(500);digitalWrite(13,LOW);delay(500);}"
      }
    ],
    "board": "arduino:avr:uno"
  }
}`}

Setup

{`cd backend
pip install -r requirements.txt

# Ensure arduino-cli is installed
arduino-cli version
arduino-cli core update-index
arduino-cli core install arduino:avr

# Run tests
python -m pytest tests/test_mcp_tools.py -v`}
Full reference:{' '} See{' '} docs/MCP.md {' '} in the repository.
); /* ── Setup / Project Status Section ──────────────────── */ const SetupSection: React.FC = () => (
// project status

Project Status

A comprehensive overview of all features currently implemented in Velxio.

AVR Emulation (avr8js)

FeatureStatus
ATmega328p CPU at 16 MHz✅ Working
Timer0, Timer1, Timer2✅ Working
USART (Serial)✅ Working
ADC (analogRead)✅ Working
Full GPIO (PORTB / PORTC / PORTD)✅ Working
~60 FPS loop (267k cycles/frame)✅ Working
Speed control (0.1× – 10×)✅ Working
PWM monitoring (6 channels)✅ Working
External pin injection (inputs)✅ Working

Component System (48+)

FeatureStatus
Automatic discovery via AST✅ 48 components detected
ComponentPickerModal with search✅ Working
9 categories with filters✅ Working
Generic DynamicComponent renderer✅ Working
Drag-and-drop on canvas✅ Working
Rotation (90° increments)✅ Working
Properties dialog (single-click)✅ Working
Pin overlay (clickable cyan dots)✅ Working

Interactive Parts (16 simulated)

PartTypeStatus
LEDOutput
RGB LEDOutput (digital + PWM)
LED Bar Graph (10 LEDs)Output
7-Segment DisplayOutput
PushbuttonInput
Pushbutton 6mmInput
Slide SwitchInput
DIP Switch 8Input
PotentiometerInput (ADC)
Slide PotentiometerInput (ADC)
PhotoresistorInput / Output
Analog JoystickInput (ADC + digital)
ServoOutput
BuzzerOutput (Web Audio)
LCD 1602Output (full HD44780)
LCD 2004Output (full HD44780)

Wire System

FeatureStatus
Pin-to-pin creation with click✅ Working
Real-time preview (green dashed)✅ Working
Orthogonal routing (no diagonals)✅ Working
Segment editing (perpendicular drag)✅ Working
8 colours by signal type✅ Working
Auto-update when moving components✅ Working
Grid snapping (20 px)✅ Working
Wire selection and deletion✅ Working

Example Projects (8)

ExampleCategoryDifficulty
Blink LEDBasicsBeginner
Traffic LightBasicsBeginner
Button ControlBasicsBeginner
Fade LED (PWM)BasicsBeginner
Serial Hello WorldCommunicationBeginner
RGB LED ColorsBasicsIntermediate
Simon Says GameGamesAdvanced
LCD 20×4 DisplayDisplaysIntermediate

Troubleshooting

ProblemSolution
Components not displayed
cd wokwi-libs/wokwi-elements{'\n'}npm run build
Cannot find module 'avr8js'
cd wokwi-libs/avr8js{'\n'}npm install && npm run build
LED doesn't blink Compile first, then click Run. Check pin assignment in the component property dialog.
New component not in picker
cd frontend{'\n'}npx tsx ../scripts/generate-component-metadata.ts
Full status:{' '} See{' '} docs/SETUP_COMPLETE.md {' '} in the repository.
); const SECTION_MAP: Record = { intro: IntroSection, 'getting-started': GettingStartedSection, emulator: EmulatorSection, components: ComponentsSection, roadmap: RoadmapSection, architecture: ArchitectureSection, 'wokwi-libs': WokwiLibsSection, mcp: McpSection, setup: SetupSection, }; /* ── Page ─────────────────────────────────────────────── */ export const DocsPage: React.FC = () => { const { section } = useParams<{ section?: string }>(); const navigate = useNavigate(); const [sidebarOpen, setSidebarOpen] = useState(false); const user = useAuthStore((s) => s.user); // Derive active section from URL; fall back to 'intro' const activeSection: SectionId = section && VALID_SECTIONS.includes(section as SectionId) ? (section as SectionId) : 'intro'; // Redirect bare /docs → /docs/intro so every section has a canonical URL useEffect(() => { if (!section) { navigate('/docs/intro', { replace: true }); } }, [section, navigate]); // Capture the original values once on mount and restore them on unmount useEffect(() => { const origTitle = document.title; // Helper to capture an element and its original attribute value const captureAttr = (selector: string, attr: string): [E | null, string] => { const el = document.querySelector(selector); return [el, el?.getAttribute(attr) ?? '']; }; const [descEl, origDesc] = captureAttr('meta[name="description"]', 'content'); const [canonicalEl, origCanonical] = captureAttr('link[rel="canonical"]', 'href'); const [ogTitleEl, origOgTitle] = captureAttr('meta[property="og:title"]', 'content'); const [ogDescEl, origOgDesc] = captureAttr('meta[property="og:description"]', 'content'); const [ogUrlEl, origOgUrl] = captureAttr('meta[property="og:url"]', 'content'); const [twTitleEl, origTwTitle] = captureAttr('meta[name="twitter:title"]', 'content'); const [twDescEl, origTwDesc] = captureAttr('meta[name="twitter:description"]', 'content'); return () => { document.title = origTitle; descEl?.setAttribute('content', origDesc); canonicalEl?.setAttribute('href', origCanonical); ogTitleEl?.setAttribute('content', origOgTitle); ogDescEl?.setAttribute('content', origOgDesc); ogUrlEl?.setAttribute('content', origOgUrl); twTitleEl?.setAttribute('content', origTwTitle); twDescEl?.setAttribute('content', origTwDesc); document.getElementById('docs-jsonld')?.remove(); }; }, []); // runs once on mount; cleanup runs once on unmount // Update all head metadata + JSON-LD per section. // No cleanup here — the mount effect above restores defaults on unmount, // and on a section change the next run of this effect immediately overwrites. useEffect(() => { const meta = SECTION_META[activeSection]; const pageUrl = `${BASE_URL}/docs/${activeSection}`; document.title = meta.title; const set = (selector: string, value: string) => document.querySelector(selector)?.setAttribute('content', value); set('meta[name="description"]', meta.description); set('meta[property="og:title"]', meta.title); set('meta[property="og:description"]', meta.description); set('meta[property="og:url"]', pageUrl); set('meta[name="twitter:title"]', meta.title); set('meta[name="twitter:description"]', meta.description); const canonicalEl = document.querySelector('link[rel="canonical"]'); if (canonicalEl) canonicalEl.setAttribute('href', pageUrl); // Build the breadcrumb section label for JSON-LD const activeNav = NAV_ITEMS.find((i) => i.id === activeSection); const sectionLabel = activeNav?.label ?? activeSection; // Inject / update JSON-LD structured data for this doc page const ldId = 'docs-jsonld'; let ldScript = document.getElementById(ldId) as HTMLScriptElement | null; if (!ldScript) { ldScript = document.createElement('script'); ldScript.id = ldId; ldScript.type = 'application/ld+json'; document.head.appendChild(ldScript); } ldScript.textContent = JSON.stringify({ '@context': 'https://schema.org', '@graph': [ { '@type': 'TechArticle', headline: meta.title, description: meta.description, url: pageUrl, isPartOf: { '@type': 'WebSite', url: `${BASE_URL}/`, name: 'Velxio' }, inLanguage: 'en-US', author: AUTHOR, }, { '@type': 'BreadcrumbList', itemListElement: [ { '@type': 'ListItem', position: 1, name: 'Home', item: `${BASE_URL}/` }, { '@type': 'ListItem', position: 2, name: 'Documentation', item: `${BASE_URL}/docs/intro` }, { '@type': 'ListItem', position: 3, name: sectionLabel, item: pageUrl }, ], }, ], }); }, [activeSection]); const ActiveContent = SECTION_MAP[activeSection]; const activeIdx = NAV_ITEMS.findIndex((i) => i.id === activeSection); return (
{/* Nav */}
{/* Sidebar */} {/* Main content */}
{/* Prev / Next navigation */}
{activeIdx > 0 && ( window.scrollTo(0, 0)} > ← {NAV_ITEMS[activeIdx - 1].label} )} {activeIdx < NAV_ITEMS.length - 1 && ( window.scrollTo(0, 0)} > {NAV_ITEMS[activeIdx + 1].label} → )}
); };