diff --git a/CLAUDE.md b/CLAUDE.md index 2063761..19f51c0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -243,6 +243,14 @@ Wire positions auto-update when components move via `updateWirePositions()`. - [frontend/src/pages/UserProfilePage.tsx](frontend/src/pages/UserProfilePage.tsx) - Profile with project grid - [frontend/src/pages/ProjectPage.tsx](frontend/src/pages/ProjectPage.tsx) - Loads project into editor +### Frontend - SEO & Public Files +- `frontend/index.html` — Full SEO meta tags, OG, Twitter Card, JSON-LD. **Domain is `https://velxio.dev`** — update if domain changes. +- `frontend/public/favicon.svg` — SVG chip favicon (scales to all sizes) +- `frontend/public/og-image.svg` — 1200×630 social preview image (OG/Twitter). Export as PNG for max compatibility. +- `frontend/public/robots.txt` — Allow all crawlers, points to sitemap +- `frontend/public/sitemap.xml` — All public routes with priorities +- `frontend/public/manifest.webmanifest` — PWA manifest, theme color `#007acc` + ### Docker & CI - [Dockerfile.standalone](Dockerfile.standalone) - Multi-stage Docker build - [.github/workflows/docker-publish.yml](.github/workflows/docker-publish.yml) - Publishes to GHCR + Docker Hub on push to master diff --git a/frontend/index.html b/frontend/index.html index 072a57e..1178b48 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -2,9 +2,206 @@ - - frontend + + + Velxio — Free Local Arduino Emulator | AVR8 · RP2040 · 48+ Components + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/frontend/package.json b/frontend/package.json index 29817ab..cbf2afe 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -5,6 +5,7 @@ "type": "module", "scripts": { "generate:metadata": "cd .. && npx tsx scripts/generate-component-metadata.ts", + "generate:favicons": "node ../scripts/generate-favicons.mjs", "dev": "npm run generate:metadata && vite", "build": "npm run generate:metadata && tsc -b && vite build", "build:docker": "vite build", diff --git a/frontend/public/android-chrome-192.png b/frontend/public/android-chrome-192.png new file mode 100644 index 0000000..6298f3d Binary files /dev/null and b/frontend/public/android-chrome-192.png differ diff --git a/frontend/public/android-chrome-512.png b/frontend/public/android-chrome-512.png new file mode 100644 index 0000000..07dd305 Binary files /dev/null and b/frontend/public/android-chrome-512.png differ diff --git a/frontend/public/apple-touch-icon.png b/frontend/public/apple-touch-icon.png new file mode 100644 index 0000000..96d47c6 Binary files /dev/null and b/frontend/public/apple-touch-icon.png differ diff --git a/frontend/public/favicon-16x16.png b/frontend/public/favicon-16x16.png new file mode 100644 index 0000000..59bdac4 Binary files /dev/null and b/frontend/public/favicon-16x16.png differ diff --git a/frontend/public/favicon-32x32.png b/frontend/public/favicon-32x32.png new file mode 100644 index 0000000..7d10170 Binary files /dev/null and b/frontend/public/favicon-32x32.png differ diff --git a/frontend/public/favicon-48x48.png b/frontend/public/favicon-48x48.png new file mode 100644 index 0000000..ffb1f6f Binary files /dev/null and b/frontend/public/favicon-48x48.png differ diff --git a/frontend/public/favicon.ico b/frontend/public/favicon.ico new file mode 100644 index 0000000..ca07804 Binary files /dev/null and b/frontend/public/favicon.ico differ diff --git a/frontend/public/favicon.svg b/frontend/public/favicon.svg new file mode 100644 index 0000000..43c9633 --- /dev/null +++ b/frontend/public/favicon.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/frontend/public/manifest.webmanifest b/frontend/public/manifest.webmanifest new file mode 100644 index 0000000..39618e4 --- /dev/null +++ b/frontend/public/manifest.webmanifest @@ -0,0 +1,51 @@ +{ + "name": "Velxio — Arduino Emulator", + "short_name": "Velxio", + "description": "Free local Arduino emulator with real AVR8 CPU emulation, 48+ electronic components, Monaco Editor, and Serial Monitor. No cloud, no latency.", + "start_url": "/editor", + "scope": "/", + "display": "standalone", + "orientation": "landscape", + "background_color": "#0d0d0f", + "theme_color": "#007acc", + "categories": ["developer", "education", "productivity"], + "lang": "en", + "icons": [ + { + "src": "/favicon-16x16.png", + "sizes": "16x16", + "type": "image/png" + }, + { + "src": "/favicon-32x32.png", + "sizes": "32x32", + "type": "image/png" + }, + { + "src": "/android-chrome-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "any" + }, + { + "src": "/android-chrome-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "any maskable" + }, + { + "src": "/favicon.svg", + "sizes": "any", + "type": "image/svg+xml", + "purpose": "any maskable" + } + ], + "screenshots": [ + { + "src": "/og-image.svg", + "sizes": "1200x630", + "type": "image/svg+xml", + "label": "Velxio Arduino Emulator" + } + ] +} diff --git a/frontend/public/og-image.svg b/frontend/public/og-image.svg new file mode 100644 index 0000000..e4a8f89 --- /dev/null +++ b/frontend/public/og-image.svg @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OPEN SOURCE · FREE · LOCAL + + + + + + + + + + + + + + + + + VELXIO + + + + Arduino Emulator — Real AVR8 · 48+ Components · Monaco Editor + + + + + + + arduino-cli + + + avr8js + + + rp2040js + + + TypeScript + + + FastAPI + + + React + + + + + + + + github.com/davidmonterocrespo24/velxio + + diff --git a/frontend/public/robots.txt b/frontend/public/robots.txt new file mode 100644 index 0000000..25208a3 --- /dev/null +++ b/frontend/public/robots.txt @@ -0,0 +1,5 @@ +User-agent: * +Allow: / + +# Sitemap +Sitemap: https://velxio.dev/sitemap.xml diff --git a/frontend/public/sitemap.xml b/frontend/public/sitemap.xml new file mode 100644 index 0000000..964b4eb --- /dev/null +++ b/frontend/public/sitemap.xml @@ -0,0 +1,40 @@ + + + + + https://velxio.dev/ + 2026-03-06 + weekly + 1.0 + + + + https://velxio.dev/editor + 2026-03-06 + weekly + 0.9 + + + + https://velxio.dev/examples + 2026-03-06 + monthly + 0.7 + + + + https://velxio.dev/login + 2026-03-06 + yearly + 0.3 + + + + https://velxio.dev/register + 2026-03-06 + yearly + 0.3 + + + diff --git a/scripts/generate-favicons.mjs b/scripts/generate-favicons.mjs new file mode 100644 index 0000000..491e17c --- /dev/null +++ b/scripts/generate-favicons.mjs @@ -0,0 +1,77 @@ +/** + * Favicon generator — converts favicon.svg into all required sizes. + * Run from project root: node scripts/generate-favicons.mjs + * + * Generates: + * frontend/public/favicon-16x16.png + * frontend/public/favicon-32x32.png + * frontend/public/favicon-48x48.png + * frontend/public/apple-touch-icon.png (180x180) + * frontend/public/android-chrome-192.png + * frontend/public/android-chrome-512.png + * frontend/public/favicon.ico (16+32+48 multi-size) + */ + +import { readFileSync, writeFileSync } from 'fs'; +import { join, dirname } from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const ROOT = join(__dirname, '..'); +const PUBLIC = join(ROOT, 'frontend', 'public'); + +// ── install deps on the fly if missing ───────────────────────────── +import { createRequire } from 'module'; +const require = createRequire(import.meta.url); + +async function ensureDep(pkg) { + try { return await import(pkg); } catch {} + console.log(`Installing ${pkg}…`); + const { execSync } = await import('child_process'); + execSync(`npm install --no-save ${pkg}`, { stdio: 'inherit', cwd: ROOT }); + return await import(pkg); +} + +const { Resvg } = await ensureDep('@resvg/resvg-js'); +const pngToIcoMod = await ensureDep('png-to-ico'); +const pngToIco = pngToIcoMod.default ?? pngToIcoMod; + +// ── render helper ─────────────────────────────────────────────────── +const svgSrc = readFileSync(join(PUBLIC, 'favicon.svg')); + +function renderPng(size) { + const resvg = new Resvg(svgSrc, { + fitTo: { mode: 'width', value: size }, + font: { loadSystemFonts: false }, + }); + return resvg.render().asPng(); +} + +// ── generate PNGs ─────────────────────────────────────────────────── +const sizes = [ + { name: 'favicon-16x16.png', size: 16 }, + { name: 'favicon-32x32.png', size: 32 }, + { name: 'favicon-48x48.png', size: 48 }, + { name: 'apple-touch-icon.png', size: 180 }, + { name: 'android-chrome-192.png', size: 192 }, + { name: 'android-chrome-512.png', size: 512 }, +]; + +const pngBuffers = {}; +for (const { name, size } of sizes) { + const buf = renderPng(size); + writeFileSync(join(PUBLIC, name), buf); + pngBuffers[size] = buf; + console.log(`✓ ${name} (${size}x${size})`); +} + +// ── generate favicon.ico (16 + 32 + 48) ──────────────────────────── +const icoBuffer = await pngToIco([ + pngBuffers[16], + pngBuffers[32], + pngBuffers[48], +]); +writeFileSync(join(PUBLIC, 'favicon.ico'), icoBuffer); +console.log('✓ favicon.ico (16+32+48)'); + +console.log('\nAll favicon assets generated in frontend/public/');