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>
This commit is contained in:
David Montero Crespo 2026-05-04 00:04:11 -03:00
parent c2413f6384
commit 531c337d19
31 changed files with 1813 additions and 2215 deletions

View File

@ -12,8 +12,13 @@ env/
# Build outputs (will be built inside Docker)
frontend/dist/
frontend/.vite/
third-party/avr8js/dist/
third-party/wokwi-elements/dist/
# Reference-only third-party clones — Velxio resolves these from npm at
# build time, so the Docker image doesn't need the upstream sources.
third-party/avr8js/
third-party/rp2040js/
third-party/wokwi-elements/
third-party/wokwi-boards/
# QEMU source & Windows builds (only prebuilt/qemu/ .so files are needed)
third-party/qemu-lcgamboa/

View File

@ -13,55 +13,26 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Do NOT use submodules: recursive — the submodule pointers in this repo
# are stale and predate package.json. We clone the libs fresh below.
- name: Setup Node.js 22
uses: actions/setup-node@v4
with:
node-version: '22'
# Clone third-party fresh (stale submodule pointers can't be used)
- name: Clone third-party
run: |
git clone --depth=1 https://github.com/wokwi/avr8js.git third-party/avr8js
git clone --depth=1 https://github.com/wokwi/rp2040js.git third-party/rp2040js
git clone --depth=1 https://github.com/wokwi/wokwi-elements.git third-party/wokwi-elements
# The metadata generator scans wokwi-elements/src/, which only ships in
# the upstream repo (not on npm). Clone it once for the freshness check.
- name: Clone wokwi-elements (for metadata regeneration only)
run: git clone --depth=1 https://github.com/wokwi/wokwi-elements.git third-party/wokwi-elements
# Cache third-party node_modules to speed up repeated runs
- name: Cache third-party node_modules
uses: actions/cache@v4
with:
path: |
third-party/avr8js/node_modules
third-party/rp2040js/node_modules
third-party/wokwi-elements/node_modules
key: third-party-${{ hashFiles('third-party/avr8js/package-lock.json', 'third-party/rp2040js/package-lock.json', 'third-party/wokwi-elements/package-lock.json') }}
# Cache frontend node_modules
- name: Cache frontend node_modules
uses: actions/cache@v4
with:
path: frontend/node_modules
key: frontend-${{ hashFiles('frontend/package-lock.json') }}
# Build avr8js (referenced as file: dep in frontend/package.json)
- name: Build avr8js
run: cd third-party/avr8js && npm install && npm run build
# Build rp2040js (referenced via vite alias to dist/esm)
- name: Build rp2040js
run: cd third-party/rp2040js && npm install && npm run build
# Build wokwi-elements (referenced as file: dep in frontend/package.json)
- name: Build wokwi-elements
run: cd third-party/wokwi-elements && npm install && npm run build
# Install frontend deps (picks up file: references to avr8js and wokwi-elements)
- name: Install frontend dependencies
run: cd frontend && npm ci
# Install root deps (typescript, tsx) so the metadata generator runs
- name: Install root dev dependencies
run: npm ci
@ -78,6 +49,5 @@ jobs:
exit 1
fi
# Run all vitest tests
- name: Run tests
run: cd frontend && npm test

View File

@ -72,25 +72,25 @@ npm run lint
**Access:**
- App: http://localhost:5173
### Wokwi Libraries (Local Repositories)
### Wokwi Libraries (npm)
The project uses local clones of Wokwi repositories in `third-party/`:
- `wokwi-elements/` - Web Components for electronic parts
- `avr8js/` - AVR8 CPU emulator
- `rp2040js/` - RP2040 emulator
`@wokwi/elements`, `avr8js` and `rp2040js` are pulled directly from the npm
registry — see version pins in `frontend/package.json`. No clone or local
build is required for the Docker image, manual install, or CI.
**Update libraries:**
```bash
update-third-party.bat
```
The folders under `third-party/` are reference-only (credits / offline
hacking). The one exception is `qemu-lcgamboa` (real source dependency for
ESP32 emulation when rebuilding QEMU).
Or manually:
```bash
cd third-party/wokwi-elements
git pull origin main
npm install
npm run build
```
**Bump a wokwi lib version:** edit the version string in
`frontend/package.json` and run `npm install` in `frontend/`.
**Adding new components to wokwi-elements:** the metadata generator
(`scripts/generate-component-metadata.ts`) scans the upstream `src/`,
which the npm package doesn't ship. Clone wokwi-elements once into
`third-party/wokwi-elements/` and run `npm run generate:metadata`. The
script gracefully skips when the clone is absent — `components-metadata.json`
is committed.
### External Dependencies
@ -116,17 +116,11 @@ arduino-cli core install arduino:avr
### Critical Architecture Patterns
**1. Vite Aliases for Local Wokwi Libs**
**1. Wokwi libs come from npm**
The `frontend/vite.config.ts` uses path aliases to import from local repositories:
```typescript
resolve: {
alias: {
'avr8js': path.resolve(__dirname, '../third-party/avr8js/dist/esm'),
'@wokwi/elements': path.resolve(__dirname, '../third-party/wokwi-elements/dist/esm'),
},
}
```
`@wokwi/elements`, `avr8js` and `rp2040js` are listed as regular
dependencies in `frontend/package.json`. Vite resolves them from
`node_modules` like any other package — no aliases, no `file:` references.
**2. Multi-File Workspace (useEditorStore)**
@ -404,15 +398,14 @@ There are known pre-existing TS errors that do NOT block the app from running:
### 8. Docker Build — third-party
The git submodule pointers for `rp2040js` and `wokwi-elements` in this repo are stale (point to very old commits that predate `package.json`). The `Dockerfile.standalone` works around this by **cloning the libs fresh from GitHub** at build time instead of COPYing from the build context:
`Dockerfile.standalone` does NOT clone any wokwi-* repos. The frontend stage
just does `COPY frontend/ scripts/` then `npm install && npm run build:docker`,
which pulls `@wokwi/elements`, `avr8js`, `rp2040js` from npm. Board SVGs live
in `frontend/public/boards/`, component SVGs in `frontend/public/component-svgs/`,
and `components-metadata.json` is committed.
```dockerfile
RUN git clone --depth=1 https://github.com/wokwi/avr8js.git third-party/avr8js \
&& git clone --depth=1 https://github.com/wokwi/rp2040js.git third-party/rp2040js \
&& git clone --depth=1 https://github.com/wokwi/wokwi-elements.git third-party/wokwi-elements
```
The GitHub Actions workflow does NOT use `submodules: recursive` for this reason.
The frontend-tests CI workflow only clones `wokwi-elements` (for the
metadata staleness check), not the other two.
### 9. Backend Gotchas
@ -445,7 +438,9 @@ npm test
### Adding a New Electronic Component
1. Check if wokwi-elements has the component (see `third-party/wokwi-elements/src/`)
1. Check if wokwi-elements has the component — either browse
https://github.com/wokwi/wokwi-elements or `ls third-party/wokwi-elements/src/`
if the optional clone is present
2. Create React wrapper in `frontend/src/components/components-wokwi/`
3. Add component type to `useSimulatorStore` interface
4. Update SimulatorCanvas to render the component

