Commit Graph

287 Commits

Author SHA1 Message Date
David Montero Crespo 70cc8c2e11 feat(editor): view-mode toggle, agent-chat slot, toolbar polish
Changes that ship to OSS — all benign for self-hosters, but most are
extension points the velxio-prod overlay (and any private fork) needs to
plug an in-editor AI chat into the page.

Editor:
- 3-way view-mode toggle (code / both / circuit) in the unified toolbar.
  Lets users hide a pane to give a right-docked sidebar (e.g. the AI
  chat overlay) more breathing room. Persisted in useEditorStore.
- Default file explorer narrower (210 → 165 px); min 110.
- Removed the redundant `tb-board-pill` (icon + "Editing: X" tooltip);
  the BoardSelector dropdown elsewhere already shows the active board.
- Inlined Import/Export/Upload-firmware buttons; the 3-dot overflow
  menu gave up too much discoverability. Removed dead overflow state.

Simulator:
- Fix: global Delete/Backspace handler in SimulatorCanvas no longer
  fires when the event target is an INPUT/TEXTAREA/SELECT/contentEditable
  — affected any in-page text field, not just the chat overlay.

Overlay extensibility:
- New `data-velxio-slot="agent-chat"` at the bottom of EditorPage so
  pro overlays can portal a chat panel into the editor without
  forking the page.
- vite.config.ts: preserveSymlinks=true when VITE_PRO_BUILD is set.
  Lets local-dev junctions (overlay tree → frontend/src/pro) resolve
  bare imports back to the OSS node_modules without resolving symlinks.

Deps:
- Added react-markdown + remark-gfm (rendered chat output) and
  @google/genai + zod (overlay agent loop). Tree-shaken from the OSS
  bundle when no pro code imports them.

gitignore:
- Ignore backend/app/pro/ and frontend/src/pro/ junctions used by
  developers running a private overlay against the OSS dev server.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 00:56:51 -03:00
David Montero Crespo 9d36eac816
Merge pull request #143 from davidmonterocrespo24/examples-real-thumbnails
Examples real thumbnails
2026-05-07 17:56:44 -03:00
davidmonterocrespo24 37fbc502d1 chore(examples): drop PNG thumbnails, ship WebP only (-80% asset size)
The PNG fallbacks were carrying 80% of the gallery's bundled weight
(16 MB of 20 MB) and serving virtually no traffic — WebP is supported
on ~97% of in-use browsers, and the few hold-outs (very old Safari)
fall through to the CircuitPreview SVG mock via the existing onError
handler. No visual regression for modern browsers.

Numbers:
- before: 258 files, ~20 MB total
- after:  129 files, ~3.6 MB total (avg 28.6 KB / WebP)

ExampleThumbnail simplified: drops the <picture>/<source> wrap around
the WebP <source> + PNG fallback, just renders the WebP <img> directly
with onError → CircuitPreview.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 22:54:33 +02:00
davidmonterocrespo24 d53f4f1380 feat(examples): real canvas screenshots as gallery thumbnails
Replaces the manually-positioned wokwi-element mock (CircuitPreview) in
the /examples gallery with real screenshots of the actual simulator
canvas, so each card shows the example's boards + components + wires
exactly as they appear when you open the example.

How it works
- A new <ExampleThumbnail> component tries
  /examples-thumbs/<id>.{webp,png} first. If the image 404s — or no
  thumbnail has been captured yet — it falls back to the existing
  CircuitPreview component. No-op for examples without a screenshot.
- ExamplesGallery and ExampleDetailPage now render <ExampleThumbnail>
  instead of CircuitPreview directly.
- Explicit example.thumbnail field still wins (kept the existing
  override path in case someone wants a custom asset).

Capture pipeline
- Generated by velxio-prod's scripts/capture-example-thumbs.mjs
  (Playwright + sharp). For each example: opens /examples/<slug>,
  clicks "Open in Simulator", waits for [data-board-id] elements,
  computes the bbox of all boards + components, sets a transform on
  .canvas-world to center and fit them with 12% padding inside the
  canvas viewport, screenshots .canvas-content, and re-encodes to
  600x360 @2x DPI as .png + .webp.

This commit ships the first batch (102 of ~129 examples — the rest
will follow once the capture completes; missing slugs gracefully
fall back to CircuitPreview in the meantime).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 22:44:10 +02:00
davidmonterocrespo24 9d5d2e8a4a fix(docs): RISC-V emulation goes through QEMU, not a TypeScript browser core
The marketing copy and docs claimed ESP32-C3 / XIAO-C3 / SuperMini /
CH32V003 ran on a "browser-native RV32IMC core written in TypeScript",
but production runs through QEMU lcgamboa (libqemu-riscv32) with the
esp32c3-picsimlab machine — same backend pattern as Xtensa ESP32, just
a different libqemu binary. The TypeScript ISA layer
(RiscVCore.ts / Esp32C3Simulator.ts / RiscVSimulator.ts) is kept only
as Vitest unit-test infrastructure for RV32IMC instruction decoding;
it cannot handle the 150+ ROM functions ESP-IDF needs at boot and is
not wired into the production emulation path.

Files updated:

Marketing pages
- LandingPage: board-group label, FAQ answer, architecture description
  no longer claim "browser-native" or "no backend needed" for RISC-V.
- AboutPage: arch card retitled "RISC-V via QEMU", body explains the
  libqemu-riscv32 / lcgamboa backend.
- Velxio2Page: arch group engine label, multi-board feature item,
  competitive-comparison card all corrected.
- ArduinoEmulatorPage: two RISC-V cards corrected.
- ESP32SimulatorPage: ESP32-C3 cross-link card corrected.
- ESP32C3SimulatorPage: hero subtitle, trust strip, supported-boards
  intro, JSON-LD description corrected.
- ElectronicsSimulatorPage: install-needed FAQ corrected.
- examples.ts: c3-blink description and code-comment corrected.

SEO surfaces
- index.html: JSON-LD SoftwareApplication description, OS-fallback FAQ
  body, supported-boards <ul> bullets, feature list bullets corrected.
- seoRoutes.ts: /esp32-c3-simulator title + description corrected;
  homepage description corrected.

Docs page
- DocsPage RiscVEmulationSection: intro paragraph rewritten — RISC-V
  goes through QEMU lcgamboa with libqemu-riscv32 / esp32c3-picsimlab,
  TypeScript layer is Vitest-only.
- DocsPage Esp32EmulationSection callout: section now applies to all
  ESP32 family (Xtensa + RISC-V), pointer to RISC-V doc clarified.

README
- "Boards" table: production-engine column for ESP32-C3 family and
  CH32V003 changed from "RiscVCore.ts (browser)" to "QEMU lcgamboa
  (backend)".
- "ESP32-C3 / XIAO-C3 / SuperMini / CH32V003" subsection retitled
  "(RISC-V via QEMU)" — body explains libqemu-riscv32 backend and
  flags the TypeScript layer as Vitest-only.

The two remaining "browser-native" hits in the codebase
(Velxio25Page:176, index.html:348) are about ngspice-WASM SPICE
analog simulation, which genuinely is browser-native — left alone.

