diff --git a/docs/img5.png b/docs/img5.png new file mode 100644 index 0000000..5af5ac2 Binary files /dev/null and b/docs/img5.png differ diff --git a/frontend/public/components-metadata.json b/frontend/public/components-metadata.json index 605712c..ffdd945 100644 --- a/frontend/public/components-metadata.json +++ b/frontend/public/components-metadata.json @@ -1,6 +1,6 @@ { "version": "1.0.0", - "generatedAt": "2026-03-14T18:11:39.708Z", + "generatedAt": "2026-03-16T21:03:46.561Z", "components": [ { "id": "arduino-mega", diff --git a/frontend/src/components/simulator/SimulatorCanvas.tsx b/frontend/src/components/simulator/SimulatorCanvas.tsx index fa0ff72..2111b1b 100644 --- a/frontend/src/components/simulator/SimulatorCanvas.tsx +++ b/frontend/src/components/simulator/SimulatorCanvas.tsx @@ -900,7 +900,7 @@ export const SimulatorCanvas = () => { return () => timers.forEach(t => clearTimeout(t)); }, [components, recalculateAllWirePositions]); - // Auto-pan to keep the board visible after a project import/load. + // Auto-pan to keep the board and all components visible after a project import/load. // We track the previous component count and only re-center when the count // jumps (indicating the user loaded a new circuit, not just added one part). const prevComponentCountRef = useRef(-1); @@ -919,9 +919,21 @@ export const SimulatorCanvas = () => { if (!canvas) return; const rect = canvas.getBoundingClientRect(); const currentZoom = zoomRef.current; + + // Compute the centroid of all world-space elements (board + extra components) + // so that the auto-pan keeps everything visible, not just the board. + const allX = [boardPositionRef.current.x, ...componentsRef.current.map((c) => c.x)]; + const allY = [boardPositionRef.current.y, ...componentsRef.current.map((c) => c.y)]; + const minX = Math.min(...allX); + const maxX = Math.max(...allX); + const minY = Math.min(...allY); + const maxY = Math.max(...allY); + const centerX = (minX + maxX) / 2; + const centerY = (minY + maxY) / 2; + const newPan = { - x: rect.width / 4 - boardPosition.x * currentZoom, - y: rect.height / 4 - boardPosition.y * currentZoom, + x: rect.width / 2 - centerX * currentZoom, + y: rect.height / 2 - centerY * currentZoom, }; panRef.current = newPan; setPan(newPan); diff --git a/frontend/src/data/examples.ts b/frontend/src/data/examples.ts index fd2bf2f..0c205c2 100644 --- a/frontend/src/data/examples.ts +++ b/frontend/src/data/examples.ts @@ -652,8 +652,8 @@ void loop() { Adafruit_ILI9341 tft(TFT_CS, TFT_DC, TFT_RST); -// Background color: dark blue -#define BG_COLOR 0x0006 +// Background color: blue (visible on dark simulator canvas) +#define BG_COLOR 0x001F // Ball state int ballX = 120, ballY = 200; @@ -724,8 +724,8 @@ void loop() { { type: 'wokwi-ili9341', id: 'tft1', - x: 480, - y: 60, + x: 300, + y: 30, properties: {}, }, ], diff --git a/frontend/src/pages/DocsPage.tsx b/frontend/src/pages/DocsPage.tsx index 85b1709..2b646fb 100644 --- a/frontend/src/pages/DocsPage.tsx +++ b/frontend/src/pages/DocsPage.tsx @@ -72,55 +72,55 @@ const NAV_ITEMS: NavItem[] = [ interface SectionMeta { title: string; description: string; } const SECTION_META: Record = { 'intro': { - title: 'Introduction — Velxio Documentation', + 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', + 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', + title: 'Emulator Architecture | Velxio Documentation', description: 'How Velxio emulates AVR8 (ATmega328p), RP2040, and RISC-V (ESP32-C3) CPUs. Covers execution loops, peripherals, and pin mapping for all supported boards.', }, 'riscv-emulation': { - title: 'RISC-V Emulation (ESP32-C3) — Velxio Documentation', + title: 'RISC-V Emulation (ESP32-C3) | Velxio Documentation', description: 'Browser-side RV32IMC emulator for ESP32-C3, XIAO ESP32-C3, and C3 SuperMini. Covers memory map, GPIO, UART0, the ESP32 image parser, RV32IMC ISA, and test suite.', }, 'esp32-emulation': { - title: 'ESP32 Emulation (Xtensa) — Velxio Documentation', + title: 'ESP32 Emulation (Xtensa) | Velxio Documentation', description: 'QEMU-based emulation for ESP32 and ESP32-S3 (Xtensa LX6/LX7). Covers the lcgamboa fork, libqemu-xtensa, GPIO, WiFi, I2C, SPI, RMT/NeoPixel, and LEDC/PWM.', }, 'components': { - title: 'Components Reference — Velxio Documentation', + 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', + 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', + 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', + 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', + 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', + 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.', }, 'rp2040-emulation': { - title: 'RP2040 Emulation (Raspberry Pi Pico) — Velxio Documentation', + title: 'RP2040 Emulation (Raspberry Pi Pico) | Velxio Documentation', description: 'How Velxio emulates the Raspberry Pi Pico and Pico W using rp2040js: ARM Cortex-M0+ at 133 MHz, GPIO, UART, ADC, I2C, SPI, PWM and WFI optimization.', }, 'raspberry-pi3-emulation': { - title: 'Raspberry Pi 3 Emulation (QEMU) — Velxio Documentation', + title: 'Raspberry Pi 3 Emulation (QEMU) | Velxio Documentation', description: 'How Velxio emulates a full Raspberry Pi 3B using QEMU raspi3b: real Raspberry Pi OS, Python + RPi.GPIO shim, dual-channel UART, VFS, and multi-board serial bridge.', }, }; @@ -133,16 +133,16 @@ const IntroSection: React.FC = () => (

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 + true AVR8 / RP2040 CPU emulation, with 48+ interactive electronic components, all without installing any software on your machine.

Why Velxio?

Supported Boards

@@ -163,7 +163,7 @@ const IntroSection: React.FC = () => (
Live Demo:{' '} velxio.dev - {' '}— no installation needed, open the editor and start simulating immediately. + {' '}, no installation needed, open the editor and start simulating immediately.
); @@ -176,7 +176,7 @@ const GettingStartedSection: React.FC = () => (

Option 1: Use the Hosted Version

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

@@ -235,8 +235,8 @@ void loop() { delay(500); }`}
    -
  1. Click Compile — the backend calls arduino-cli and returns a .hex file.
  2. -
  3. Click Run — the AVR8 emulator executes the compiled program.
  4. +
  5. Click Compile: the backend calls arduino-cli and returns a .hex file.
  6. +
  7. Click Run: the AVR8 emulator executes the compiled program.
  8. Add components using the component picker (click the + button on the canvas).
  9. Connect wires by clicking a component pin and then another pin.
@@ -317,7 +317,7 @@ cpu.tick(); // advance peripheral timers and counters`} 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 + 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 @@ -392,7 +392,7 @@ const ComponentsSection: React.FC = () => (

Connecting Components

    -
  1. Click a pin on any component — a wire starts from that pin.
  2. +
  3. Click a pin on any component, a wire starts from that pin.
  4. Click a destination pin to complete the connection.
  5. Wires are color-coded by signal type:
@@ -488,7 +488,7 @@ const RoadmapSection: React.FC = () => (

✅ Implemented

diff --git a/frontend/src/pages/LandingPage.css b/frontend/src/pages/LandingPage.css index c24df59..48bd0df 100644 --- a/frontend/src/pages/LandingPage.css +++ b/frontend/src/pages/LandingPage.css @@ -238,19 +238,23 @@ /* ── Hero ─────────────────────────────────────────────── */ .landing-hero { display: grid; - grid-template-columns: 1fr 1.3fr; - align-items: center; - gap: 56px; - padding: 80px 80px 80px 80px; - max-width: 1400px; - margin: 0 auto; - min-height: 90vh; + grid-template-columns: 1fr 1.2fr; + align-items: stretch; + gap: 0; + min-height: calc(100vh - 52px); + width: 100%; + max-width: none; + margin: 0; + padding: 0; + overflow: hidden; } .hero-left { display: flex; flex-direction: column; + justify-content: center; gap: 0; + padding: 80px 60px 80px 80px; } .hero-eyebrow { @@ -370,11 +374,11 @@ display: none; } -/* Hero right — schematic */ +/* Hero right — image full-bleed */ .hero-right { - display: flex; - align-items: center; - justify-content: center; + position: relative; + overflow: hidden; + min-height: calc(100vh - 52px); } .schematic-svg { @@ -386,11 +390,12 @@ } .hero-preview-img { + position: absolute; + inset: 0; width: 100%; - max-width: 100%; - height: auto; - border-radius: var(--radius); - filter: drop-shadow(0 24px 64px rgba(0, 0, 0, 0.75)); + height: 100%; + object-fit: cover; + object-position: center; } /* ── Sections ─────────────────────────────────────────── */ @@ -495,12 +500,12 @@ /* Apple product tile */ .board-card-sm { background: #141416; - border-radius: 20px; - padding: 28px 14px 22px; + border-radius: 16px; + padding: 20px 14px 18px; display: flex; flex-direction: column; align-items: center; - gap: 14px; + gap: 10px; transition: transform 0.32s cubic-bezier(0.34, 1.46, 0.64, 1), box-shadow 0.32s ease; cursor: default; @@ -531,6 +536,32 @@ line-height: 1.4; } +/* Uniform image container — all cards same height */ +.board-img-box { + width: 100%; + height: 110px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.board-img-box img { + max-width: 90px; + max-height: 110px; + width: auto; + height: auto; + object-fit: contain; + filter: drop-shadow(0 4px 14px rgba(0, 0, 0, 0.5)); +} + +.board-img-box .board-svg { + max-width: 110px; + max-height: 100px; + width: auto; + height: auto; +} + .board-img-sm { width: 80px; height: auto; @@ -898,9 +929,11 @@ /* ── Responsive ───────────────────────────────────────── */ @media (max-width: 1024px) { .landing-hero { - grid-template-columns: 1fr 1.2fr; - gap: 40px; - padding: 72px 48px; + grid-template-columns: 1fr 1fr; + } + + .hero-left { + padding: 72px 40px 72px 48px; } .landing-section { @@ -925,13 +958,18 @@ @media (max-width: 900px) { .landing-hero { grid-template-columns: 1fr; - padding: 64px 32px; min-height: auto; - gap: 44px; + } + + .hero-left { + padding: 64px 32px; + order: 1; } .hero-right { order: -1; + min-height: 55vw; + max-height: 480px; } .schematic-svg { @@ -963,7 +1001,7 @@ display: none; } - .landing-hero { + .hero-left { padding: 52px 20px; } diff --git a/frontend/src/pages/LandingPage.tsx b/frontend/src/pages/LandingPage.tsx index d0f0183..32f4154 100644 --- a/frontend/src/pages/LandingPage.tsx +++ b/frontend/src/pages/LandingPage.tsx @@ -508,7 +508,6 @@ export const LandingPage: React.FC = () => { {/* Hero */}
-

Open Source · Free · Runs Locally

Emulate Hardware.
In your browser. @@ -526,14 +525,7 @@ export const LandingPage: React.FC = () => { View on GitHub

-

- ATmega328p - RP2040 - RV32IMC - Xtensa LX6/LX7 - ARM Cortex-A53 - 48+ components -

+
Velxio simulator preview @@ -556,30 +548,24 @@ export const LandingPage: React.FC = () => {
-
- -
+
Arduino Uno ATmega328p · 32 KB
-
- -
+
Arduino Nano ATmega328p · 32 KB
-
- -
+
Arduino Mega 2560 ATmega2560 · 256 KB
- +
ATtiny85 - ATtiny85 · 8 KB + AVR · 8 KB · DIP-8
@@ -592,12 +578,16 @@ export const LandingPage: React.FC = () => {
- Raspberry Pi Pico +
+ Raspberry Pi Pico +
Raspberry Pi Pico RP2040 · 264 KB RAM
- Raspberry Pi Pico W +
+ Raspberry Pi Pico W +
Raspberry Pi Pico W RP2040 + WiFi
@@ -612,22 +602,28 @@ export const LandingPage: React.FC = () => {
- ESP32-C3 +
+ ESP32-C3 +
ESP32-C3 DevKit RV32IMC · 4 MB flash
- XIAO ESP32-C3 +
+ XIAO ESP32-C3 +
XIAO ESP32-C3 RV32IMC · compact
- ESP32-C3 SuperMini +
+ ESP32-C3 SuperMini +
ESP32-C3 SuperMini RV32IMC · mini form
- +
CH32V003 (RISC-V) RV32EC · 48 MHz
@@ -642,34 +638,44 @@ export const LandingPage: React.FC = () => {
-
- +
+ ESP32 DevKit V1
ESP32 DevKit V1 LX6 · 4 MB flash
- ESP32 DevKit C V4 +
+ ESP32 DevKit C V4 +
ESP32 DevKit C V4 LX6 · 4 MB flash
- ESP32-CAM +
+ ESP32-CAM +
ESP32-CAM LX6 · camera module
- ESP32-S3 +
+ ESP32-S3 +
ESP32-S3 DevKit LX7 · 4 MB flash
- XIAO ESP32-S3 +
+ XIAO ESP32-S3 +
XIAO ESP32-S3 LX7 · compact
- Arduino Nano ESP32 +
+ Arduino Nano ESP32 +
Arduino Nano ESP32 LX7 · Nano form
@@ -684,8 +690,8 @@ export const LandingPage: React.FC = () => {
-
- Raspberry Pi 3 +
+ Raspberry Pi 3B
Raspberry Pi 3B Cortex-A53 · 1.2 GHz · Linux diff --git a/frontend/src/simulation/parts/ComplexParts.ts b/frontend/src/simulation/parts/ComplexParts.ts index ae54a9d..3c0b49b 100644 --- a/frontend/src/simulation/parts/ComplexParts.ts +++ b/frontend/src/simulation/parts/ComplexParts.ts @@ -586,6 +586,8 @@ const ili9341Simulation = { const pinManager = (avrSimulator as any).pinManager; const spi = (avrSimulator as any).spi; + console.log('[ILI9341] attachEvents called. spi=', !!spi, 'pinManager=', !!pinManager, 'cpu=', !!(avrSimulator as any).cpu); + if (!pinManager || !spi) { console.warn('[ILI9341] pinManager or SPI peripheral not available'); return () => {}; @@ -622,6 +624,7 @@ const ili9341Simulation = { let pendingFlush = false; let rafId: number | null = null; + let flushCount = 0; const scheduleFlush = () => { if (rafId !== null) return; rafId = requestAnimationFrame(() => { @@ -629,6 +632,10 @@ const ili9341Simulation = { if (pendingFlush && ctx && imageData) { ctx.putImageData(imageData, 0, 0); pendingFlush = false; + flushCount++; + if (flushCount === 1) console.log('[ILI9341] First canvas flush complete'); + } else if (pendingFlush) { + console.warn('[ILI9341] Flush skipped: ctx=', !!ctx, 'imageData=', !!imageData); } }); }; @@ -681,6 +688,7 @@ const ili9341Simulation = { }; // ── Command / data processing ───────────────────────────────────── + let ramwrPixelCount = 0; const processCommand = (cmd: number) => { currentCmd = cmd; dataBytes = []; @@ -688,11 +696,15 @@ const ili9341Simulation = { pixelByteCount = 0; if (cmd === 0x01) { // SWRESET – clear framebuffer + console.log('[ILI9341] SWRESET received, ctx=', !!ctx); colStart = 0; colEnd = SCREEN_W - 1; rowStart = 0; rowEnd = SCREEN_H - 1; curX = 0; curY = 0; imageData = null; if (ctx) ctx.clearRect(0, 0, SCREEN_W, SCREEN_H); + } else if (cmd === 0x2C) { // RAMWR + ramwrPixelCount = 0; + console.log('[ILI9341] RAMWR received, ctx=', !!ctx, 'col=', colStart, '-', colEnd, 'row=', rowStart, '-', rowEnd); } }; @@ -706,6 +718,10 @@ const ili9341Simulation = { writePixel(pixelHiByte, value); scheduleFlush(); pixelByteCount = 0; + ramwrPixelCount++; + if (ramwrPixelCount === 1) { + console.log('[ILI9341] First pixel written: rgb565=', ((pixelHiByte << 8) | value).toString(16)); + } } return; } @@ -727,7 +743,12 @@ const ili9341Simulation = { // ── Intercept SPI ───────────────────────────────────────────────── const prevOnByte = spi.onByte.bind(spi); + let spiByteCount = 0; spi.onByte = (value: number) => { + spiByteCount++; + if (spiByteCount === 1) { + console.log('[ILI9341] First SPI byte! value=0x' + value.toString(16) + ' dcState=' + dcState); + } if (!dcState) { processCommand(value); } else { @@ -736,7 +757,8 @@ const ili9341Simulation = { spi.completeTransfer(0xFF); // Unblock CPU immediately }; - console.log(`[ILI9341] SPI simulation ready. DC→pin${pinDC}`); + // Verify the override was applied (same object ref check) + console.log(`[ILI9341] SPI simulation ready. DC→pin${pinDC}. spi.onByte overridden=${spi.onByte !== prevOnByte}`); // ── Cleanup ─────────────────────────────────────────────────────── return () => { diff --git a/frontend/src/store/useSimulatorStore.ts b/frontend/src/store/useSimulatorStore.ts index d780647..57bef45 100644 --- a/frontend/src/store/useSimulatorStore.ts +++ b/frontend/src/store/useSimulatorStore.ts @@ -497,10 +497,10 @@ export const useSimulatorStore = create((set, get) => { set((s) => { const boards = s.boards.map((b) => - b.id === boardId ? { ...b, running: true, serialMonitorOpen: true } : b + b.id === boardId ? { ...b, running: true } : b ); const isActive = s.activeBoardId === boardId; - return { boards, ...(isActive ? { running: true, serialMonitorOpen: true } : {}) }; + return { boards, ...(isActive ? { running: true } : {}) }; }); },