View File

@ -69,29 +69,10 @@ FROM node:20 AS frontend-builder
WORKDIR /app
# Clone third-party fresh from upstream to avoid stale submodule pointers.
# git is pre-installed in the node:20 Debian image.
RUN git clone --depth=1 https://github.com/wokwi/avr8js.git third-party/avr8js \
&& git clone --depth=1 https://github.com/wokwi/rp2040js.git third-party/rp2040js \
&& git clone --depth=1 https://github.com/wokwi/wokwi-elements.git third-party/wokwi-elements \
&& git clone --depth=1 https://github.com/wokwi/wokwi-boards.git third-party/wokwi-boards
# Build avr8js
WORKDIR /app/third-party/avr8js
RUN npm install && npm run build --if-present
# Build rp2040js (may have no build script on some commits)
WORKDIR /app/third-party/rp2040js
RUN npm install && npm run build --if-present
# Build wokwi-elements
WORKDIR /app/third-party/wokwi-elements
RUN npm install && npm run build --if-present
# Build frontend
# components-metadata.json is already committed; skip generate:metadata
# (it requires wokwi-elements/src which isn't needed at runtime)
WORKDIR /app
# avr8js, rp2040js and @wokwi/elements are pulled directly from the npm
# registry (see frontend/package.json) — no upstream git clones needed.
# Board SVGs live in frontend/public/boards/, component SVGs in
# frontend/public/component-svgs/, and components-metadata.json is committed.
COPY frontend/ frontend/
COPY scripts/ scripts/
WORKDIR /app/frontend
@ -144,7 +125,10 @@ COPY backend/app/ ./app/
# run with: docker exec velxio-app python /app/scripts/<script> --apply
COPY backend/scripts/ ./scripts/
# Setup Nginx configuration
# Setup Nginx configuration. Remove Debian's stock site so it doesn't shadow
# ours as the default_server (was Issue #108: users behind reverse proxies got
# the "Welcome to nginx" page because the stock site claimed default_server).
RUN rm -f /etc/nginx/sites-enabled/default
COPY deploy/nginx.conf /etc/nginx/conf.d/default.conf
# Copy built frontend assets from builder stage

View File

