velxio/frontend/src/pages/DocsPage.tsx

1243 lines
52 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 = () => (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
<rect x="5" y="5" width="14" height="14" rx="2" />
<rect x="9" y="9" width="6" height="6" />
<path d="M9 1v4M15 1v4M9 19v4M15 19v4M1 9h4M1 15h4M19 9h4M19 15h4" />
</svg>
);
const IcoGitHub = () => (
<svg viewBox="0 0 24 24" fill="currentColor">
<path d="M12 0C5.37 0 0 5.37 0 12c0 5.3 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61-.546-1.385-1.335-1.755-1.335-1.755-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 21.795 24 17.295 24 12c0-6.63-5.37-12-12-12z" />
</svg>
);
/* ── 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<SectionId, SectionMeta> = {
'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 = () => (
<div className="docs-section">
<span className="docs-label">// overview</span>
<h1>Introduction</h1>
<p>
<strong>Velxio</strong> is a fully local, open-source Arduino emulator that runs entirely in your browser.
Write Arduino C++ code, compile it with a real <code>arduino-cli</code> backend, and simulate it using
true AVR8 / RP2040 CPU emulation with 48+ interactive electronic components, all without installing
any software on your machine.
</p>
<h2>Why Velxio?</h2>
<ul>
<li><strong>No installation required</strong> everything runs in the browser.</li>
<li><strong>Real emulation</strong> not a simplified model, but accurate AVR8 / RP2040 CPU emulation.</li>
<li><strong>Interactive components</strong> LEDs, buttons, potentiometers, displays, sensors, and more.</li>
<li><strong>Open-source</strong> inspect, modify, and self-host it yourself.</li>
</ul>
<h2>Supported Boards</h2>
<table>
<thead>
<tr><th>Board</th><th>CPU</th><th>Emulator</th></tr>
</thead>
<tbody>
<tr><td>Arduino Uno</td><td>ATmega328p @ 16 MHz</td><td>avr8js</td></tr>
<tr><td>Arduino Nano</td><td>ATmega328p @ 16 MHz</td><td>avr8js</td></tr>
<tr><td>Arduino Mega</td><td>ATmega2560 @ 16 MHz</td><td>avr8js</td></tr>
<tr><td>Raspberry Pi Pico</td><td>RP2040 @ 133 MHz</td><td>rp2040js</td></tr>
</tbody>
</table>
<div className="docs-callout">
<strong>Live Demo:</strong>{' '}
<a href="https://velxio.dev" target="_blank" rel="noopener noreferrer">velxio.dev</a>
{' '} no installation needed, open the editor and start simulating immediately.
</div>
</div>
);
const GettingStartedSection: React.FC = () => (
<div className="docs-section">
<span className="docs-label">// setup</span>
<h1>Getting Started</h1>
<p>Follow these steps to simulate your first Arduino sketch.</p>
<h2>Option 1: Use the Hosted Version</h2>
<p>
No installation needed go to{' '}
<a href="https://velxio.dev" target="_blank" rel="noopener noreferrer">https://velxio.dev</a>{' '}
and start coding immediately.
</p>
<h2>Option 2: Self-Host with Docker</h2>
<p>Run a single Docker command to start a fully local instance:</p>
<pre><code>{`docker run -d \\
--name velxio \\
-p 3080:80 \\
-v $(pwd)/data:/app/data \\
ghcr.io/davidmonterocrespo24/velxio:master`}</code></pre>
<p>Then open <strong>http://localhost:3080</strong> in your browser.</p>
<h2>Option 3: Manual Setup (Development)</h2>
<p><strong>Prerequisites:</strong> Node.js 18+, Python 3.12+, <code>arduino-cli</code></p>
<h3>1. Clone the repository</h3>
<pre><code>{`git clone https://github.com/davidmonterocrespo24/velxio.git
cd velxio`}</code></pre>
<h3>2. Start the backend</h3>
<pre><code>{`cd backend
python -m venv venv && source venv/bin/activate
pip install -r requirements.txt
uvicorn app.main:app --reload --port 8001`}</code></pre>
<h3>3. Start the frontend</h3>
<pre><code>{`cd frontend
npm install
npm run dev`}</code></pre>
<p>Open <strong>http://localhost:5173</strong>.</p>
<h3>4. Set up arduino-cli (first time)</h3>
<pre><code>{`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`}</code></pre>
<h2>Your First Simulation</h2>
<ol>
<li><strong>Open the editor</strong> at <a href="https://velxio.dev/editor" target="_blank" rel="noopener noreferrer">velxio.dev/editor</a>.</li>
<li><strong>Select a board</strong> from the toolbar (e.g., <em>Arduino Uno</em>).</li>
<li><strong>Write Arduino code</strong> in the Monaco editor, for example:</li>
</ol>
<pre><code>{`void setup() {
pinMode(13, OUTPUT);
}
void loop() {
digitalWrite(13, HIGH);
delay(500);
digitalWrite(13, LOW);
delay(500);
}`}</code></pre>
<ol start={4}>
<li><strong>Click Compile</strong> the backend calls <code>arduino-cli</code> and returns a <code>.hex</code> file.</li>
<li><strong>Click Run</strong> the AVR8 emulator executes the compiled program.</li>
<li><strong>Add components</strong> using the component picker (click the <strong>+</strong> button on the canvas).</li>
<li><strong>Connect wires</strong> by clicking a component pin and then another pin.</li>
</ol>
<h2>Troubleshooting</h2>
<table>
<thead>
<tr><th>Problem</th><th>Solution</th></tr>
</thead>
<tbody>
<tr>
<td><code>arduino-cli: command not found</code></td>
<td>Install <code>arduino-cli</code> and add it to your PATH.</td>
</tr>
<tr>
<td>LED doesn't blink</td>
<td>Check the browser console for port listener errors; verify pin assignment in the component property dialog.</td>
</tr>
<tr>
<td>Serial Monitor is empty</td>
<td>Ensure <code>Serial.begin()</code> is called inside <code>setup()</code> before any <code>Serial.print()</code>.</td>
</tr>
<tr>
<td>Compilation errors</td>
<td>Check the compilation console at the bottom of the editor for full <code>arduino-cli</code> output.</td>
</tr>
</tbody>
</table>
</div>
);
const EmulatorSection: React.FC = () => (
<div className="docs-section">
<span className="docs-label">// internals</span>
<h1>Emulator Architecture</h1>
<p>
Velxio uses <strong>real CPU emulation</strong> rather than a simplified model.
This document describes how each layer of the simulation works.
</p>
<h2>High-Level Data Flow</h2>
<pre><code>{`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`}</code></pre>
<h2>AVR8 Emulation (Arduino Uno / Nano / Mega)</h2>
<p>
The AVR backend uses <a href="https://github.com/wokwi/avr8js" target="_blank" rel="noopener noreferrer">avr8js</a>,
which implements a complete ATmega328p / ATmega2560 processor.
</p>
<h3>Execution Loop</h3>
<p>Each animation frame executes approximately 267,000 CPU cycles (16 MHz ÷ 60 FPS):</p>
<pre><code>{`avrInstruction(cpu); // decode and execute one AVR instruction
cpu.tick(); // advance peripheral timers and counters`}</code></pre>
<h3>Supported Peripherals</h3>
<table>
<thead>
<tr><th>Peripheral</th><th>Details</th></tr>
</thead>
<tbody>
<tr><td>GPIO</td><td>PORTB (pins 813), PORTC (A0A5), PORTD (pins 07)</td></tr>
<tr><td>Timer0 / Timer1 / Timer2</td><td><code>millis()</code>, <code>delay()</code>, PWM via <code>analogWrite()</code></td></tr>
<tr><td>USART</td><td>Full transmit and receive — powers the Serial Monitor</td></tr>
<tr><td>ADC</td><td>10-bit, 5 V reference on pins A0A5</td></tr>
<tr><td>SPI</td><td>Hardware SPI (enables ILI9341, SD card, etc.)</td></tr>
<tr><td>I2C (TWI)</td><td>Hardware I2C with virtual device bus</td></tr>
</tbody>
</table>
<h3>Pin Mapping</h3>
<table>
<thead>
<tr><th>Arduino Pin</th><th>AVR Port</th><th>Bit</th></tr>
</thead>
<tbody>
<tr><td>07</td><td>PORTD</td><td>07</td></tr>
<tr><td>813</td><td>PORTB</td><td>05</td></tr>
<tr><td>A0A5</td><td>PORTC</td><td>05</td></tr>
</tbody>
</table>
<h2>RP2040 Emulation (Raspberry Pi Pico)</h2>
<p>
The RP2040 backend uses <a href="https://github.com/wokwi/rp2040js" target="_blank" rel="noopener noreferrer">rp2040js</a>.
</p>
<ul>
<li>Real RP2040 emulation at 133 MHz</li>
<li>UART0 serial output displayed in the Serial Monitor</li>
<li>12-bit ADC on GPIO 2629 (A0A3) with 3.3 V reference</li>
</ul>
<h2>HEX File Format</h2>
<p>Arduino compilation produces <strong>Intel HEX</strong> format. The parser in <code>hexParser.ts</code>:</p>
<ol>
<li>Reads lines starting with <code>:</code></li>
<li>Extracts the address, record type, and data bytes</li>
<li>Returns a <code>Uint8Array</code> of program bytes</li>
<li><code>AVRSimulator</code> converts this to a <code>Uint16Array</code> (16-bit words, little-endian)</li>
</ol>
<h2>Key Source Files</h2>
<table>
<thead>
<tr><th>File</th><th>Purpose</th></tr>
</thead>
<tbody>
<tr><td><code>frontend/src/simulation/AVRSimulator.ts</code></td><td>AVR8 CPU emulator wrapper</td></tr>
<tr><td><code>frontend/src/simulation/PinManager.ts</code></td><td>Maps Arduino pins to UI components</td></tr>
<tr><td><code>frontend/src/utils/hexParser.ts</code></td><td>Intel HEX parser</td></tr>
<tr><td><code>frontend/src/components/simulator/SimulatorCanvas.tsx</code></td><td>Canvas rendering</td></tr>
<tr><td><code>backend/app/services/arduino_cli.py</code></td><td>arduino-cli wrapper</td></tr>
<tr><td><code>backend/app/api/routes/compile.py</code></td><td>Compilation API endpoint</td></tr>
</tbody>
</table>
</div>
);
const ComponentsSection: React.FC = () => (
<div className="docs-section">
<span className="docs-label">// reference</span>
<h1>Components Reference</h1>
<p>
Velxio ships with <strong>48+ interactive electronic components</strong> powered by{' '}
<a href="https://github.com/wokwi/wokwi-elements" target="_blank" rel="noopener noreferrer">wokwi-elements</a>.
All components can be placed on the simulation canvas, connected with wires, and interact with your Arduino sketch in real time.
</p>
<h2>Adding Components</h2>
<ol>
<li>Click the <strong>+</strong> button on the simulation canvas.</li>
<li>Use <strong>search</strong> or browse by <strong>category</strong> in the component picker.</li>
<li>Click a component to place it on the canvas.</li>
<li><strong>Drag</strong> to reposition; click to open the <strong>Property Dialog</strong>.</li>
</ol>
<h2>Connecting Components</h2>
<ol>
<li>Click a <strong>pin</strong> on any component — a wire starts from that pin.</li>
<li>Click a <strong>destination pin</strong> to complete the connection.</li>
<li>Wires are <strong>color-coded</strong> by signal type:</li>
</ol>
<table>
<thead>
<tr><th>Color</th><th>Signal Type</th></tr>
</thead>
<tbody>
<tr><td><span className="wire-dot" style={{ background: '#ef4444' }} /> Red</td><td>VCC (power)</td></tr>
<tr><td><span className="wire-dot" style={{ background: '#374151' }} /> Black</td><td>GND (ground)</td></tr>
<tr><td><span className="wire-dot" style={{ background: '#3b82f6' }} /> Blue</td><td>Analog</td></tr>
<tr><td><span className="wire-dot" style={{ background: '#22c55e' }} /> Green</td><td>Digital</td></tr>
<tr><td><span className="wire-dot" style={{ background: '#a855f7' }} /> Purple</td><td>PWM</td></tr>
<tr><td><span className="wire-dot" style={{ background: '#eab308' }} /> Gold</td><td>I2C (SDA/SCL)</td></tr>
<tr><td><span className="wire-dot" style={{ background: '#f97316' }} /> Orange</td><td>SPI (MOSI/MISO/SCK)</td></tr>
<tr><td><span className="wire-dot" style={{ background: '#06b6d4' }} /> Cyan</td><td>USART (TX/RX)</td></tr>
</tbody>
</table>
<h2>Component Categories</h2>
<h3>Output</h3>
<table>
<thead><tr><th>Component</th><th>Description</th></tr></thead>
<tbody>
<tr><td>LED</td><td>Single LED with configurable color</td></tr>
<tr><td>RGB LED</td><td>Three-color LED (red, green, blue channels)</td></tr>
<tr><td>7-Segment Display</td><td>Single digit numeric display</td></tr>
<tr><td>LCD 16×2</td><td>2-line character LCD (I2C or parallel)</td></tr>
<tr><td>LCD 20×4</td><td>4-line character LCD</td></tr>
<tr><td>ILI9341 TFT</td><td>240×320 color TFT display (SPI)</td></tr>
<tr><td>Buzzer</td><td>Passive piezo buzzer</td></tr>
<tr><td>NeoPixel</td><td>Individually addressable RGB LED strip</td></tr>
</tbody>
</table>
<h3>Input</h3>
<table>
<thead><tr><th>Component</th><th>Description</th></tr></thead>
<tbody>
<tr><td>Push Button</td><td>Momentary push button</td></tr>
<tr><td>Slide Switch</td><td>SPDT slide switch</td></tr>
<tr><td>Potentiometer</td><td>Analog voltage divider (ADC input)</td></tr>
<tr><td>Rotary Encoder</td><td>Incremental rotary encoder</td></tr>
<tr><td>Keypad 4×4</td><td>16-button matrix keypad</td></tr>
<tr><td>Joystick</td><td>Dual-axis analog joystick</td></tr>
</tbody>
</table>
<h3>Sensors</h3>
<table>
<thead><tr><th>Component</th><th>Description</th></tr></thead>
<tbody>
<tr><td>HC-SR04</td><td>Ultrasonic distance sensor</td></tr>
<tr><td>DHT22</td><td>Temperature and humidity sensor</td></tr>
<tr><td>PIR Motion</td><td>Passive infrared motion sensor</td></tr>
<tr><td>Photoresistor</td><td>Light-dependent resistor (LDR)</td></tr>
<tr><td>IR Receiver</td><td>38 kHz infrared receiver</td></tr>
</tbody>
</table>
<h3>Passive Components</h3>
<table>
<thead><tr><th>Component</th><th>Description</th></tr></thead>
<tbody>
<tr><td>Resistor</td><td>Standard resistor (configurable value)</td></tr>
<tr><td>Capacitor</td><td>Electrolytic capacitor</td></tr>
<tr><td>Inductor</td><td>Coil inductor</td></tr>
</tbody>
</table>
<h2>Component Properties</h2>
<p>Each component has a <strong>Property Dialog</strong> accessible by clicking it on the canvas:</p>
<table>
<thead><tr><th>Property</th><th>Description</th></tr></thead>
<tbody>
<tr><td>Arduino Pin</td><td>The digital or analog pin this component is connected to</td></tr>
<tr><td>Color</td><td>Visual color (LEDs, wires)</td></tr>
<tr><td>Value</td><td>Component value (e.g., resistance in Ω)</td></tr>
<tr><td>Rotation</td><td>Rotate in 90° increments</td></tr>
<tr><td>Delete</td><td>Remove the component from the canvas</td></tr>
</tbody>
</table>
</div>
);
const RoadmapSection: React.FC = () => (
<div className="docs-section">
<span className="docs-label">// future</span>
<h1>Roadmap</h1>
<p>Features that are implemented, in progress, and planned for future releases of Velxio.</p>
<h2>✅ Implemented</h2>
<ul>
<li>Monaco Editor with C++ syntax highlighting, autocomplete, and minimap</li>
<li>Multi-file workspace — create, rename, delete, and switch between files</li>
<li>Arduino compilation via <code>arduino-cli</code> (multi-file sketch support)</li>
<li>Real ATmega328p / ATmega2560 emulation at 16 MHz via avr8js</li>
<li>Full GPIO, Timers, USART, ADC, SPI, I2C support</li>
<li>Real RP2040 emulation at 133 MHz via rp2040js</li>
<li>48+ wokwi-elements components with component picker</li>
<li>Wire creation, orthogonal routing, and segment-based editing</li>
<li>Serial Monitor with auto baud-rate detection and send support</li>
<li>Library Manager (browse and install Arduino libraries)</li>
<li>Email/password and Google OAuth authentication</li>
<li>Project save, update, delete with permanent URLs</li>
<li>8 built-in example projects</li>
<li>Single-container Docker image published to GHCR + Docker Hub</li>
</ul>
<h2>🔄 In Progress</h2>
<ul>
<li>Functional wire connections — electrical signal routing and validation</li>
<li>Wire connection error handling — detect short circuits and invalid connections</li>
</ul>
<h2>🗓 Planned — Near-Term</h2>
<ul>
<li>Undo / redo for code edits and canvas changes</li>
<li>Export / import projects as <code>.zip</code> files</li>
<li>More boards — ESP32, Arduino Leonardo</li>
<li>Breadboard — place components with automatic wire routing</li>
</ul>
<h2>🗓 Planned — Mid-Term</h2>
<ul>
<li>TypeDoc API documentation — auto-generated from source code</li>
<li>GitHub Pages docs site — automatic deployment on push to <code>main</code></li>
<li>More sensor simulations — HC-SR04, DHT22, IR receiver</li>
<li>EEPROM emulation — persistent read/write across simulation restarts</li>
<li>Oscilloscope component — plot analog pin voltages over time</li>
</ul>
<h2>🗓 Planned — Long-Term</h2>
<ul>
<li>Multiplayer — share and co-edit simulations in real time</li>
<li>Embedded tutorial system — step-by-step guided projects inside the editor</li>
<li>Custom component SDK — define new components with a JSON/TypeScript API</li>
<li>Mobile / tablet support — responsive layout for touch devices</li>
</ul>
<div className="docs-callout">
<strong>Want to contribute?</strong>{' '}
Feature requests, bug reports, and pull requests are welcome at{' '}
<a href={GITHUB_URL} target="_blank" rel="noopener noreferrer">github.com/davidmonterocrespo24/velxio</a>.
</div>
</div>
);
/* ── Architecture Section ─────────────────────────────── */
const ArchitectureSection: React.FC = () => (
<div className="docs-section">
<span className="docs-label">// system design</span>
<h1>Project Architecture</h1>
<p>
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.
</p>
<h2>High-Level Overview</h2>
<pre><code>{`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`}</code></pre>
<h2>Data Flows</h2>
<h3>1. Compilation</h3>
<pre><code>{`Click "Compile"
→ EditorToolbar reads all workspace files
→ POST /api/compile/ { files, board_fqbn }
→ Backend: ArduinoCLIService writes temp dir
→ arduino-cli compile --fqbn <board> --output-dir build/
→ Returns hex_content (Intel HEX string)
→ useSimulatorStore.setCompiledHex() → loadHex()`}</code></pre>
<h3>2. Simulation Loop</h3>
<pre><code>{`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`}</code></pre>
<h3>3. Input Components</h3>
<pre><code>{`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`}</code></pre>
<h2>Key Frontend Stores (Zustand)</h2>
<table>
<thead>
<tr><th>Store</th><th>Key State</th><th>Purpose</th></tr>
</thead>
<tbody>
<tr><td><code>useEditorStore</code></td><td>files[], activeFileId</td><td>Multi-file Monaco workspace</td></tr>
<tr><td><code>useSimulatorStore</code></td><td>simulator, components, wires, running</td><td>Simulation + canvas state</td></tr>
<tr><td><code>useAuthStore</code></td><td>user, token</td><td>Auth (persisted localStorage)</td></tr>
<tr><td><code>useProjectStore</code></td><td>projectId, slug</td><td>Currently open project</td></tr>
</tbody>
</table>
<h2>Backend Routes</h2>
<table>
<thead>
<tr><th>Route</th><th>Description</th></tr>
</thead>
<tbody>
<tr><td><code>POST /api/compile/</code></td><td>Compile sketch files → Intel HEX / UF2</td></tr>
<tr><td><code>GET /api/compile/boards</code></td><td>List available boards</td></tr>
<tr><td><code>GET/POST /api/auth/*</code></td><td>Email/password + Google OAuth</td></tr>
<tr><td><code>GET/POST /api/projects/*</code></td><td>CRUD project persistence (SQLite)</td></tr>
<tr><td><code>GET /api/libraries/*</code></td><td>Arduino Library Manager integration</td></tr>
<tr><td><code>GET /health</code></td><td>Health check endpoint</td></tr>
</tbody>
</table>
<h2>Wire System</h2>
<p>Wires are stored as objects with start/end endpoints tied to component pin positions:</p>
<pre><code>{`{
id: string
start: { componentId, pinName, x, y }
end: { componentId, pinName, x, y }
color: string
signalType: 'digital' | 'analog' | 'power-vcc' | 'power-gnd'
}`}</code></pre>
<ul>
<li>Orthogonal routing — no diagonal segments</li>
<li>Segment drag — drag perpendicular to segment orientation</li>
<li>Auto-update — wire positions recalculate when components move</li>
<li>Grid snapping — 20 px grid for all wire endpoints</li>
</ul>
<div className="docs-callout">
<strong>Full details:</strong>{' '}
See{' '}
<a href={`${GITHUB_URL}/blob/master/docs/ARCHITECTURE.md`} target="_blank" rel="noopener noreferrer">
docs/ARCHITECTURE.md
</a>{' '}
in the repository.
</div>
</div>
);
/* ── Wokwi Libraries Section ──────────────────────────── */
const WokwiLibsSection: React.FC = () => (
<div className="docs-section">
<span className="docs-label">// open-source libs</span>
<h1>Wokwi Libraries</h1>
<p>
Velxio uses official Wokwi open-source repositories cloned locally in <code>wokwi-libs/</code>.
This gives you up-to-date, compatible emulation engines and visual components without npm registry
dependencies.
</p>
<h2>Cloned Repositories</h2>
<table>
<thead>
<tr><th>Library</th><th>Location</th><th>Purpose</th></tr>
</thead>
<tbody>
<tr>
<td><a href="https://github.com/wokwi/wokwi-elements" target="_blank" rel="noopener noreferrer">wokwi-elements</a></td>
<td><code>wokwi-libs/wokwi-elements/</code></td>
<td>48+ Lit Web Components (LEDs, LCDs, sensors, buttons…)</td>
</tr>
<tr>
<td><a href="https://github.com/wokwi/avr8js" target="_blank" rel="noopener noreferrer">avr8js</a></td>
<td><code>wokwi-libs/avr8js/</code></td>
<td>ATmega328p / ATmega2560 CPU emulator at 16 MHz</td>
</tr>
<tr>
<td><a href="https://github.com/wokwi/rp2040js" target="_blank" rel="noopener noreferrer">rp2040js</a></td>
<td><code>wokwi-libs/rp2040js/</code></td>
<td>Raspberry Pi Pico (RP2040) emulator</td>
</tr>
</tbody>
</table>
<h2>Vite Configuration</h2>
<p><code>frontend/vite.config.ts</code> uses path aliases so imports resolve to the local builds:</p>
<pre><code>{`resolve: {
alias: {
'avr8js': '../wokwi-libs/avr8js/dist/esm',
'@wokwi/elements': '../wokwi-libs/wokwi-elements/dist/esm',
},
}`}</code></pre>
<h2>Updating the Libraries</h2>
<h3>All at once (recommended)</h3>
<pre><code>{`# Windows
update-wokwi-libs.bat`}</code></pre>
<h3>Manually</h3>
<pre><code>{`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`}</code></pre>
<h3>After updating wokwi-elements</h3>
<p>Regenerate component metadata so new components appear in the picker:</p>
<pre><code>{`cd frontend
npx tsx ../scripts/generate-component-metadata.ts`}</code></pre>
<h2>Available Wokwi Components (48)</h2>
<table>
<thead>
<tr><th>Category</th><th>Components</th></tr>
</thead>
<tbody>
<tr><td>Boards</td><td>Arduino Uno, Mega, Nano, ESP32 DevKit</td></tr>
<tr><td>Sensors</td><td>DHT22, HC-SR04, PIR, Photoresistor, NTC, Joystick</td></tr>
<tr><td>Displays</td><td>LCD 16×2, LCD 20×4, 7-Segment</td></tr>
<tr><td>Input</td><td>Push button, 6mm button, Slide switch, DIP switch 8, Potentiometer</td></tr>
<tr><td>Output</td><td>LED, RGB LED, LED bar graph, Buzzer, NeoPixel</td></tr>
<tr><td>Motors</td><td>Servo, Stepper motor</td></tr>
<tr><td>Passive</td><td>Resistor, Slide potentiometer, LED ring, Matrix keypad</td></tr>
<tr><td>Other</td><td>IR receiver, DS1307 RTC, breadboards, etc.</td></tr>
</tbody>
</table>
<h2>How avr8js Powers the Simulation</h2>
<pre><code>{`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);
}`}</code></pre>
<div className="docs-callout">
<strong>Full details:</strong>{' '}
See{' '}
<a href={`${GITHUB_URL}/blob/master/docs/WOKWI_LIBS.md`} target="_blank" rel="noopener noreferrer">
docs/WOKWI_LIBS.md
</a>{' '}
in the repository.
</div>
</div>
);
/* ── MCP Server Section ───────────────────────────────── */
const McpSection: React.FC = () => (
<div className="docs-section">
<span className="docs-label">// AI integration</span>
<h1>MCP Server</h1>
<p>
Velxio exposes a{' '}
<a href="https://modelcontextprotocol.io/" target="_blank" rel="noopener noreferrer">
Model Context Protocol
</a>{' '}
(MCP) server that lets AI agents (Claude, Cursor, and others) create circuits,
generate code, and compile Arduino sketches directly.
</p>
<h2>Available Tools</h2>
<table>
<thead>
<tr><th>Tool</th><th>Description</th></tr>
</thead>
<tbody>
<tr><td><code>compile_project</code></td><td>Compile Arduino sketch files → Intel HEX / binary</td></tr>
<tr><td><code>run_project</code></td><td>Compile and mark artifact as simulation-ready</td></tr>
<tr><td><code>import_wokwi_json</code></td><td>Parse a Wokwi <code>diagram.json</code> → Velxio circuit</td></tr>
<tr><td><code>export_wokwi_json</code></td><td>Serialise a Velxio circuit → Wokwi <code>diagram.json</code></td></tr>
<tr><td><code>create_circuit</code></td><td>Create a new circuit definition</td></tr>
<tr><td><code>update_circuit</code></td><td>Merge changes into an existing circuit</td></tr>
<tr><td><code>generate_code_files</code></td><td>Generate starter <code>.ino</code> code from a circuit</td></tr>
</tbody>
</table>
<h2>Transport Options</h2>
<h3>1. stdio — Claude Desktop / CLI agents</h3>
<pre><code>{`cd backend
python mcp_server.py`}</code></pre>
<p>Claude Desktop config (<code>~/.claude/claude_desktop_config.json</code>):</p>
<pre><code>{`{
"mcpServers": {
"velxio": {
"command": "python",
"args": ["/absolute/path/to/velxio/backend/mcp_server.py"]
}
}
}`}</code></pre>
<h3>2. SSE / HTTP — Cursor IDE / web agents</h3>
<pre><code>{`cd backend
python mcp_sse_server.py --port 8002`}</code></pre>
<p>MCP client config:</p>
<pre><code>{`{
"mcpServers": {
"velxio": { "url": "http://localhost:8002/sse" }
}
}`}</code></pre>
<h2>Circuit Data Format</h2>
<p>Velxio circuits are plain JSON objects:</p>
<pre><code>{`{
"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" }
]
}`}</code></pre>
<h3>Supported Board FQBNs</h3>
<table>
<thead>
<tr><th>Board</th><th>FQBN</th></tr>
</thead>
<tbody>
<tr><td>Arduino Uno</td><td><code>arduino:avr:uno</code></td></tr>
<tr><td>Arduino Mega</td><td><code>arduino:avr:mega</code></td></tr>
<tr><td>Arduino Nano</td><td><code>arduino:avr:nano</code></td></tr>
<tr><td>Raspberry Pi Pico</td><td><code>rp2040:rp2040:rpipico</code></td></tr>
</tbody>
</table>
<h2>Example — Blink LED from Scratch</h2>
<pre><code>{`// 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": "<result from Step 1>",
"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"
}
}`}</code></pre>
<h2>Setup</h2>
<pre><code>{`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`}</code></pre>
<div className="docs-callout">
<strong>Full reference:</strong>{' '}
See{' '}
<a href={`${GITHUB_URL}/blob/master/docs/MCP.md`} target="_blank" rel="noopener noreferrer">
docs/MCP.md
</a>{' '}
in the repository.
</div>
</div>
);
/* ── Setup / Project Status Section ──────────────────── */
const SetupSection: React.FC = () => (
<div className="docs-section">
<span className="docs-label">// project status</span>
<h1>Project Status</h1>
<p>A comprehensive overview of all features currently implemented in Velxio.</p>
<h2>AVR Emulation (avr8js)</h2>
<table>
<thead><tr><th>Feature</th><th>Status</th></tr></thead>
<tbody>
<tr><td>ATmega328p CPU at 16 MHz</td><td>✅ Working</td></tr>
<tr><td>Timer0, Timer1, Timer2</td><td>✅ Working</td></tr>
<tr><td>USART (Serial)</td><td>✅ Working</td></tr>
<tr><td>ADC (<code>analogRead</code>)</td><td>✅ Working</td></tr>
<tr><td>Full GPIO (PORTB / PORTC / PORTD)</td><td>✅ Working</td></tr>
<tr><td>~60 FPS loop (267k cycles/frame)</td><td>✅ Working</td></tr>
<tr><td>Speed control (0.1× 10×)</td><td>✅ Working</td></tr>
<tr><td>PWM monitoring (6 channels)</td><td>✅ Working</td></tr>
<tr><td>External pin injection (inputs)</td><td>✅ Working</td></tr>
</tbody>
</table>
<h2>Component System (48+)</h2>
<table>
<thead><tr><th>Feature</th><th>Status</th></tr></thead>
<tbody>
<tr><td>Automatic discovery via AST</td><td>✅ 48 components detected</td></tr>
<tr><td>ComponentPickerModal with search</td><td>✅ Working</td></tr>
<tr><td>9 categories with filters</td><td>✅ Working</td></tr>
<tr><td>Generic DynamicComponent renderer</td><td>✅ Working</td></tr>
<tr><td>Drag-and-drop on canvas</td><td>✅ Working</td></tr>
<tr><td>Rotation (90° increments)</td><td>✅ Working</td></tr>
<tr><td>Properties dialog (single-click)</td><td>✅ Working</td></tr>
<tr><td>Pin overlay (clickable cyan dots)</td><td>✅ Working</td></tr>
</tbody>
</table>
<h2>Interactive Parts (16 simulated)</h2>
<table>
<thead><tr><th>Part</th><th>Type</th><th>Status</th></tr></thead>
<tbody>
<tr><td>LED</td><td>Output</td><td>✅</td></tr>
<tr><td>RGB LED</td><td>Output (digital + PWM)</td><td>✅</td></tr>
<tr><td>LED Bar Graph (10 LEDs)</td><td>Output</td><td>✅</td></tr>
<tr><td>7-Segment Display</td><td>Output</td><td>✅</td></tr>
<tr><td>Pushbutton</td><td>Input</td><td>✅</td></tr>
<tr><td>Pushbutton 6mm</td><td>Input</td><td>✅</td></tr>
<tr><td>Slide Switch</td><td>Input</td><td>✅</td></tr>
<tr><td>DIP Switch 8</td><td>Input</td><td>✅</td></tr>
<tr><td>Potentiometer</td><td>Input (ADC)</td><td>✅</td></tr>
<tr><td>Slide Potentiometer</td><td>Input (ADC)</td><td>✅</td></tr>
<tr><td>Photoresistor</td><td>Input / Output</td><td>✅</td></tr>
<tr><td>Analog Joystick</td><td>Input (ADC + digital)</td><td>✅</td></tr>
<tr><td>Servo</td><td>Output</td><td>✅</td></tr>
<tr><td>Buzzer</td><td>Output (Web Audio)</td><td>✅</td></tr>
<tr><td>LCD 1602</td><td>Output (full HD44780)</td><td>✅</td></tr>
<tr><td>LCD 2004</td><td>Output (full HD44780)</td><td>✅</td></tr>
</tbody>
</table>
<h2>Wire System</h2>
<table>
<thead><tr><th>Feature</th><th>Status</th></tr></thead>
<tbody>
<tr><td>Pin-to-pin creation with click</td><td>✅ Working</td></tr>
<tr><td>Real-time preview (green dashed)</td><td>✅ Working</td></tr>
<tr><td>Orthogonal routing (no diagonals)</td><td>✅ Working</td></tr>
<tr><td>Segment editing (perpendicular drag)</td><td>✅ Working</td></tr>
<tr><td>8 colours by signal type</td><td>✅ Working</td></tr>
<tr><td>Auto-update when moving components</td><td>✅ Working</td></tr>
<tr><td>Grid snapping (20 px)</td><td>✅ Working</td></tr>
<tr><td>Wire selection and deletion</td><td>✅ Working</td></tr>
</tbody>
</table>
<h2>Example Projects (8)</h2>
<table>
<thead><tr><th>Example</th><th>Category</th><th>Difficulty</th></tr></thead>
<tbody>
<tr><td>Blink LED</td><td>Basics</td><td>Beginner</td></tr>
<tr><td>Traffic Light</td><td>Basics</td><td>Beginner</td></tr>
<tr><td>Button Control</td><td>Basics</td><td>Beginner</td></tr>
<tr><td>Fade LED (PWM)</td><td>Basics</td><td>Beginner</td></tr>
<tr><td>Serial Hello World</td><td>Communication</td><td>Beginner</td></tr>
<tr><td>RGB LED Colors</td><td>Basics</td><td>Intermediate</td></tr>
<tr><td>Simon Says Game</td><td>Games</td><td>Advanced</td></tr>
<tr><td>LCD 20×4 Display</td><td>Displays</td><td>Intermediate</td></tr>
</tbody>
</table>
<h2>Troubleshooting</h2>
<table>
<thead><tr><th>Problem</th><th>Solution</th></tr></thead>
<tbody>
<tr>
<td>Components not displayed</td>
<td><pre style={{margin:0}}><code>cd wokwi-libs/wokwi-elements{'\n'}npm run build</code></pre></td>
</tr>
<tr>
<td><code>Cannot find module 'avr8js'</code></td>
<td><pre style={{margin:0}}><code>cd wokwi-libs/avr8js{'\n'}npm install && npm run build</code></pre></td>
</tr>
<tr>
<td>LED doesn't blink</td>
<td>Compile first, then click Run. Check pin assignment in the component property dialog.</td>
</tr>
<tr>
<td>New component not in picker</td>
<td><pre style={{margin:0}}><code>cd frontend{'\n'}npx tsx ../scripts/generate-component-metadata.ts</code></pre></td>
</tr>
</tbody>
</table>
<div className="docs-callout">
<strong>Full status:</strong>{' '}
See{' '}
<a href={`${GITHUB_URL}/blob/master/docs/SETUP_COMPLETE.md`} target="_blank" rel="noopener noreferrer">
docs/SETUP_COMPLETE.md
</a>{' '}
in the repository.
</div>
</div>
);
const SECTION_MAP: Record<SectionId, React.FC> = {
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 <head> 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 = <E extends Element>(selector: string, attr: string): [E | null, string] => {
const el = document.querySelector<E>(selector);
return [el, el?.getAttribute(attr) ?? ''];
};
const [descEl, origDesc] = captureAttr<HTMLMetaElement>('meta[name="description"]', 'content');
const [canonicalEl, origCanonical] = captureAttr<HTMLLinkElement>('link[rel="canonical"]', 'href');
const [ogTitleEl, origOgTitle] = captureAttr<HTMLMetaElement>('meta[property="og:title"]', 'content');
const [ogDescEl, origOgDesc] = captureAttr<HTMLMetaElement>('meta[property="og:description"]', 'content');
const [ogUrlEl, origOgUrl] = captureAttr<HTMLMetaElement>('meta[property="og:url"]', 'content');
const [twTitleEl, origTwTitle] = captureAttr<HTMLMetaElement>('meta[name="twitter:title"]', 'content');
const [twDescEl, origTwDesc] = captureAttr<HTMLMetaElement>('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<HTMLMetaElement>(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<HTMLLinkElement>('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 (
<div className="docs-page">
{/* Nav */}
<nav className="docs-nav">
<Link to="/" className="docs-nav-brand">
<IcoChip />
<span>Velxio</span>
</Link>
<span className="docs-nav-divider">/</span>
<span className="docs-nav-section">Docs</span>
<div className="docs-nav-links">
<Link to="/" className="docs-nav-link">Home</Link>
<Link to="/examples" className="docs-nav-link">Examples</Link>
<Link to="/editor" className="docs-nav-link">Editor</Link>
<a href={GITHUB_URL} target="_blank" rel="noopener noreferrer" className="docs-nav-link">
<IcoGitHub /> GitHub
</a>
{user ? (
<Link to={`/${user.username}`} className="docs-nav-btn">My Projects</Link>
) : (
<Link to="/login" className="docs-nav-btn">Sign in</Link>
)}
</div>
<button
className="docs-sidebar-toggle"
onClick={() => setSidebarOpen((v) => !v)}
aria-label="Toggle sidebar"
>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
<line x1="3" y1="6" x2="21" y2="6" />
<line x1="3" y1="12" x2="21" y2="12" />
<line x1="3" y1="18" x2="21" y2="18" />
</svg>
</button>
</nav>
<div className="docs-body">
{/* Sidebar */}
<aside className={`docs-sidebar${sidebarOpen ? ' docs-sidebar--open' : ''}`}>
<div className="docs-sidebar-title">Documentation</div>
<nav className="docs-sidebar-nav">
{NAV_ITEMS.map((item) => (
<Link
key={item.id}
to={`/docs/${item.id}`}
className={`docs-sidebar-item${activeSection === item.id ? ' docs-sidebar-item--active' : ''}`}
onClick={() => setSidebarOpen(false)}
>
{item.label}
</Link>
))}
</nav>
<div className="docs-sidebar-divider" />
<div className="docs-sidebar-title docs-sidebar-title--pages">Pages</div>
<nav className="docs-sidebar-nav">
<Link to="/" className="docs-sidebar-item docs-sidebar-link" onClick={() => setSidebarOpen(false)}>Home</Link>
<Link to="/editor" className="docs-sidebar-item docs-sidebar-link" onClick={() => setSidebarOpen(false)}>Editor</Link>
<Link to="/examples" className="docs-sidebar-item docs-sidebar-link" onClick={() => setSidebarOpen(false)}>Examples</Link>
</nav>
<div className="docs-sidebar-footer">
<a href={GITHUB_URL} target="_blank" rel="noopener noreferrer" className="docs-sidebar-gh">
<IcoGitHub /> View on GitHub
</a>
</div>
</aside>
{/* Main content */}
<main className="docs-main">
<ActiveContent />
{/* Prev / Next navigation */}
<div className="docs-pagination">
{activeIdx > 0 && (
<Link
to={`/docs/${NAV_ITEMS[activeIdx - 1].id}`}
className="docs-pagination-btn docs-pagination-btn--prev"
onClick={() => window.scrollTo(0, 0)}
>
{NAV_ITEMS[activeIdx - 1].label}
</Link>
)}
{activeIdx < NAV_ITEMS.length - 1 && (
<Link
to={`/docs/${NAV_ITEMS[activeIdx + 1].id}`}
className="docs-pagination-btn docs-pagination-btn--next"
onClick={() => window.scrollTo(0, 0)}
>
{NAV_ITEMS[activeIdx + 1].label}
</Link>
)}
</div>
</main>
</div>
</div>
);
};