Build verified: npm run build:docker succeeds, 246 SEO pages prerender.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 21:56:29 +02:00
davidmonterocrespo24 0c8d96a05c feat(marketing): replace mock hero with live editor screenshot
Captures /examples/traffic-light → /editor in headless Chromium and saves
the rendered editor as a 3840x2160 (2x DPI) PNG + WebP for the landing
page hero.

The shot includes the code editor on the left (Traffic Light Simulator
.ino), the Arduino Uno on the canvas with three LEDs wired up, the SPICE
nets indicator, and the full chrome — a much stronger first impression
than the previous CSS-mocked schematic.

Generation script lives in the private velxio-prod repo
(scripts/capture-hero.mjs) and can be re-run any time to refresh the
asset against the live deployment.

- /marketing/hero-editor.png (320 KB)
- /marketing/hero-editor.webp (160 KB)
- LandingPage hero <picture> now points at these (loading=eager,
  fetchPriority=high since it's above the fold).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 21:21:52 +02:00
davidmonterocrespo24 e0dbde0a76 feat(design): tokens, webfonts, Lucide icons, board PNG/WebP picture strip
Foundation
- Add 7 token CSS files in src/tokens/ — semantic colors, 4-pt spacing,
  Apple-HIG type ramp, radius/elevation/motion/z-index scales.
- Refactor src/index.css to import tokens and remap legacy aliases
  (--accent, --bg, etc.) onto the new --color-* semantics so existing
  components keep rendering during migration.
- Drop the duplicate font-family from src/App.css; body inherits from :root.
- Global *:focus-visible ring backed by --color-focus-ring (WCAG 2.4.7).

Webfonts (self-hosted)
- Add Inter.var.woff2 (variable, OFL) and JetBrainsMono.var.woff2 to
  public/fonts/. Preloaded in index.html with crossorigin.
- Old stack -apple-system kept as fallback so Mac users still get SF Pro.
- Fixes cross-OS rendering inconsistency (Win/Linux/Android were falling
  back to Segoe UI / Roboto, breaking the type grid).

Component primitives
- New src/components/ui/{Button,Card,Input}.tsx + .css. Built on the
  semantic tokens, ready for incremental migration of .ap-* CSS classes.

Lucide icons
- Replace 6 inline SVG icon components in LandingPage (IcoChip / IcoCpu /
  IcoCode / IcoZap / IcoLayers / IcoMonitor) with lucide-react imports.
  Aliased so call sites are unchanged. ~80 lines of inline SVG removed.
- IcoGitHub kept bespoke (filled glyph, brand-correct).

Marketing assets
- Convert top 8 boards to transparent PNG + WebP at 1x / 2x:
  Arduino Uno, Nano, Mega 2560, Pi Pico, Pi Pico W, ESP32-C3,
  ESP32-DevKit-V1, XIAO ESP32-S3.
- Migrate matching cards in LandingPage and Velxio2Page to <picture>
  with WebP > PNG > SVG fallback. Other 8 boards keep <img src=*.svg>
  for now (Raspberry Pi 3B, ESP32-CAM, etc.).
- Refresh og-image.png — same canonical URL, new content (4 hero boards
  + branding instead of generic logo card).
- Fix latent bug in LandingPage: ESP32 DevKit V1 card was loading
  esp32-devkit-c-v4.svg; now uses esp32-devkit-v1.{webp,png,svg}.

Build verified: npm run build:docker succeeds, 246 SEO pages prerender.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 20:45:44 +02:00
David Montero Crespo 694a2f4073 fix(frontend): polyfill crypto.randomUUID for non-secure contexts
crypto.randomUUID() is only exposed on secure contexts (HTTPS, localhost,
127.0.0.1, ::1). When Velxio is self-hosted and accessed via a LAN IP over
plain HTTP (e.g. http://192.168.31.139:3080/), crypto.randomUUID is
undefined and any code path that calls it throws TypeError.

This silently broke ESP32 simulation start for self-hosters: the frontend
generates a UUID for the WS client_id when Run is clicked; the throw
rejected the promise before reaching the WS connect, so the backend
never got the start request — no worker spawned, logs empty, simulation
"didn't start" with no visible error.

Same root cause would also break the multi-file editor (createFile,
createFileGroup) on the same LAN-HTTP self-host setup, just less
observably.

Add a single generateUUID() helper that:
  1. Uses crypto.randomUUID() when available (secure context fast path).
  2. Falls back to crypto.getRandomValues() — which IS available in
     non-secure contexts — to build a v4 UUID by hand.
  3. Final fallback to Math.random() if even that is missing
     (defensive — Web Crypto getRandomValues has been universal for
     years).

Replace all 6 crypto.randomUUID() call sites:
  - frontend/src/simulation/Esp32Bridge.ts (2 sites — getTabSessionId)
  - frontend/src/store/useEditorStore.ts   (4 sites — file IDs)

Reported by a self-hoster on OrangePi 5B accessing Velxio via LAN IP.
DevTools console showed:
  TypeError: crypto.randomUUID is not a function
    at Ph (...) at wh.connect (...) at startBoard (...)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 13:41:30 -03:00
David Montero Crespo cd3ee6172b copy: rewrite landing hero — drop SPICE jargon, lead with the boards
The previous hero ("Circuits + Code. / One Browser Tab. / SPICE-accurate.")
was optimised for EE engineers searching for circuit simulators. Most
visitors land here looking for an Arduino emulator they can use without
installing anything — the names of the supported boards are a stronger
hook than analog-simulation accuracy.

Restored the older "Arduino, ESP32 & Raspberry Pi. / Right in your
browser." framing and tightened the subtitle to action verbs (Write,
wire, run) plus concrete numbers (19 boards, 48+ parts). Drops:
- "SPICE-accurate" — kept on the dedicated /arduino-emulator,
  /circuit-simulator etc. SEO landing pages where the audience is
  actively looking for it
- "ngspice", "co-simulated", "custom chips in C or Rust" — niche, fit
  better in the features section below
2026-05-05 11:29:45 -03:00
David Montero Crespo 26e0c2be60 feat(components): add mergeComponents API to ComponentRegistry
Forgotten in the prior commit (case-mismatch on Windows tracked the wrong
filename). Adds the public method overlays use to splice extra components
into the picker after default-metadata load. Components with an existing
id are replaced; new ones are appended.
2026-05-05 10:34:43 -03:00
David Montero Crespo 8b14815094 feat(components): pro_only flag + registry merge API + picker gate hook
Three small additions so private overlays can add components gated behind
a paid subscription without forking the picker:

- types/component-metadata.ts: optional pro_only?: boolean field on
  ComponentMetadata. Self-hosters never set it; picker behaves identically.
- services/componentRegistry.ts: new mergeComponents() public method.
  Pro overlay calls this after the default registry has loaded to splice
  in extra components (replacing any with the same id).
- components/ComponentPickerModal.tsx: when a pro_only component is
  clicked, the picker first calls window.__velxio_pro_gate__(component)
  if defined. If the gate returns true, the click is consumed (overlay
  shows an upgrade modal). If absent or returns false, the click passes
  through to onSelectComponent as normal.

Net upstream change: ~25 lines, all backwards-compatible. OSS image
behaves exactly as before since no overlay sets pro_only or installs
the gate.
2026-05-05 10:33:53 -03:00
David Montero Crespo 77b3e86b50 feat(frontend): /pricing route placeholder + UserResponse subscription fields
Two upstream additions to support private overlays implementing paid tiers
without forking client code:

- store/useAuthStore.ts: UserResponse extended with optional
  is_paid_subscriber, subscription_status, subscription_period_end. The
  backend now returns these in /api/auth/me; the persist middleware
  serialises them automatically.
- pages/PricingPlaceholder.tsx (NEW): the /pricing route. Renders a polite
  "this image is fully free" message for self-hosters plus a
  data-velxio-slot="pricing-page" target where private overlays can
  portal-inject a real pricing page.
- App.tsx: register the /pricing route after /about.

Self-hosted OSS image: /pricing shows the placeholder, no behavioural
change anywhere else. Production with a private overlay: /pricing shows
the overlay's full pricing UI.

Frontend build verified.
2026-05-05 10:24:01 -03:00
David Montero Crespo a5b5e57aae feat: add data-velxio-slot markers for overlay portal targets
Three small markers (each one HTML attribute) so private overlays can
portal-inject UI into well-defined places without forking the upstream
component:

- AppHeader user dropdown: data-velxio-slot="user-menu"
  Lets overlays add menu items between "My projects" and "Sign out"
  (e.g. a Privacy / opt-out item).
- AdminPage tab bar: data-velxio-slot="admin-tabs"
  Lets overlays add extra tabs alongside Dashboard / Users / Projects /
  Boards (e.g. a Pro Analytics tab).
- AdminPage tab content area: data-velxio-slot="admin-tab-content"
  Sibling div where overlay tab content can portal-render.

Generic markers, no overlay-specific code in upstream. Anyone with
private extensions can use them. The OSS build is otherwise unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 23:28:02 -03:00
David Montero Crespo edd2ac32d5 feat: add optional extension hooks for private overlays
Three small, backwards-compatible hooks let anyone with private features
(velxio.dev's analytics, custom integrations, paid tiers, …) layer them
on top of the open-source build without forking files.

Backend (app/main.py):
- After standard router registration, try-import an optional `app.pro`
  module exposing `register_pro(app)`. ImportError is silently swallowed
  (the OSS image doesn't ship `app.pro`, so this is a no-op there).

Frontend:
- EditorToolbar: new optional `rightSlot` prop renders extra elements
  after the built-in right-group buttons (mirrors the existing
  `centerSlot` pattern).
- main.tsx: dynamic `import('@pro/index')` gated by VITE_PRO_BUILD env.
  When unset (OSS build), the branch is dead-code-eliminated and no pro
  chunk is emitted.
- vite.config.ts: `@pro` alias resolves to `src/__pro_stub__/` by default.
  Private builds set `VITE_PRO_BUILD=true` and `PRO_OVERLAY_PATH=<path>`
  to point at their real overlay tree.
- src/__pro_stub__/index.ts: 1-line no-op `mountPro` so TypeScript and
  Vite resolvers stay happy in OSS builds.

Verified: `npm run build:docker` succeeds; `npm test` passes 1161/1162;
the OSS bundle (43 MB) contains zero references to `__pro_stub__`,
`@pro`, or `pro/index` (verified via `grep dist/`).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 14:03:20 -03:00
David Montero 71e90f9b90 fix(compile): bump frontend axios timeout 180s → 600s for ESP-IDF builds
Cold ESP-IDF builds (esp32, esp32-c3, esp32-cam) routinely take 5-10
minutes the first time a project is compiled. The 180s axios timeout
on POST /api/compile/ was cutting the connection long before the
backend finished, surfacing as the misleading 'No response from
server. Is the backend running on port 8001?' error.

Bumping the client timeout to 600s aligns with the nginx
proxy_read_timeout (also 600s) so the chain end-to-end is consistent.

Arduino sketches still compile in seconds — the timeout is an upper
bound, not a delay.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 15:32:24 +02:00
David Montero 78a834700b fix(examples): use esp32-devkit-c-v4 for ePaper examples (GPIO 16/17)
The 4 ESP32 ePaper examples (BW weather, BWR alert, UC8179 dashboard,
ACeP rainbow) wired GxEPD2 to GPIO 16 (RST) and GPIO 17 (DC), which is
the canonical pinout shown in every GxEPD2 example. But those pins are
not broken out on the DevKit V1 variant (PINS_ESP32) — they only exist
on DevKit-C-V4 (PINS_ESP32_DEVKIT_C_V4).

Result: the RST and DC wires fell back to (0,0) and rendered as a red
+ purple line shooting from the corner of the board. CLAUDE.md §6a
documents this exact symptom.

Switching boardType to 'esp32-devkit-c-v4' renders the variant whose
pinInfo includes 16 and 17. Also rename pinName 'GND' → 'GND.1' since
DevKit-C-V4 exposes three GND pins as GND.1/2/3 (not a plain 'GND').

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 06:37:20 +02:00
David Montero Crespo 531c337d19 fix(install): unblock self-hosting + drop forced wokwi clones
Resolves several install pain points reported by users (#108, #120) and
removes the obligatory upstream-clone step that confused contributors and
slowed down every Docker build.

Install fixes:
- nginx: server_name → catch-all default_server, drop Debian's stock site
  so reverse-proxied users no longer get the "Welcome to nginx" page.
- entrypoint: auto-generate SECRET_KEY at first boot, persisted under
  data/.secret_key. backend/.env is now optional in docker-compose.yml.
- backend: add greenlet>=3.0.0 (SQLAlchemy async dep that was missing on
  some Python builds — caused uvicorn startup failures on WSL).

Wokwi libs come from npm:
- @wokwi/elements 1.9.2, avr8js 0.21.0, rp2040js 1.3.2 are pinned in
  frontend/package.json. Vite aliases removed.
- Dockerfile.standalone no longer clones avr8js / rp2040js / wokwi-elements
  / wokwi-boards. Frontend stage is just COPY + npm install + build:docker.
- Board SVGs vendored under frontend/public/boards/ (10 deduped against
  existing files, 2 truly new). third-party/wokwi-* clones become reference-
  only credits — generate-component-metadata.ts skips gracefully when absent.

Production config split out:
- docker-compose.prod.yml, deploy/nginx.prod.conf, nginx-host-velxio*.conf,
  update-third-party.bat removed. Production deployment lives in its own
  repo: https://github.com/velxio/velxio-prod (host nginx + HTTPS + backups
  + pinned upstream commit).

Verified locally: 1161 frontend tests pass, build:docker completes clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 00:04:11 -03:00
David Montero 888cb5b92b fix(autosave): only project owner triggers auto-save
Without an ownership check, viewing someone else's project (admin
inspection, browsing public projects) caused the auto-save hook to
PUT the project on every store change. The backend correctly rejects
non-owner updates with 403, but the frontend surfaced these as
"save fail" to the user — misleading and noisy in logs.

The hook now stays idle unless the authenticated user matches
currentProject.ownerUsername. Manual saves through SaveProjectModal
are unaffected.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 21:41:32 +02:00
David Montero Crespo 9cd5061732 refactor: rename wokwi-libs/ → third-party/
The directory grew well beyond Wokwi-only contents: it now hosts
lcgamboa's QEMU fork (qemu-lcgamboa), Espressif's esp32-camera, the
ngspice WASM build, fritzing-parts, picowi, an alternative QEMU
(qemu-esp32), the 100_Days_100_IoT_Projects examples repo, and
Wokwi's own avr8js/rp2040js/wokwi-elements/wokwi-features/wokwi-boards.
"wokwi-libs" was misleading — half the contents have nothing to do
with Wokwi. "third-party/" is the standard convention for vendored
external dependencies.

Mechanical changes:

  Path rename:
    wokwi-libs/ → third-party/
    update-wokwi-libs.bat → update-third-party.bat
    docs/WOKWI_LIBS.md → docs/THIRD_PARTY.md

  Submodule reconfiguration:
    .gitmodules — 4 path= and section names updated
    .git/modules/wokwi-libs/ → .git/modules/third-party/
    each submodule's .git file rewired to ../../.git/modules/third-party/<name>

  Reference updates (~80 files): vite.config.ts aliases, Dockerfile
    COPY paths, GH Actions workflow steps, build_qemu_*.sh, all
    docs/* and test/*/autosearch/* entries that mention the path,
    package-lock.json file: dependencies, .gitignore patterns,
    sitemap.xml + index.html SEO blurbs, scripts/generate-component-*,
    .dockerignore, .idea/vcs.xml. Bulk replaced both `wokwi-libs/`
    (path) and bare `wokwi-libs` (textual mentions in docs/comments).

Verified:
  - npx tsc -b --noEmit produces no new errors related to these paths
  - vite.config.ts aliases now point at ../third-party/avr8js etc.
  - All 4 git submodules (avr8js, rp2040js, wokwi-elements,
    wokwi-features) are linked under third-party/ with their
    worktrees re-populated and config files referencing the new path
  - `grep -r wokwi-libs` returns zero hits outside node_modules,
    .vite, frontend/dist, third-party/ (upstream submodule contents),
    *.pyc caches, and *.dll.pre-camera rollback binaries

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 00:58:57 -03:00
David Montero Crespo 36914e209c feat(webcam): universal compatibility — any webcam on any PC
User goal: ESP32-CAM live preview that works with any webcam,
regardless of resolution, brand, or scene complexity. The previous
fixed-quality 0.28 was fragile (intermittent decode errors on
moving/textured scenes) and capped visual quality unnecessarily.

Two-layer fix; either alone is insufficient:

LAYER A — Bounded JPEG encoder (frontend, this repo)
  frontend/src/hooks/useWebcamFrames.ts:
    encodeBoundedJpeg() walks a quality ladder [0.6, 0.5, ..., 0.1]
    until the JPEG fits in MAX_FRAME_BYTES (23 000). If even q=0.1
    overshoots — extreme HD/4K scenes — falls back to a 240×180
    downscaled canvas at q=0.4. Guarantees every emitted frame fits
    the deliverable budget regardless of webcam hardware.

    The hook now exposes lastQualityUsed + lastDownscaled so UI can
    surface when auto-tuning kicks in.

  frontend/src/components/simulator/CameraToggle.tsx:
    Tooltip shows "(auto-tuned to q=0.X)" or "(auto-downscaled, q=0.X)"
    while streaming so users see what the encoder picked.

LAYER B — Multi-lap descriptor ring walker (qemu-lcgamboa, submodule)
  Bumps the QEMU per-frame deliverable cap from 8 KiB to ~32 KiB by
  letting the walker reset the descriptor ring up to 4 times per
  VSYNC. Submodule pointer bumped to eb8b7a5d.

Combined, the demo now supports:
  - Cheap 480p webcams: q=0.6, 5-10 KiB JPEGs, sharp
  - Logitech mid-range:  q=0.5-0.6, 8-15 KiB JPEGs, sharp
  - HD 1080p webcams:    q=0.4-0.6, 15-23 KiB JPEGs, sharp
  - 4K complex scenes:   downscaled, still readable

Documented as bug closure in:
  test/test-esp32-cam/autosearch/15_universal_webcam_compat.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 00:48:24 -03:00
David Montero Crespo 2c08e84fc6 fix(useWebcamFrames): JPEG quality 0.35 → 0.28 — intermittent decode fails
User reported "JPG Decompression Failed! Data format error" hitting
intermittently with quality 0.35. Worker log showed actual JPEG
payloads at 7959-8123 bytes per frame — right at the QEMU emulator's
8192-byte deliverable budget (8 EOFs × 1024 bytes from cam_hal's
default 16-descriptor ring).

The webcam JPEG encoder produces variable-size output: simple uniform
scenes compress to ~6 KiB, complex/textured/moving frames bloat to
~9-10 KiB. Anything over 8192 gets truncated mid-Huffman-scan in the
firmware framebuffer, my walker injects FF D9 at byte 8190 to keep
cam_verify_jpeg_eoi happy, but the upstream jpg2rgb565 actually
parses the structure and chokes on the truncated stream.

Quality 0.28 keeps even the worst-case complex frame comfortably
under 8 KiB. Visual quality is still much better than the 0.25
fallback — fine for a 160×120 preview where the user cares about
"is my face there" not "did the JPEG quantization tables converge".

Real long-term fix would be to bump EOFS_PER_FRAME and lift the 8 KiB
ceiling — but that touches the QEMU walker (DLL rebuild cycle) and
risks breaking the descriptor-ring math. Doing this frontend tweak
first to unblock the demo.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 00:32:46 -03:00
David Montero Crespo d4d015c25d perf(spi): batch SPI bytes per WS message — ~50× faster TFT in emulator
User reported the ESP32-CAM + ILI9341 live preview at ~1 frame/min.
Profile: 80×60 preview pushes 9600 SPI bytes per drawRGBBitmap, and
each byte was emitting a full {type:'spi_event'} JSON message over
the worker→backend→WS→frontend pipeline. Per-byte overhead ~150-200µs
in Python (json.dumps + sys.stdout.write+flush dominates) plus
asyncio + WS dispatch. Net: 1.5-2 sec/frame minimum, much worse with
GIL contention.

Fix: buffer MOSI bytes in the worker and emit a single base64-encoded
`spi_batch` message when CS goes HIGH (transaction ended) or the
buffer crosses 4 KiB. ~9600 events/frame collapse to ~3 messages.

  backend/app/services/esp32_worker.py:_on_spi_event
    - Add _spi_byte_buf bytearray + threading.Lock
    - On op==0x00 (byte): append; flush early if buf >= 4096
    - On op==0x01 (CS change): flush buffer, then emit the CS event
      via the legacy spi_event channel (ePaper / custom chips that
      observe CS still get it).

  frontend/src/simulation/Esp32Bridge.ts
    - New 'spi_batch' message handler decodes b64 and replays each
      byte through the existing onSpiByte callback. Parts that
      subscribed via simulator.spi.onByte don't notice the protocol
      change. The 'spi_event' branch still handles CS changes plus
      legacy single-byte payloads for backwards compat.

Now that 38 KB/frame is cheap, restore preview to 160×120 + JPEG
quality 0.35 in the gallery example. Real measured speedup: ~50× on
the QVGA preview demo. Real hardware was never affected — it runs
SPI at 80 MHz and pushes the bitmap in ~4 ms either way.

PSRAM emulation is unrelated to this bottleneck and was left untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 00:25:06 -03:00
David Montero Crespo a20b10a252 perf(esp32-cam-lcd-preview): 4x faster preview by shrinking SPI traffic
User reported the live preview "looks slow" after the JPEG decode fix.
Diagnosis: each tft.drawRGBBitmap pushes width × height × 2 bytes over
SPI, and every byte takes a full QEMU → worker → backend (WS) → frontend
round-trip. At 160×120 that's 38 400 messages per frame; the bus
saturates at ~0.2 fps perceived.

Two changes shrink the per-frame SPI bandwidth:

1. Preview 160×120 → 80×60 (and JPG_SCALE_2X → JPG_SCALE_4X).
   38 400 bytes/frame → 9 600 bytes/frame. Already 4× faster.

2. Status bar redraw throttled to every 10th frame instead of every
   frame. The text writes (printf, fillRect, fillCircle) account for
   another ~1-2 KB of SPI traffic per loop iteration. Skipping 9 of
   every 10 redraws frees up a chunk more bandwidth without losing
   the headline numbers (fps, frame counter) — they just refresh
   once a second instead of 5x/sec.

Also dropped the trailing `delay(20)` — we don't need an artificial
throttle, the SPI bus is the throttle.

Real-hardware effect: zero. ESP32 SPI runs at 80 MHz; a full
160×120 bitmap pushes in ~4 ms either way.

Applied in two places:
- examples/esp32-cam-lcd-preview/esp32-cam-lcd-preview.ino
- frontend/src/data/examples.ts (in-app gallery copy)

Long-term plan: batch SPI bytes at the worker level (one WS message
per N bytes instead of per byte) — that's a deeper change in
qemu-lcgamboa + Esp32Bridge.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 00:15:06 -03:00
David Montero Crespo c4c446cf39 fix(useWebcamFrames): drop JPEG quality 0.6 → 0.25 for emulator preview
The ESP32-CAM + ILI9341 example was rendering grey-X "decode failed"
rectangles. Serial showed:

    E (53868) esp_jpg_decode: JPG Decompression Failed!
                              Data format error

Root cause: the QEMU emulation delivers up to 8 KiB of JPEG bytes per
frame (8 EOFs × 1024 = 8192) plus a 2-byte FF D9 EOI injection at the
end of that window. Real webcam frames at quality 0.6 are ~11 KiB —
they get truncated mid-Huffman-scan in the firmware framebuffer.
cam_verify_jpeg_eoi accepts the frame (it found FF D9), but the
upstream jpg2rgb565() actually parses the JPEG and rejects the
truncated structure.

Quality 0.25 produces ~3-5 KiB JPEGs that fit the budget entirely.
The decoder finds the natural EOI well before our injection point,
parses cleanly, and renders to the TFT. Visual quality is fine for
an emulator preview — the user is seeing their webcam, not editing
print-quality photos.

Long-term fix is a smarter QEMU walker that ring-wraps to deliver
bigger JPEGs (>16 KiB possible by reusing descriptors mid-frame),
but that's a separate change in qemu-lcgamboa. This frontend tweak
unblocks the demo without another DLL rebuild cycle.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 00:08:17 -03:00
David Montero Crespo 6d56fe9a90 fix(tests): restore Frontend Tests CI — patch stale RP2040 mocks + install-libraries
CI's Frontend Tests workflow had been failing on master for ~15 runs.
Two pre-existing issues, neither related to the SPI refactor in 8b1433d
or the ESP32-CAM work:

1. RP2040Simulator mock missing attachCyw43 method (23 test files)

   PR #126 (8e769f8 "feat(multi-board): add wire-aware cross-board
   interconnect router", merged 2026-04-25) added a Pico-W-specific
   `sim.attachCyw43(bridge)` call inside addBoard(). The 23 test files
   that mock RP2040Simulator with vi.fn weren't updated; whenever a
   test path created a Pico W board the mock threw "TypeError:
   sim.attachCyw43 is not a function" and aborted addBoard.

   Fix: add `this.attachCyw43 = vi.fn()` to every affected mock.
   Also pre-populate `this.spi = { onByte: null, completeTransfer: vi.fn() }`
   so any future SPI-part tests don't trip on the new generic .spi
   adapter from 8b1433d.

2. install-libraries.test.ts payload mismatch

   PR #135 (b1026ec7 "library-version-uninstall", merged 2026-04-29)
   extended `installLibrary(name)` to `installLibrary(name, version?)`
   and now sends `{name, version: version ?? null}` over the wire.
   The test still asserted `{name}` only and failed.

   Fix: assert `{name, version: null}` for the no-version call.

Verified locally: 1161 passed | 1 skipped (was 1117 passed | 44 failed).

Backend E2E "Run HC-SR04 e2e test" is a separate failure that needs
its own investigation — it downloads QEMU binaries from a release and
runs real firmware compilation, which I can't reproduce on Windows.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 22:46:32 -03:00
David Montero Crespo c068612077
Merge pull request #137 from davidmonterocrespo24/esp32-cam
Esp32 cam
2026-05-02 22:40:06 -03:00
David Montero Crespo 8b1433deae refactor(spi): unify SPI bus interface across all simulators
Previous fix added an ESP32-specific code path inside ili9341Simulation
to subscribe to the QEMU worker's spi_event stream. That made the LCD
work on ESP32-CAM but left the underlying issue unsolved: every other
SPI part (custom chips, future SD-card emulators, the SSD168x ePaper
already in the codebase) would also need its own per-board branching.

The right shape: every simulator exposes a `.spi` member matching the
SAME SpiBusLike interface, and SPI parts hook .spi.onByte without
caring which board they're attached to. AVRSimulator already had
this — now everything else does too.

  frontend/src/simulation/SpiBus.ts (new)
    Defines the contract — `onByte: (mosi) => void | null` plus
    optional `completeTransfer(miso)`. Documents the single-listener
    semantics that AVR has had since day one.

  frontend/src/store/useSimulatorStore.ts
    Esp32BridgeShim gets a lazy `.spi` getter that wraps
    bridge.onSpiByte (the per-byte WS event from the QEMU worker).
    completeTransfer is a no-op because the worker drives MISO via
    its own _spi_response global. Covers ESP32 (Xtensa), ESP32-S3,
    ESP32-CAM, ESP32-C3 — every kind that routes through Esp32Bridge.

  frontend/src/simulation/RP2040Simulator.ts
    Adds a lazy `.spi` getter that re-routes rp2040.spi[0].onTransmit
    through the adapter. Default loopback (the prior behaviour) is
    preserved when no part has accessed `.spi` yet — only consumers
    that opt in see their handler invoked. Covers Pico and Pico W.

  frontend/src/simulation/parts/ComplexParts.ts
    ili9341Simulation no longer has an ESP32 special case. Single
    code path: `simulator.spi.onByte = handler`. Works on AVR,
    RP2040, all ESP32 variants. Same pattern is now available to
    every future SPI part — ssd1306, sd-card, oled, etc.

The Esp32Bridge.ts spi_event field-name fix from 6afa62e (msg.data.event
instead of the non-existent msg.data.data) stays in place — that's what
makes the per-byte stream actually arrive in the bridge.

Verified: ILI9341 + ESP32-CAM gallery example renders the live webcam
preview after a hard refresh. The same simulation code works on Arduino
Uno + ILI9341 (the existing ili9341-test-sketch in example_zip).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 22:36:29 -03:00
David Montero Crespo 6afa62ea17 fix(ili9341): add ESP32 SPI byte routing so the LCD renders on ESP32-CAM
The ILI9341 part simulation only hooked AVR's SPI peripheral. For
ESP32 the simulator is Esp32BridgeShim (no .spi member), so
attachEvents bailed early and the LCD stayed black even though the
firmware was driving SPI traffic correctly.

The QEMU worker already emits per-byte spi_event WS messages
(see backend/app/services/esp32_worker.py::_on_spi_event), and the
Esp32Bridge already had an onSpiEvent hook — but the bridge was
reading msg.data.data (a non-existent field) instead of decoding
the worker's {bus, event, response} format. Fixed.

Two changes:

1. Esp32Bridge.ts: decode the spi_event payload correctly. The
   worker encodes byte transfers as `mosi << 8` (op = low byte = 0x00)
   and CS-line changes as `((cs<<1)|level) << 8 | 0x01` (op == 0x01).
   Added onSpiByte (per-byte) and onSpiCsChange callbacks alongside
   the existing onSpiEvent for backwards compat.

2. ComplexParts.ts ili9341Simulation: detect Esp32BridgeShim via
   `getBridge()` duck-type check. When present, subscribe to
   bridge.onSpiByte and feed bytes into the same processCommand /
   processData pipeline used by the AVR path. DC tracking via
   pinManager.onPinChange already works for ESP32 because the bridge
   fires triggerPinChange on every gpio_change WS event.

Verified end-to-end: ESP32-CAM + ILI9341 example in the gallery now
renders the live webcam preview to the simulated TFT (160×120 RGB565
centered in the 320×240 panel) at ~3-4 fps.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 22:31:24 -03:00
David Montero Crespo 155b962c21 feat(gallery): add 2 ESP32-CAM examples to the in-app gallery
Adds two listed examples to the gallery (book icon → Examples) so
users can one-click load the new ESP32-CAM emulation:

1. ESP32-CAM: Webcam Demo (sensors / beginner)
   Minimal sketch — init OV2640, verify SCCB chip-id, loop on
   esp_camera_fb_get() printing frame metadata to Serial. Proves
   the emulation is alive without any external components.

2. ESP32-CAM + ILI9341 Live Preview (displays / intermediate)
   Full demo — decode JPEG with jpg2rgb565() (built-in to
   esp32-camera/conversions, header exposed by the Velxio compile
   template) and render the resulting RGB565 bitmap to a 320×240
   SPI TFT. Pre-wired diagram: ILI9341 connected via VSPI to GPIOs
   12-15 (the only block free after OV2640 takes over the rest of
   the AI-Thinker pins).

Type changes:
- ExampleProject.boardType union extended with 'esp32-cam'
- BOARD_TABS in ExamplesGallery.tsx gets a new "ESP32-CAM" tab
  (orange #d35400)

Both examples use boardFilter: 'esp32-cam' so they show under
the new tab and not the generic ESP32 one.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 22:21:19 -03:00
David Montero Crespo e73c1d341c feat: ESP32-CAM emulation with webcam frame bridge
First open-source end-to-end emulation of the AI-Thinker ESP32-CAM
in QEMU, paired with a browser webcam → firmware bridge so users can
develop camera sketches without hardware. Status: esp_camera_init()
returns ESP_OK; OV2640 chip-id verifies (PID/VER/MIDH/MIDL exactly
match the datasheet); GPIO 25 VSYNC NEGEDGE interrupt enabled by
the upstream driver. Final piece (cam_task accepting frames) is in
progress — descriptor walker fix landed in this commit.

Backend (Python/FastAPI):
- simulation.py: camera_attach/frame/detach WS handlers
- esp32_worker.py: ctypes binding to velxio_push_camera_frame +
  feature-detection fallback for older DLLs
- esp32_lib_manager.py: forward camera commands to the worker stdin
- esp-idf-template/main/CMakeLists.txt: esp32-camera headers added
  via add_prebuilt_library + REQUIRES driver (resolves i2c_master_*
  symbols). LED_BUILTIN=2 fallback for sketches that hardcode it.

Frontend (React/TS):
- EditorToolbar.tsx: ESP32-CAM (and the rest of the ESP32 family)
  added to isQemuBoard list — Run button now starts the QEMU bridge
  for these boards instead of falling through to the AVR path
- useWebcamFrames.ts: getUserMedia → OffscreenCanvas →
  toBlob('image/jpeg') → base64 → WS at ~10 fps
- CameraToggle.tsx: header button with status colors + frame counter
- SimulatorCanvas.tsx: render CameraToggle for esp32-cam boards
- Esp32Bridge.ts: sendCameraAttach/Frame/Detach + chunked btoa
- useSimulatorStore.ts: diagnostic log on compileBoardProgram
- components-metadata.json: regen including esp32-cam component

Submodule pointer:
- wokwi-libs/qemu-lcgamboa → ff8eee0 (camera devices commit on
  davidmonterocrespo24/qemu-lcgamboa branch picsimlab-esp32)

Investigation + tests in test/test-esp32-cam/:
- 13 autosearch markdown docs (overview, SOTA, OV2640 spec, DVP/I2S
  spec, build blueprint, blockers resolved, descriptor walker fix)
- 5 sketches (camera_init, sccb_probe, dma_smoke, frame_roundtrip,
  webcam_demo) + 8 live + WS regression tests
- README with the user-facing flow

.gitignore:
- libqemu-*.dll.{pre-camera,new,bak} (rollback points, regenerated)
- wokwi-libs/esp32-camera/ (clone consumed by arduino-esp32 path,
  not part of this repo)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 19:29:01 -03:00
David Montero Crespo 81f3563b9f
Merge pull request #127 from naweiss/fix/oscilloscope
Fix Oscilloscope
2026-05-02 19:15:34 -03:00
ZhadowValker cc43a956ba feat: Add library version management and uninstall functionality
Backend:
- Add version field to InstallLibraryRequest
- Add fallback and requested_version to InstallResponse
- Add DELETE /api/libraries/uninstall endpoint
- Enhance install_library() for versioned installs (LibName@version)
- Add semver validation and fallback logic
- Add uninstall_library() method
- Fix _parse_version() to reject non-numeric version parts

Frontend:
- Update installLibrary() with optional version parameter
- Add uninstallLibrary() and resolveLibraryVersion() helpers
- Add version selector dropdown in Library Manager
- Add UNINSTALL button for installed libraries
- Show fallback messages when requested version unavailable
- Add parseLibSpec() and version badges in InstallLibrariesModal
2026-05-02 12:54:09 +05:30
David Montero Crespo 6a88375bc0 feat: persist multi-board projects + add auto-save
The project save/load pipeline only persisted a single `board_type`, so
multi-board workspaces silently lost every board except the active one
on save, and wires referencing the dropped boards' IDs orphaned to the
canvas corner on reload. An audit of the production backup found 74/306
projects (24%) with at least one orphaned wire and 174/301 non-trivial
projects whose code was still the default Blink template — strong signal
that users save once and never re-save.

Backend
- Add `boards_json` column on `projects` with idempotent ALTER TABLE in
  the lifespan migration list.
- New `FileGroup` schema + `file_groups` array on
  ProjectCreate/Update/Response. Legacy `files`/`code` kept for back-compat.
- `project_files.py` now uses `{pid}/{groupId}/{filename}` subdirs via
  `read_groups`/`write_groups`. Legacy flat layouts are auto-promoted on
  read; legacy single-list `files` only updates the active group, leaving
  other boards' files intact.
- `_persist_files_from_body` honors file_groups → files → code priority.

Frontend
- `useSimulatorStore.addBoard` accepts an optional `explicitId` so
  saved board IDs can be restored verbatim (wires reference IDs literally).
- New `loadProjectState({boards, fileGroups, components, wires,
  activeBoardId})` action: tears down current boards, recreates from the
  payload, restores file groups atomically, recalculates wire positions
  on the next frame, and refreshes the Interconnect.
- `useEditorStore.replaceFileGroups` for atomic multi-group restore.
- `SaveProjectModal` and `ProjectByIdPage`/`ProjectPage` now go through
  `buildSavePayload` / `buildLoadPayload` (handles pre-backfill projects
  by synthesising a default board from `board_type`).

Auto-save (#useAutoSaveProject hook)
- 2.5s debounced silent PUT triggered ONLY when an authenticated user
  has a `currentProject` with a UUID. State hash detects real changes
  vs. UI-only churn; baseline is reset on project load so the just-loaded
  state isn't immediately re-saved.
- `beforeunload` flush via `fetch keepalive: true` (supports PUT +
  credentials, survives unload).
- Compact status indicator in `AppHeader` (idle/dirty/saving/saved/error).

Backfill script (one-off, idempotent)
- `backend/scripts/backfill_boards_2026_05.py` populates `boards_json`
  for legacy projects. Heuristic per project, based on which board IDs
  the wires reference:
    Case A — wires only ref 'arduino-uno' but board_type ≠ uno:
             rename id→board_type and rewrite wire endpoints.
    Case B — single-board normal: keep verbatim.
    Case C — multi-board: recreate one board per distinct ref, infer
             kind by stripping trailing -N suffix.
  Also moves any flat files into the active board's group subdir.
  Stdlib-only, runs from host or `docker exec`.

Docker
- `Dockerfile.standalone` now copies `backend/scripts/` into the image
  so the backfill is callable via `docker exec velxio-app python
  /app/scripts/backfill_boards_2026_05.py --apply`.

Verified locally on the restored production backup (363 projects):
33 Case A, 316 Case B, 14 Case C, 135 wire endpoints renamed, 0 orphans.
Re-running the script after apply skips all 363 (idempotent).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 13:43:33 -03:00
David Montero Crespo a9b48a3cc6 feat: implement EditorPage with file explorer and simulator canvas components 2026-04-30 15:27:58 -03:00
naweiss ec730fb463 Bug fix: missing boardId in getOscilloscopeCallback 2026-04-30 08:28:17 +03:00
David Montero Crespo 2e4c470f75 feat: add support for UC8159c (ACeP 7-colour) display
- Implement UC8159cDecoder for handling 7-colour ACeP panels.
- Introduce painting functions for UC8159c frames in EPaperPart.
- Update EPaperPart to handle both SSD168x and UC8159c frame types.
- Add integration tests for EPaperPart and UC8159cDecoder.
- Create example sketch for 5.65" ACeP 7-colour panel.
- Enhance error handling in test cases for library dependencies.
2026-04-30 00:27:40 -03:00
David Montero 2dd152e383 SEO improvements 2026-04-30 00:27:39 -03:00
David Montero Crespo bcc7129aa3 feat: Add support for SSD168x ePaper panels
- Introduced EPaperPanels.ts to define configurations for various ePaper panels including dimensions, refresh rates, and controller details.
- Implemented SSD168xDecoder.ts to handle the decoding of SPI commands for the SSD168x family of ePaper displays.
- Created EPaperPart.ts to manage the simulation of ePaper panels, integrating with the existing simulator architecture and handling events.
- Added example sketches for 2.13", 2.9", 4.2", and 7.5" ePaper displays, demonstrating basic functionality and text rendering.
- Ensured compatibility with AVR, RP2040, and ESP32 platforms, with appropriate pin configurations for each.
2026-04-29 22:23:00 -03:00
David Montero Crespo 175b248108 feat(epaper): Add SVG layouts and emulation plan for ePaper panels
- Introduced SVG layout dimensions for Phase 1 (B/W mono) and Phase 2 (colour) ePaper panels, detailing active areas, bezels, and pin layouts.
- Developed a phased emulation plan outlining the architecture and deliverables for different panel types, including SSD168x and UC81xx.
- Created a canonical "Hello, World!" sketch for the 1.54" ePaper panel, ensuring compatibility across ESP32, Raspberry Pi Pico, and Arduino Uno.
- Implemented a pure Python SSD168x decoder to validate SPI command sets and framebuffers against specifications.
- Added tests for compiling the hello-world sketch across supported boards and for the SSD168x protocol to ensure correct framebuffer behavior.
2026-04-29 02:33:59 -03:00
David Montero Crespo 641ac8c1de Add comprehensive tests for Cyw43Emulator functionality and lifecycle
- Implemented handshake tests to validate initial bus state and register responses.
- Created end-to-end tests for Pico W LED blinking using MicroPython firmware.
- Added SDPCM framing tests to ensure proper encoding and decoding of control frames.
- Developed IOCTL tests to verify command responses and state changes in the emulator.
- Established a full lifecycle test for WiFi operations, including scanning, connecting, and packet handling.
- Introduced TypeScript configuration for test files to ensure compatibility and strict type checking.
2026-04-29 00:21:26 -03:00
David Montero Crespo b39041ca4c feat(editor): enhance toolbar layout with center slot for file tabs and improve responsiveness 2026-04-28 23:23:20 -03:00
David Montero Crespo 7a26d01965 feat(attiny85): add Web Component for ATtiny85 with pinInfo support 2026-04-28 22:57:50 -03:00
David Montero Crespo 0a66c70512 feat(examples): update dual Pico example to demonstrate bidirectional digital handshake 2026-04-28 22:38:54 -03:00
David Montero Crespo 2ac94aed24 fix(loadExample): update filename logic for Arduino-style boards to ensure correct file extension 2026-04-28 20:40:48 -03:00
David Montero Crespo 7f2014bef7 Add ESP32 chip demos and comprehensive tests for I2C, SPI, and UART interactions
- Implemented `esp32_spi_chip_demo.ino` to demonstrate SPI communication with a 74HC595 shift register.
- Created `esp32_uart_chip_demo.ino` for UART loopback testing with ROT13 transformation.
- Added Python tests for compiling chips and sketches, ensuring valid WASM output and successful compilation for various board families.
- Developed end-to-end tests for ESP32 with custom chips using I2C and SPI, validating synchronous communication through the backend.
- Introduced GPIO bridge tests to verify serial communication and GPIO state changes.
- Ensured all tests validate the expected behavior of the custom chips and their interaction with the ESP32 firmware.
2026-04-28 19:24:39 -03:00
David Montero Crespo fa170a082a Add shared validators and test configurations for Velxio projects
https://github.com/kritishmohapatra/100_Days_100_IoT_Projects
- Introduced `_lib.py` containing shared validators for board support and static source analysis for MicroPython projects.
- Added `conftest.py` to configure pytest for the test suite, simplifying import paths.
- Created `NOT_SUPPORTED.md` files for two projects indicating they cannot be emulated in Velxio due to lack of source code.
- Implemented unit tests for the unsupported projects to verify the presence of the NOT_SUPPORTED marker and source preservation.
2026-04-28 00:36:05 -03:00
David Montero Crespo 63896e2049 feat(activity): add user daily activity metrics and modal for detailed project interaction 2026-04-26 19:39:45 -03:00
David Montero Crespo 8e769f8a4e feat(multi-board): add wire-aware cross-board interconnect router
Fixes the user-reported bug where two RPi Pico W boards wired GP0↔GP1
running SerialPassthrough don't communicate. Replaces the broken
broadcast-style cross-board logic in addBoard (only routed AVR↔Pi3B,
ignored wires entirely, no RP2040↔anything path) with a wire-aware
Interconnect singleton.

Architecture: digital pin transitions are the lowest-common-denominator
abstraction. Each simulator's hardware peripherals (UART/I2C/SPI) and
bit-banging libraries (SoftwareSerial, software I2C) decode the
transitions naturally — propagate the pin and the protocols come for
free. For cross-process boards (ESP32 backend QEMU, Pi3B QEMU) a
byte-level shortcut is additionally enabled on hardware-UART pin
pairs to handle high-baud links over WebSocket latency.

Implementation:
- New simulation/Interconnect.ts singleton subscribes to wire/board
  changes via the Zustand store. Handlers per tier: browser-sim →
  pinManager.onPinChange, ESP32 → Esp32Bridge.sendPinEvent, Pi3B →
  bridge.sendPinEvent. Re-entrancy guard via per-(board,pin) Set.
- New utils/boardProtocols.ts classifies pins (uart-tx, i2c-sda, etc.)
  per board kind, used as optimization hint for the byte shortcut.
- types/wire.ts: added signalType field, exports WireSignalType /
  WireColorMap (fixes a pre-existing TS import error in wireColors).
- Deleted the bridgeMap/simulatorMap broadcast forEach blocks in
  addBoard. Initial board + future boards register with Interconnect
  via setInterconnectRuntime + store subscription.
- PinManager.resetPinStates() helper for test isolation.

Tests (16 new files, 96 tests, all passing):
- Per-pair × per-protocol matrix: dual-arduino-digital,
  dual-pico-digital, arduino-pico-digital, triple-pico-digital-chain,
  dual-arduino-hw-uart, dual-arduino-software-serial,
  arduino-pico-mixed-uart, arduino-esp32-uart, dual-esp32-uart,
  pi3-pico-uart, arduino-pico-i2c, arduino-arduino-spi,
  interconnect-routing, dual-arduino-multi-protocol (UART+I2C+SPI+
  digital + concurrent), dual-pico-multi-protocol (UART0+UART1 alt+
  I2C0+I2C1+SPI0+digital + 3-Pico star topology)
- Updated dual-pico-serial-passthrough to assert correct behaviour
- Backend test/multi_board_esp32/test_dual_esp32_serial.py for two
  real QEMU instances (skip-graceful when lcgamboa lib absent)

Verified: 1107/1107 tests pass, zero regressions, vite build OK.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 19:47:40 -03:00
David Montero Crespo 5bf3a3d5ed feat(metrics): add usage analytics dashboard with country tracking
Track per-user, per-project, and per-board usage to inform pricing tier
decisions. Adds an admin dashboard with KPIs (DAU/WAU/MAU, totals,
success rate), time-series charts for compiles/runs, board family +
FQBN breakdowns, "board diversity" pie chart (key freemium signal),
top users/projects, and per-country breakdown via Cloudflare's
CF-IPCountry header. Admin can now also view private projects.

Backend:
- New UsageEvent table (append-only event log with user_id, project_id,
  event_type, board_fqbn/family, country, error_kind, duration_ms)
- Aggregate counters on User (total_compiles/runs/errors, last_active,
  signup_country, last_country) and Project (compile/run/update counts,
  last_compiled/run timestamps) kept in sync by MetricsService for O(1)
  dashboard reads
- 10 admin endpoints under /api/admin/metrics/{overview, timeseries,
  boards, board-diversity, top-users, top-projects, countries,
  users/{id}, projects/{id}}
- POST /api/metrics/run for client-side run telemetry
- Country detection via cf-ipcountry header (no DB / no API calls)
- Auto-migrations in lifespan for legacy DBs

Frontend:
- recharts-powered Dashboard tab with KPI cards and 4 charts
- New Boards tab with per-family + per-FQBN breakdown
- Country column with flag emoji on Users tab
- Top countries card on Dashboard
- compileCode now forwards project_id; Run button reports via WS

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 19:47:40 -03:00
davidmonterocrespo24 4d17dfd832 feat: add /v2-5 release landing page for Velxio 2.5 (SPICE)
Mirrors the /v2 page structure but targets the 2.5 launch: ngspice-WASM
analog simulation, hybrid digital + analog co-simulation with Arduino /
ESP32 / RP2040, expanded component catalog, live instruments, 40 new
analog/hybrid examples.

- Reuses Velxio2Page.css + SEOPage.css — no new stylesheet to maintain
- Adds SoftwareApplication, BreadcrumbList, and FAQPage JSON-LD for
  rich-results eligibility
- Registers the route in App, entry-server (SSR prerender), and
  seoRoutes (sitemap, priority 0.95 / changefreq weekly)
2026-04-24 16:42:47 +02:00