@ -267,15 +267,22 @@ The `/app/data` volume contains:
```bash
git clone https://github.com/davidmonterocrespo24/velxio.git
cd velxio
cp backend/.env.example backend/.env # edit as needed
docker compose -f docker-compose.prod.yml up -d
docker compose up -d --build
```
#### Environment variables (`backend/.env`)
That's it — open <http://localhost:3080>. The container generates a random
`SECRET_KEY` on first boot and persists it in `./data/`, so no `.env` is
required to get going.
#### Optional: customize environment
Create `backend/.env` (copy from `backend/.env.example`) only when you need
OAuth, a fixed `SECRET_KEY`, or HTTPS-only cookies. The compose file picks it
up automatically if it exists.
| Variable | Default | Description |
| --- | --- | --- |
| `SECRET_KEY` | *(required)* | JWT signing secret |
| `SECRET_KEY` | *(auto-generated)* | JWT signing secret. If unset the entrypoint creates one and saves it under `data/.secret_key`. |
| `DATABASE_URL` | `sqlite+aiosqlite:////app/data/velxio.db` | SQLite path |
| `DATA_DIR` | `/app/data` | Directory for project files |
| `FRONTEND_URL` | `http://localhost:5173` | Used for OAuth redirect |
@ -284,6 +291,15 @@ docker compose -f docker-compose.prod.yml up -d
| `GOOGLE_REDIRECT_URI` | `http://localhost:8001/api/auth/google/callback` | Must match Google Console |
| `COOKIE_SECURE` | `false` | Set `true` when serving over HTTPS |
> **Deploying behind a reverse proxy?** The container listens on plain HTTP
> on port 80 and accepts any `Host` header. If you used to get the
> "Welcome to nginx" page through your proxy, that bug was fixed in the
> master image.
> **Running velxio.dev itself?** Production-only configuration (host nginx
> + HTTPS, backups, pinned upstream commit) lives in its own repo:
> [github.com/velxio/velxio-prod](https://github.com/velxio/velxio-prod).
### Option C: Manual Setup
**Prerequisites:** Node.js 18+, Python 3.12+, arduino-cli
@ -293,31 +309,24 @@ git clone --recurse-submodules https://github.com/davidmonterocrespo24/velxio.gi
cd velxio
```
> **Already cloned without `--recurse-submodules`?** The `third-party/` directories will be empty. Run:
> ```bash
> git submodule update --init --recursive
> ```
> If that fails because the submodule pointers are stale, clone the libs fresh:
> ```bash
> cd third-party
> git clone --depth=1 https://github.com/wokwi/avr8js.git avr8js
> git clone --depth=1 https://github.com/wokwi/wokwi-elements.git wokwi-elements
> git clone --depth=1 https://github.com/wokwi/rp2040js.git rp2040js
> git clone --depth=1 https://github.com/wokwi/wokwi-boards.git wokwi-boards
> cd ..
> ```
> `wokwi-boards` ships static SVG assets imported by the ESP32 / Pi Pico W
> board components — without it the frontend build fails on missing
> `board.svg` imports.
**Build the Wokwi libraries** (required before running the frontend):
```bash
cd third-party/avr8js && npm install && npm run build && cd ../..
cd third-party/wokwi-elements && npm install && npm run build && cd ../..
cd third-party/rp2040js && npm install && npm run build && cd ../..
# wokwi-boards is asset-only (SVGs) — no install or build needed.
```
> **Heads-up about `third-party/`:** `avr8js`, `rp2040js` and
> `@wokwi/elements` are now resolved from the npm registry — `npm install`
> in `frontend/` pulls them automatically, no clone or build of upstream
> repos is required. The folders under `third-party/` are kept as
> reference-only credits for the upstream projects; you only need to clone
> them if you are *adding new components to wokwi-elements* (the metadata
> generator scans its `src/`).
>
> Board SVGs live directly in `frontend/public/boards/`, so `wokwi-boards`
> is also not required to clone.
>
> The one third-party folder that DOES need to be present for ESP32
> emulation is `qemu-lcgamboa` — but only if you're rebuilding QEMU from
> source. Otherwise the prebuilt `.so` files come from the
> [qemu-prebuilt release](https://github.com/davidmonterocrespo24/velxio/releases/tag/qemu-prebuilt).
>
> Older guides may mention a `wokwi-libs/` directory; that was renamed to
> `third-party/` long ago, so any path containing `wokwi-libs/` is stale.
```bash
# Backend
@ -371,17 +380,19 @@ velxio/
│ ├── models/ # User, Project (SQLAlchemy)
│ ├── services/ # arduino_cli, esp32_worker, qemu_manager, gpio_shim
│ └── core/ # config, security, dependencies
├── third-party/ # Local clones of Wokwi repos
├── third-party/ # Local clones of upstream repos
│ ├── wokwi-elements/ # Web Components for electronic parts
│ ├── avr8js/ # AVR8 CPU emulator
│ ├── rp2040js/ # RP2040 emulator
│ └── qemu-lcgamboa/ # QEMU fork for ESP32 Xtensa emulation
│ # NB: board SVGs live directly in frontend/public/boards/
├── img/ # Raspberry Pi 3 boot images (kernel8.img, dtb, OS image)
├── deploy/ # nginx.conf, entrypoint.sh
├── docs/ # Technical documentation
├── Dockerfile.standalone # Single-container production image
├── docker-compose.yml # Development compose
└── docker-compose.prod.yml # Production compose
└── docker-compose.yml # Self-hosting compose
# (production deployment lives in
# https://github.com/velxio/velxio-prod)
```
---

View File

@ -2,6 +2,10 @@ fastapi==0.115.0
uvicorn[standard]==0.32.0
websockets>=12.0
sqlalchemy==2.0.36
# greenlet is needed by SQLAlchemy async on some Python builds (notably Windows
# Python 3.12+ wheels). Without it, app startup fails with
# "ValueError: the greenlet library is required". See issue #120.
greenlet>=3.0.0
aiosqlite==0.20.0
pydantic>=2.11.0
pydantic-settings>=2.6.0

View File

@ -1,6 +1,19 @@
#!/bin/bash
set -e
# Auto-generate SECRET_KEY if not provided so the app boots out-of-the-box
# without requiring the user to create backend/.env first. Persists in the
# data volume so JWTs survive container restarts.
if [ -z "$SECRET_KEY" ]; then
SECRET_FILE="${DATA_DIR:-/app/data}/.secret_key"
mkdir -p "$(dirname "$SECRET_FILE")"
if [ ! -f "$SECRET_FILE" ]; then
echo "🔑 No SECRET_KEY provided — generating one (saved to $SECRET_FILE)"
head -c 48 /dev/urandom | base64 | tr -d '\n' > "$SECRET_FILE"
fi
export SECRET_KEY="$(cat "$SECRET_FILE")"
fi
# Ensure arduino-cli config and board manager URLs are set up
if [ ! -f /root/.arduino15/arduino-cli.yaml ]; then
echo "📦 Initializing arduino-cli config..."

View File

@ -1,7 +1,7 @@
server {
listen 80;
listen [::]:80;
server_name localhost velxio.dev www.velxio.dev;
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /usr/share/nginx/html;
index index.html;

View File

@ -1,57 +0,0 @@
server {
listen 80;
listen [::]:80;
server_name velxio.dev www.velxio.dev;
root /usr/share/nginx/html;
index index.html;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Frontend SPA routing
location / {
try_files $uri $uri/ /index.html;
}
# Health check endpoint
location /health {
proxy_pass http://127.0.0.1:8001/health;
proxy_set_header Host $host;
}
# Proxy /api requests to localhost backend
location /api/ {
proxy_pass http://127.0.0.1:8001/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300s;
proxy_connect_timeout 75s;
}
# Proxy websockets/docs if needed
location /docs {
proxy_pass http://127.0.0.1:8001/docs;
}
location /openapi.json {
proxy_pass http://127.0.0.1:8001/openapi.json;
}
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied any;
gzip_types text/plain text/css text/xml text/javascript application/javascript application/json application/xml;
}

View File

@ -1,29 +0,0 @@
services:
velxio:
build:
context: .
dockerfile: Dockerfile.standalone
container_name: velxio-app
restart: unless-stopped
ports:
- "3080:80"
env_file:
- ./backend/.env
environment:
- DATABASE_URL=sqlite+aiosqlite:////app/data/velxio.db
- DATA_DIR=/app/data
- IDF_PATH=/opt/esp-idf
- IDF_TOOLS_PATH=/root/.espressif
- ARDUINO_ESP32_PATH=/opt/arduino-esp32
volumes:
- ./data:/app/data
- arduino-libs:/root/.arduino15
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 90s
volumes:
arduino-libs:

View File

@ -1,6 +1,10 @@
# Development Docker Compose — same image as production for full parity.
# Usage: docker compose up --build
# Access: http://localhost:3080
#
# backend/.env is OPTIONAL. If missing, the entrypoint generates a random
# SECRET_KEY so the app boots out-of-the-box. Create backend/.env (copy
# backend/.env.example) only when you need OAuth or want a fixed SECRET_KEY.
services:
velxio:
build:
@ -11,7 +15,8 @@ services:
ports:
- "3080:80"
env_file:
- ./backend/.env
- path: ./backend/.env
required: false
environment:
- DATABASE_URL=sqlite+aiosqlite:////app/data/velxio.db
- DATA_DIR=/app/data

View File

@ -42,7 +42,7 @@ Complete documentation of the Docker build system, CI/CD pipelines, multi-archit
- [Security Headers](#security-headers)
9. [Docker Compose](#docker-compose)
- [Development (docker-compose.yml)](#development-docker-composeyml)
- [Production (docker-compose.prod.yml)](#production-docker-composeprodyml)
- [Production deployment](#production-deployment)
- [Environment Variables](#environment-variables)
- [Volumes](#volumes)
- [Health Checks](#health-checks)
@ -705,11 +705,14 @@ docker compose logs -f velxio # Follow logs
**Access:** `http://localhost:3080`
### Production (docker-compose.prod.yml)
### Production deployment
**Location:** `docker-compose.prod.yml`
Production-only configuration (host nginx with HTTPS, deploy/backup scripts,
pinned upstream commit) lives in a separate repo:
**[github.com/velxio/velxio-prod](https://github.com/velxio/velxio-prod)**.
Nearly identical to the dev compose file, with `container_name: velxio-app` instead of `velxio-dev`. In production, the image is typically pulled from a registry rather than built locally:
For self-hosters who don't need the velxio.dev-specific bits, the easiest
approach is the prebuilt image from the registry:
```bash
# Pull and run the pre-built image
@ -993,7 +996,6 @@ For ESP32 first-time compilation (cold build cache), this may still not be enoug
| `third-party/qemu-lcgamboa/.github/workflows/build-libqemu.yml` | CI/CD: builds QEMU .so for amd64 + arm64 |
| `deploy/entrypoint.sh` | Container startup script |
| `deploy/nginx.conf` | Nginx reverse proxy configuration |
| `docker-compose.yml` | Development compose file |
| `docker-compose.prod.yml` | Production compose file |
| `docker-compose.yml` | Self-hosting compose file (production at github.com/velxio/velxio-prod) |
| `prebuilt/qemu/` | Local QEMU prebuilt files (optional, for dev) |
| `backend/.env` | Backend environment variables (not committed) |

View File

@ -1,33 +1,15 @@
# ---- Stage 1: Build third-party and frontend ----
# ---- Stage 1: Build frontend ----
# avr8js / rp2040js / @wokwi/elements are resolved from npm by frontend's
# package.json — no upstream clones needed.
FROM node:20-slim AS builder
WORKDIR /app
# Copy root package.json (needed for metadata generation via tsx)
COPY package.json .
RUN npm install
# Copy metadata generation script
COPY scripts/ scripts/
# Copy and build avr8js
COPY third-party/avr8js/ third-party/avr8js/
WORKDIR /app/third-party/avr8js
RUN npm install && npm run build
# Copy and build wokwi-elements
COPY third-party/wokwi-elements/ third-party/wokwi-elements/
WORKDIR /app/third-party/wokwi-elements
RUN npm install && npm run build
# Copy frontend source and install dependencies
WORKDIR /app
COPY frontend/ frontend/
WORKDIR /app/frontend
RUN npm install
# Generate component metadata and build frontend for production
RUN npm run build
WORKDIR /app/frontend
RUN npm install && npm run build:docker
# ---- Stage 2: Serve with nginx ----
FROM nginx:alpine

File diff suppressed because it is too large Load Diff

View File

@ -27,10 +27,10 @@
"dependencies": {
"@monaco-editor/react": "^4.7.0",
"@types/react-syntax-highlighter": "^15.5.13",
"@wokwi/elements": "file:../third-party/wokwi-elements",
"@wokwi/elements": "1.9.2",
"@xterm/addon-fit": "^0.11.0",
"@xterm/xterm": "^6.0.0",
"avr8js": "file:../third-party/avr8js",
"avr8js": "0.21.0",
"axios": "^1.13.6",
"eecircuit-engine": "^1.7.0",
"idb-keyval": "^6.2.2",
@ -39,6 +39,7 @@
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-router-dom": "^7.13.1",
"rp2040js": "1.3.2",
"react-syntax-highlighter": "^16.1.1",
"recharts": "^3.8.1",
"zustand": "^5.0.11"

View File

@ -0,0 +1,121 @@
<svg width="28.2mm" height="53mm" version="1.1" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="pin-pattern" height="2.54mm" width="2.54mm" patternUnits="userSpaceOnUse">
<circle fill="#9a916c" cx="1.27mm" cy="1.27mm" r="1mm" />
<circle fill="white" cx="1.27mm" cy="1.27mm" r="0.6mm" />
</pattern>
<pattern id="esp-pin-pattern" height="4.6" width="5" patternUnits="userSpaceOnUse">
<path d="m3.5 2.85c0.268 1.82e-4 0.525-0.106 0.716-0.296 0.187-0.19 0.296-0.445 0.297-0.713l5.7e-4 -0.836c1.82e-4 -0.268-0.109-0.525-0.296-0.716-0.19-0.19-0.447-0.296-0.715-0.297l-3.5-0.0024-0.0019 2.85z" fill="#d1c479" stroke-width="3.11"></path>
</pattern>
<pattern id="small-pin-pattern" height="4.6" width="2.5" patternUnits="userSpaceOnUse">
<path d="m1.4 1.32c0-0.138-0.0547-0.271-0.153-0.37-0.098-0.0965-0.23-0.153-0.368-0.153h-0.432c-0.138 0-0.271 0.0563-0.37 0.153-0.098 0.098-0.153 0.231-0.153 0.37v1.81h1.47z" fill="#f5f9f0" stroke-width="1.61"></path>
</pattern>
</defs>
<!-- Board -->
<path d="m7.56 0c-4.19 0-7.56 3.37-7.56 7.56v181c0 4.19 3.37 7.56 7.56 7.56h91.5c4.19 0 7.56-3.37 7.56-7.56v-181c0-4.19-3.37-7.56-7.56-7.56h-91.5zm1.11 2.5a6.24 6.24 0 0 1 6.24 6.24 6.24 6.24 0 0 1-6.24 6.24 6.24 6.24 0 0 1-6.24-6.24 6.24 6.24 0 0 1 6.24-6.24zm88.9 0.217a6.24 6.24 0 0 1 6.24 6.24 6.24 6.24 0 0 1-6.24 6.24 6.24 6.24 0 0 1-6.24-6.24 6.24 6.24 0 0 1 6.24-6.24zm-88.9 176a6.24 6.24 0 0 1 0.141 0 6.24 6.24 0 0 1 6.24 6.24 6.24 6.24 0 0 1-6.24 6.24 6.24 6.24 0 0 1-6.24-6.24 6.24 6.24 0 0 1 6.1-6.24zm88.7 0.217a6.24 6.24 0 0 1 0.00195 0 6.24 6.24 0 0 1 0.139 0 6.24 6.24 0 0 1 6.24 6.24 6.24 6.24 0 0 1-6.24 6.24 6.24 6.24 0 0 1-6.24-6.24 6.24 6.24 0 0 1 6.1-6.24z" fill="#1a1a1a" fill-rule="evenodd"></path>
<!-- Pins -->
<rect transform="translate(0, 17.12)" width="2.54mm" height="38.1mm" fill="url(#pin-pattern)" />
<rect transform="translate(96.5, 17.12)" width="2.54mm" height="38.1mm" fill="url(#pin-pattern)" />
<!-- ESP32 Chip -->
<rect transform="translate(85,34)" width="4.8" height="55" fill="url(#esp-pin-pattern)"></rect>
<rect transform="translate(20.5,87) rotate(180)" width="4.8" height="55" fill="url(#esp-pin-pattern)"></rect>
<rect transform="translate(80,98) rotate(90)" width="4.8" height="55" fill="url(#esp-pin-pattern)"></rect>
<rect x="20" y="24.8" width="65.6" height="73.3" fill="#808080" fill-rule="evenodd"></rect>
<!-- Regulator -->
<g fill="#ececec" fill-rule="evenodd">
<rect x="19.3" y="143" width="7.21" height="11.5"></rect>
<rect x="39.8" y="139" width="6.59" height="3.07"></rect>
<rect x="40" y="147" width="6.59" height="3.07"></rect>
<rect x="40.2" y="156" width="6.59" height="3.07"></rect>
</g>
<rect x="26.3" y="137" width="14" height="24.7" fill="#4d4d4d" fill-rule="evenodd"></rect>
<!-- Buttons -->
<g stroke-width="1.24">
<rect x="77.6" y="177" width="11.1" height="9.96" fill="#cecccb"></rect>
<circle cx="83.2" cy="182" r="3.48" fill="#ffdc8e"></circle>
<g fill="#cecccb">
<path d="m80.7 190h-1.34v1.64c0 0.734 0.595 1.33 1.33 1.33h0.0099z"></path>
<path d="m80.7 175h-1.34v-1.64c0-0.734 0.595-1.33 1.33-1.33h0.0099z"></path>
<rect x="81" y="190" width="5.23" height="2.59"></rect>
<rect x="81" y="173" width="5.23" height="2.59"></rect>
<path d="m84.5 175c0.062 0 0.122 0.0248 0.166 0.0682 0.0434 0.0446 0.0682 0.104 0.0682 0.167 0 0.134 0.0533 0.263 0.149 0.358 0.0955 0.0942 0.224 0.148 0.358 0.148h0.0236c0.141 0 0.277-0.0558 0.376-0.155s0.155-0.234 0.155-0.374v-0.564h2.16v3.09h-1.69v0.744h-2.16v-0.392h-1.87v0.392h-2.16v-0.744h-1.69v-3.09h2.16v0.564c0 0.14 0.0558 0.275 0.155 0.374s0.234 0.155 0.376 0.155h0.0236c0.135 0 0.263-0.0533 0.358-0.148 0.0955-0.0955 0.149-0.224 0.149-0.358 0-0.0632 0.0248-0.123 0.0682-0.167 0.0446-0.0434 0.104-0.0682 0.167-0.0682z"></path>
<path d="m81.8 190c-0.0632 0-0.123-0.0248-0.167-0.0694-0.0434-0.0434-0.0682-0.103-0.0682-0.166 0-0.134-0.0533-0.263-0.149-0.358-0.0955-0.0955-0.223-0.149-0.358-0.149h-0.0236c-0.141 0-0.277 0.0558-0.376 0.156-0.0992 0.0992-0.155 0.234-0.155 0.374v0.564h-2.16v-3.09h1.69v-0.745h2.16v0.393h1.87v-0.393h2.16v0.745h1.69v3.09h-2.16v-0.564c0-0.14-0.0558-0.275-0.155-0.374-0.0992-0.1-0.234-0.156-0.376-0.156h-0.0236c-0.134 0-0.263 0.0533-0.358 0.149s-0.149 0.224-0.149 0.358c0 0.0632-0.0248 0.123-0.0682 0.166-0.0446 0.0446-0.104 0.0694-0.166 0.0694z"></path>
</g>
</g>
<g stroke-width="1.24">
<rect x="17.7" y="177" width="11.1" height="9.96" fill="#cecccb"></rect>
<circle cx="23.3" cy="182" r="3.48" fill="#ffdc8e"></circle>
<g fill="#cecccb">
<path d="m20.8 189h-1.34v1.64c0 0.734 0.595 1.33 1.33 1.33h0.0099z"></path>
<path d="m20.8 175h-1.34v-1.64c0-0.734 0.595-1.33 1.33-1.33h0.0099z"></path>
<rect x="21" y="189" width="5.23" height="2.59"></rect>
<rect x="21" y="172" width="5.23" height="2.59"></rect>
<path d="m24.5 175c0.062 0 0.122 0.0248 0.166 0.0682 0.0434 0.0446 0.0682 0.104 0.0682 0.167 0 0.134 0.0533 0.263 0.149 0.358 0.0955 0.0942 0.224 0.148 0.358 0.148h0.0236c0.141 0 0.277-0.0558 0.376-0.155s0.155-0.234 0.155-0.374v-0.564h2.16v3.09h-1.69v0.744h-2.16v-0.392h-1.87v0.392h-2.16v-0.744h-1.69v-3.09h2.16v0.564c0 0.14 0.0558 0.275 0.155 0.374s0.234 0.155 0.376 0.155h0.0236c0.135 0 0.263-0.0533 0.358-0.148 0.0955-0.0955 0.149-0.224 0.149-0.358 0-0.0632 0.0248-0.123 0.0682-0.167 0.0446-0.0434 0.104-0.0682 0.167-0.0682z"></path>
<path d="m21.9 189c-0.0632 0-0.123-0.0248-0.167-0.0694-0.0434-0.0434-0.0682-0.103-0.0682-0.166 0-0.134-0.0533-0.263-0.149-0.358-0.0955-0.0955-0.223-0.149-0.358-0.149h-0.0236c-0.141 0-0.277 0.0558-0.376 0.156-0.0992 0.0992-0.155 0.234-0.155 0.374v0.564h-2.16v-3.09h1.69v-0.745h2.16v0.393h1.87v-0.393h2.16v0.745h1.69v3.09h-2.16v-0.564c0-0.14-0.0558-0.275-0.155-0.374-0.0992-0.1-0.234-0.156-0.376-0.156h-0.0236c-0.134 0-0.263 0.0533-0.358 0.149s-0.149 0.224-0.149 0.358c0 0.0632-0.0248 0.123-0.0682 0.166-0.0446 0.0446-0.104 0.0694-0.166 0.0694z"></path>
</g>
</g>
<!-- USB Connection -->
<path d="m66.4 197 1.06 2.24c0.0651 0.142 0.0731 0.302 0.0207 0.448-0.0525 0.147-0.16 0.266-0.301 0.332-0.14 0.0665-0.302 0.0744-0.448 0.022-0.146-0.0525-0.266-0.16-0.332-0.301l-0.724-1.54-3e-3 0.207c-6e-3 0.488-0.206 0.955-0.556 1.3-0.35 0.342-0.821 0.529-1.31 0.522l-22.2-0.29c-0.489-6e-3 -0.955-0.206-1.3-0.556-0.341-0.35-0.529-0.821-0.522-1.31l3e-3 -0.207-0.764 1.52c-0.0701 0.14-0.192 0.244-0.34 0.292-0.147 0.0486-0.307 0.0365-0.446-0.0336l-1e-3 -1e-5c-0.138-0.0701-0.244-0.192-0.292-0.34-0.0486-0.147-0.0365-0.307 0.0336-0.447l1.11-2.21-0.602-8e-3 0.269-20.6 28.2 0.369-0.269 20.6z" fill="#cecccb" stroke-width="1.26"></path>
<path d="m60.7 177-0.0236 1.8c-7.9e-4 0.0607 0.0301 0.116 0.0802 0.148 0.522 0.329 3.38 2.12 3.38 2.12l-0.0217 1.66-1.74-0.0227-0.0156 1.19-2.63-0.0344 0.0156-1.19-1.66-0.0217 0.0413-3.16c2e-3 -0.136-0.0496-0.265-0.143-0.361-0.0948-0.096-0.223-0.151-0.357-0.152l-1.58-0.0207-0.0172 1.31-6.46-0.0845 0.0172-1.31-1.58-0.0207c-0.134-2e-3 -0.264 0.0496-0.36 0.143-0.0973 0.0936-0.152 0.221-0.154 0.357l-0.0413 3.16-1.66-0.0217-0.0156 1.19-2.63-0.0344 0.0156-1.19-1.74-0.0228 0.0217-1.66s2.91-1.73 3.43-2.03c0.0522-0.0309 0.0833-0.0848 0.0841-0.146l0.0236-1.8z" fill="#989898" stroke-width="1.26"></path>
<!-- LEDs -->
<g stroke-width="1.44">
<rect x="35" y="108" width="3.83" height="9.3" fill="#e5e5e5"></rect>
<rect x="35" y="110" width="3.83" height="5.31" fill="#f5ecde"></rect>
<rect x="69.5" y="108" width="3.83" height="9.3" fill="#e5e5e5"></rect>
<rect x="69.5" y="110" width="3.83" height="5.31" fill="#f5ecde"></rect>
</g>
<!-- Small Chip-->
<rect transform="translate(69,137)" width="13.9" height="3" fill="url(#small-pin-pattern)"></rect>
<rect transform="translate(82.8,160.5) rotate(180)" width="13.9" height="3" fill="url(#small-pin-pattern)"></rect>
<rect transform="translate(87.2,142) rotate(90)" width="13.9" height="3" fill="url(#small-pin-pattern)"></rect>
<rect transform="translate(64,155.8) rotate(270)" width="13.9" height="3" fill="url(#small-pin-pattern)"></rect>
<rect x="66.9" y="140" width="17.4" height="17.4" fill="#333" stroke-width="1.61"></rect>
<!-- Texts -->
<text fill="#ffffff" font-family="sans-serif" font-size="3.72px" transform="rotate(270)">
<tspan x="-160.21" y="12.8">VIN</tspan>
<tspan x="-151.57" y="12.8">GND</tspan>
<tspan x="-141.43" y="12.8">D13</tspan>
<tspan x="-131.41" y="12.8">D12</tspan>
<tspan x="-121.9" y="12.8">D14</tspan>
<tspan x="-112.82" y="12.8">D27</tspan>
<tspan x="-102.75" y="12.8">D26</tspan>
<tspan x="-93.604" y="12.8">D25</tspan>
<tspan x="-84.082" y="12.8">D33</tspan>
<tspan x="-73.939" y="12.8">D32</tspan>
<tspan x="-64.263" y="12.8">D35</tspan>
<tspan x="-54.385" y="12.8">D34</tspan>
<tspan x="-44.529" y="12.8">VN</tspan>
<tspan x="-35.205" y="12.8">VP</tspan>
<tspan x="-25.439" y="12.8">EN</tspan>
<tspan x="-161.01" y="95.8">3V3</tspan>
<tspan x="-151.84" y="95.8">GND</tspan>
<tspan x="-140.12" y="95.8">D15</tspan>
<tspan x="-129.96" y="95.8">D2</tspan>
<tspan x="-121.33" y="95.8">D4</tspan>
<tspan x="-113.75" y="95.8">RX2</tspan>
<tspan x="-104.04" y="95.8">TX2</tspan>
<tspan x="-93.3" y="95.8">D5</tspan>
<tspan x="-85.06" y="95.8">D18</tspan>
<tspan x="-75.215" y="95.8">D19</tspan>
<tspan x="-65.796" y="95.8">D21</tspan>
<tspan x="-55.802" y="95.8">RX0</tspan>
<tspan x="-45.850" y="95.8">TX0</tspan>
<tspan x="-36.582" y="95.8">D22</tspan>
<tspan x="-26.250" y="95.8">D23</tspan>
</text>
<text x="30" y="59" fill="#cecccb" font-family="sans-serif" font-size="15px">ESP32</text>
<!-- Antenna -->
<path d="m24.3 22.1v-18.8h8v11.5h10.2v-11h8.5v10.5h10v-10.5h17.8v20.2" fill="none" stroke="#4f4c48" stroke-width="1px"></path>
<path d="m69.7 4.16v19.5" fill="none" stroke="#4f4c48" stroke-width="1px"></path>
</svg>

After

Width:  |  Height:  |  Size: 9.8 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 60 KiB

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
/**
* ESP32 Web Components
*
* Uses the official wokwi-boards SVG assets for realistic board rendering.
* Pin positions are derived from board.json definitions (mm × 5 px/mm).
* Uses board SVG assets for realistic rendering.
* Pin positions are in mm × 5 px/mm.
*
* Supports three variants via the `board-kind` attribute:
* - esp32 ESP32 DevKit V1 (141 × 265 px)
@ -10,16 +10,19 @@
* - esp32-c3 ESP32-C3 DevKitM-1 (127 × 215 px)
*/
import esp32SvgUrl from '../../../../third-party/wokwi-boards/boards/esp32-devkit-v1/board.svg?url';
import esp32S3SvgUrl from '../../../../third-party/wokwi-boards/boards/esp32-s3-devkitc-1/board.svg?url';
import esp32C3SvgUrl from '../../../../third-party/wokwi-boards/boards/esp32-c3-devkitm-1/board.svg?url';
import esp32DevkitCV4SvgUrl from '../../../../third-party/wokwi-boards/boards/esp32-devkit-c-v4/board.svg?url';
import esp32CamSvgUrl from '../../../../third-party/wokwi-boards/boards/esp32-cam/board.svg?url';
import wemosLolin32SvgUrl from '../../../../third-party/wokwi-boards/boards/wemos-lolin32-lite/board.svg?url';
import xiaoEsp32S3SvgUrl from '../../../../third-party/wokwi-boards/boards/xiao-esp32-s3/board.svg?url';
import arduinoNanoEsp32SvgUrl from '../../../../third-party/wokwi-boards/boards/arduino-nano-esp32/board.svg?url';
import xiaoEsp32C3SvgUrl from '../../../../third-party/wokwi-boards/boards/xiao-esp32-c3/board.svg?url';
import aitewinC3SvgUrl from '../../../../third-party/wokwi-boards/boards/aitewinrobot-esp32c3-supermini/board.svg?url';
// Board SVG assets are served from frontend/public/boards/ as static URLs.
// No Vite import resolution needed — these resolve at runtime against the
// static asset root.
const esp32SvgUrl = '/boards/esp32-devkit-v1.svg';
const esp32S3SvgUrl = '/boards/esp32-s3.svg';
const esp32C3SvgUrl = '/boards/esp32-c3.svg';
const esp32DevkitCV4SvgUrl = '/boards/esp32-devkit-c-v4.svg';
const esp32CamSvgUrl = '/boards/esp32-cam.svg';
const wemosLolin32SvgUrl = '/boards/wemos-lolin32-lite.svg';
const xiaoEsp32S3SvgUrl = '/boards/xiao-esp32-s3.svg';
const arduinoNanoEsp32SvgUrl = '/boards/arduino-nano-esp32.svg';
const xiaoEsp32C3SvgUrl = '/boards/xiao-esp32-c3.svg';
const aitewinC3SvgUrl = '/boards/esp32c3-supermini.svg';
// ─── Pin positions (mm × 5 px/mm, from board.json) ───────────────────────────

View File

@ -1,11 +1,10 @@
/**
* Raspberry Pi Pico W Web Component
*
* Uses the official wokwi-boards SVG for the Pi Pico W.
* Dimensions: 20.9 mm × 52.75 mm 105 × 264 px (× 5 px/mm)
*/
import piPicoWSvgUrl from '../../../../third-party/wokwi-boards/boards/pi-pico-w/board.svg?url';
const piPicoWSvgUrl = '/boards/pi-pico-w.svg';
// Pi Pico W pins: 1.6 mm → x=8 (left), 19.3 mm → x=97 (right)
const PINS_PI_PICO_W = [

View File

@ -1112,11 +1112,11 @@ const WokwiLibsSection: React.FC = () => (
target="_blank"
rel="noopener noreferrer"
>
wokwi-elements
@wokwi/elements
</a>
</td>
<td>
<code>third-party/wokwi-elements/</code>
<code>npm</code>
</td>
<td>48+ Lit Web Components (LEDs, LCDs, sensors, buttons)</td>
</tr>
@ -1127,7 +1127,7 @@ const WokwiLibsSection: React.FC = () => (
</a>
</td>
<td>
<code>third-party/avr8js/</code>
<code>npm</code>
</td>
<td>ATmega328p / ATmega2560 CPU emulator at 16 MHz</td>
</tr>
@ -1138,46 +1138,36 @@ const WokwiLibsSection: React.FC = () => (
</a>
</td>
<td>
<code>third-party/rp2040js/</code>
<code>npm</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:
All three libraries are pulled directly from the npm registry by{' '}
<code>frontend/package.json</code>. Reference clones may live under{' '}
<code>third-party/</code> for credits and offline hacking, but the build
does not require them.
</p>
<CodeBlock language="typescript">{`resolve: {
alias: {
'avr8js': '../third-party/avr8js/dist/esm',
'@wokwi/elements': '../third-party/wokwi-elements/dist/esm',
},
}`}</CodeBlock>
<h2>Updating the Libraries</h2>
<h3>All at once (recommended)</h3>
<CodeBlock language="bash">{`# Windows
update-third-party.bat`}</CodeBlock>
<p>
Edit the version pins in <code>frontend/package.json</code>{' '}
(<code>@wokwi/elements</code>, <code>avr8js</code>, <code>rp2040js</code>)
and run <code>npm install</code> in <code>frontend/</code>.
</p>
<h3>Manually</h3>
<CodeBlock language="bash">{`cd third-party/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`}</CodeBlock>
<h3>After updating wokwi-elements</h3>
<p>Regenerate component metadata so new components appear in the picker:</p>
<CodeBlock language="bash">{`cd frontend
npx tsx ../scripts/generate-component-metadata.ts`}</CodeBlock>
<h3>After bumping wokwi-elements (only when adding new components)</h3>
<p>
The metadata generator scans the upstream <code>src/</code>, which the
npm package doesn&apos;t ship. Clone wokwi-elements once into{' '}
<code>third-party/</code> and regenerate:
</p>
<CodeBlock language="bash">{`git clone --depth=1 https://github.com/wokwi/wokwi-elements.git \\
third-party/wokwi-elements
cd frontend && npm run generate:metadata`}</CodeBlock>
<h2>Available Wokwi Components (48)</h2>
<table>
@ -1776,25 +1766,18 @@ const SetupSection: React.FC = () => (
</thead>
<tbody>
<tr>
<td>Components not displayed</td>
<td>
<code>Cannot find module &apos;@wokwi/elements&apos;</code> /{' '}
<code>&apos;avr8js&apos;</code>
</td>
<td>
<pre style={{ margin: 0 }}>
<code>cd third-party/wokwi-elements{'\n'}npm run build</code>
<code>cd frontend{'\n'}npm install</code>
</pre>
</td>
</tr>
<tr>
<td>
<code>Cannot find module 'avr8js'</code>
</td>
<td>
<pre style={{ margin: 0 }}>
<code>cd third-party/avr8js{'\n'}npm install && npm run build</code>
</pre>
</td>
</tr>
<tr>
<td>LED doesn't blink</td>
<td>LED doesn&apos;t blink</td>
<td>
Compile first, then click Run. Check pin assignment in the component property dialog.
</td>
@ -1802,9 +1785,9 @@ const SetupSection: React.FC = () => (
<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>
See &quot;After bumping wokwi-elements&quot; above clone
wokwi-elements into <code>third-party/</code> then run{' '}
<code>npm run generate:metadata</code>.
</td>
</tr>
</tbody>

View File

@ -9,7 +9,7 @@ import { AppHeader } from '../components/layout/AppHeader';
import { useSEO } from '../utils/useSEO';
import { getSeoMeta } from '../seoRoutes';
import { trackClickCTA } from '../utils/analytics';
import esp32C3SvgUrl from '../../../third-party/wokwi-boards/boards/esp32-c3-devkitm-1/board.svg?url';
const esp32C3SvgUrl = '/boards/esp32-c3.svg';
import './SEOPage.css';
const META = getSeoMeta('/esp32-c3-simulator')!;

View File

@ -9,7 +9,7 @@ import { AppHeader } from '../components/layout/AppHeader';
import { useSEO } from '../utils/useSEO';
import { getSeoMeta } from '../seoRoutes';
import { trackClickCTA } from '../utils/analytics';
import esp32S3SvgUrl from '../../../third-party/wokwi-boards/boards/esp32-s3-devkitc-1/board.svg?url';
const esp32S3SvgUrl = '/boards/esp32-s3.svg';
import './SEOPage.css';
const META = getSeoMeta('/esp32-s3-simulator')!;

View File

@ -9,7 +9,7 @@ import { AppHeader } from '../components/layout/AppHeader';
import { useSEO } from '../utils/useSEO';
import { getSeoMeta } from '../seoRoutes';
import { trackClickCTA } from '../utils/analytics';
import esp32SvgUrl from '../../../third-party/wokwi-boards/boards/esp32-devkit-v1/board.svg?url';
const esp32SvgUrl = '/boards/esp32-devkit-v1.svg';
import './SEOPage.css';
const META = getSeoMeta('/esp32-simulator')!;

View File

@ -9,7 +9,7 @@ import { AppHeader } from '../components/layout/AppHeader';
import { useSEO } from '../utils/useSEO';
import { getSeoMeta } from '../seoRoutes';
import { trackClickCTA } from '../utils/analytics';
import piPicoSvgUrl from '../../../third-party/wokwi-boards/boards/pi-pico/board.svg?url';
const piPicoSvgUrl = '/boards/pi-pico.svg';
import './SEOPage.css';
const META = getSeoMeta('/raspberry-pi-pico-simulator')!;

View File

@ -1,17 +1,11 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path'
// https://vite.dev/config/
// avr8js / rp2040js / @wokwi/elements are resolved from npm via package.json.
// (The third-party/ clones are reference-only — keep them updated for credits.)
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'avr8js': path.resolve(__dirname, '../third-party/avr8js/dist/esm'),
'rp2040js': path.resolve(__dirname, '../third-party/rp2040js/dist/esm'),
'@wokwi/elements': path.resolve(__dirname, '../third-party/wokwi-elements/dist/esm'),
},
},
server: {
proxy: {
'/api': {
@ -23,8 +17,6 @@ export default defineConfig({
assetsInclude: ['**/*.wasm'],
optimizeDeps: {
include: ['avr8js', 'rp2040js', '@wokwi/elements', 'littlefs'],
// Force Vite to re-bundle local wokwi-elements after adding new components
force: true,
},
test: {
globals: true,

View File

@ -1,19 +0,0 @@
# Configuración temporal SIN SSL para obtener certificado
server {
listen 80;
server_name velxio.dev www.velxio.dev;
# Let's Encrypt challenge
location /.well-known/acme-challenge/ {
root /var/www/html;
}
# Proxy temporal al contenedor
location / {
proxy_pass http://127.0.0.1:3080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

View File

@ -1,56 +0,0 @@
# Configuración de Nginx para velxio.dev
# Copiar a: /etc/nginx/sites-available/velxio.dev
# Luego: sudo ln -s /etc/nginx/sites-available/velxio.dev /etc/nginx/sites-enabled/
server {
listen 80;
server_name velxio.dev www.velxio.dev;
# Redirect HTTP to HTTPS
location / {
return 301 https://$host$request_uri;
}
# Let's Encrypt challenge
location /.well-known/acme-challenge/ {
root /var/www/html;
}
}
server {
listen 443 ssl http2;
server_name velxio.dev www.velxio.dev;
# SSL certificates (generados por certbot)
ssl_certificate /etc/letsencrypt/live/velxio.dev/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/velxio.dev/privkey.pem;
# SSL configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Proxy to Docker container
location / {
proxy_pass http://127.0.0.1:3080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 300s;
proxy_connect_timeout 75s;
proxy_buffering off;
}
}

View File

@ -109,11 +109,19 @@ class MetadataGenerator {
async generate(): Promise<void> {
console.log('🔍 Scanning wokwi-elements directory...');
// Source-only step: needs the wokwi-elements TypeScript src/ to scan
// @property decorators. The npm package ships dist/ only, so this only
// works when the upstream repo has been cloned into third-party/. If it
// isn't there, fall back to the committed JSON — adding new components
// requires the clone, but day-to-day dev does not.
if (!fs.existsSync(this.wokwiElementsPath)) {
console.error(`❌ wokwi-elements not found at: ${this.wokwiElementsPath}`);
console.log('💡 Make sure to initialize the git submodule:');
console.log(' git submodule update --init --recursive');
process.exit(1);
console.log(` wokwi-elements src/ not found at: ${this.wokwiElementsPath}`);
console.log(' Skipping metadata regeneration — using the committed');
console.log(' frontend/public/components-metadata.json as-is.');
console.log(' To regenerate (only needed when adding new components):');
console.log(' git clone https://github.com/wokwi/wokwi-elements.git \\');
console.log(' third-party/wokwi-elements');
return;
}
const components: ComponentMetadata[] = [];

View File

@ -23,7 +23,25 @@ const Module = require('module');
const path = require('path');
const fs = require('fs');
const CJS_DIR = path.resolve(__dirname, '../third-party/wokwi-elements/dist/cjs');
// Resolve @wokwi/elements from frontend/node_modules (preferred — installed
// from npm) with a fallback to the third-party/ clone if someone built the
// dist locally for development. Either way we want the CJS dist.
function findCjsDir() {
const candidates = [
path.resolve(__dirname, '../frontend/node_modules/@wokwi/elements/dist/cjs'),
path.resolve(__dirname, '../node_modules/@wokwi/elements/dist/cjs'),
path.resolve(__dirname, '../third-party/wokwi-elements/dist/cjs'),
];
for (const p of candidates) {
if (fs.existsSync(p)) return p;
}
console.error('[generate-component-svgs] Could not find @wokwi/elements/dist/cjs.');
console.error('Tried:\n ' + candidates.join('\n '));
console.error('Run `npm install` in frontend/ first.');
process.exit(1);
}
const CJS_DIR = findCjsDir();
const OUT_DIR = path.resolve(__dirname, '../frontend/public/component-svgs');
const BOARDS_DIR = path.resolve(__dirname, '../frontend/public/boards');

View File

@ -1,42 +0,0 @@
@echo off
echo ========================================
echo Actualizando Wokwi Libraries
echo ========================================
echo.
cd third-party
echo [1/3] Actualizando wokwi-elements...
cd wokwi-elements
git pull origin main
call npm install --legacy-peer-deps
call npm run build
cd ..
echo.
echo [2/3] Actualizando avr8js...
cd avr8js
git pull origin main
call npm install --legacy-peer-deps
call npm run build
cd ..
echo.
echo [3/3] Actualizando rp2040js...
cd rp2040js
git pull origin main
call npm install --legacy-peer-deps
call npm run build
cd ..
cd ..
echo.
echo ========================================
echo Actualizacion completada!
echo ========================================
echo.
echo Los repositorios de Wokwi han sido actualizados.
echo Puedes reiniciar el servidor de desarrollo si estaba corriendo.
echo.
pause