feat: add multi-board support section to Velxio 2.0 landing page and update sitemap generation script

pull/60/head
David Montero Crespo 2026-03-23 19:01:11 -03:00
parent 3e77c1a546
commit 1c1775c327
4 changed files with 184 additions and 1 deletions

View File

@ -8,7 +8,7 @@
"generate:metadata": "cd .. && npx tsx scripts/generate-component-metadata.ts",
"generate:favicons": "node ../scripts/generate-favicons.mjs",
"generate:og-image": "node ../scripts/generate-og-image.mjs",
"generate:sitemap": "cd .. && npx tsx scripts/generate-sitemap.ts",
"generate:sitemap": "npx tsx scripts/generate-sitemap.ts",
"dev": "npm run generate:metadata && vite",
"build": "npm run generate:metadata && npm run generate:sitemap && tsc -b && vite build",
"build:docker": "npm run generate:sitemap && vite build",

View File

@ -0,0 +1,62 @@
/**
* Auto-generates public/sitemap.xml from seoRoutes.ts
* Run: npx tsx scripts/generate-sitemap.ts
* Integrated into: npm run build (via generate:sitemap)
*/
import { writeFileSync } from 'fs';
import { resolve, dirname } from 'path';
import { fileURLToPath } from 'url';
// Import the single source of truth
import { SEO_ROUTES } from '../src/seoRoutes';
const __dirname_resolved = dirname(fileURLToPath(import.meta.url));
const DOMAIN = 'https://velxio.dev';
const TODAY = new Date().toISOString().slice(0, 10); // YYYY-MM-DD
const indexableRoutes = SEO_ROUTES.filter((r) => !r.noindex);
const xml = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
${indexableRoutes
.map(
(r) => `
<url>
<loc>${DOMAIN}${r.path}</loc>
<lastmod>${TODAY}</lastmod>
<changefreq>${r.changefreq ?? 'monthly'}</changefreq>
<priority>${r.priority ?? 0.5}</priority>
</url>`
)
.join('')}
</urlset>
`;
const outPath = resolve(__dirname_resolved, '../public/sitemap.xml');
writeFileSync(outPath, xml.trimStart(), 'utf-8');
console.log(`sitemap.xml generated → ${indexableRoutes.length} URLs (${TODAY})`);
// Also ping Google & Bing sitemap endpoints (non-blocking, best-effort)
const SITEMAP_URL = `${DOMAIN}/sitemap.xml`;
const PING_URLS = [
`https://www.google.com/ping?sitemap=${encodeURIComponent(SITEMAP_URL)}`,
`https://www.bing.com/ping?sitemap=${encodeURIComponent(SITEMAP_URL)}`,
];
if (process.argv.includes('--ping')) {
console.log('Pinging search engines...');
Promise.allSettled(
PING_URLS.map(async (url) => {
try {
const res = await fetch(url);
console.log(` ${res.ok ? 'OK' : 'FAIL'} ${url.split('?')[0]}`);
} catch (e: any) {
console.log(` FAIL ${url.split('?')[0]}: ${e.message}`);
}
})
);
}

View File

@ -175,6 +175,56 @@
white-space: nowrap;
}
/* ── Multi-board visual ──────────────────────────────── */
.v2-multiboard-visual {
display: flex;
align-items: center;
justify-content: center;
gap: 0;
flex-wrap: wrap;
padding: 32px 16px;
background: #121214;
border: 1px solid #1c1c1e;
border-radius: 12px;
}
.v2-mb-node {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
padding: 16px 20px;
border: 2px solid;
border-radius: 10px;
background: #0d0d0f;
transition: transform 0.15s, border-color 0.2s;
}
.v2-mb-node:hover {
transform: translateY(-3px);
}
.v2-mb-node img {
height: 56px;
width: auto;
object-fit: contain;
}
.v2-mb-node span {
font-size: 0.75rem;
font-weight: 600;
color: #e6edf3;
white-space: nowrap;
}
.v2-mb-wire {
flex-shrink: 0;
}
.v2-mb-wire svg {
display: block;
}
/* ── Changelog ───────────────────────────────────────── */
.v2-changelog {
display: grid;
@ -322,4 +372,16 @@
.v2-board-card img {
height: 48px;
}
.v2-mb-wire {
display: none;
}
.v2-multiboard-visual {
gap: 10px;
}
.v2-mb-node img {
height: 40px;
}
}

View File

@ -100,6 +100,16 @@ const IcoStar = () => (
</svg>
);
const IcoMultiBoard = () => (
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<rect x="2" y="3" width="8" height="7" rx="1.5" />
<rect x="14" y="3" width="8" height="7" rx="1.5" />
<rect x="8" y="14" width="8" height="7" rx="1.5" />
<path d="M6 10v2.5a1.5 1.5 0 0 0 1.5 1.5H8" />
<path d="M18 10v2.5a1.5 1.5 0 0 1-1.5 1.5H16" />
</svg>
);
const IcoRefresh = () => (
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M21.5 2v6h-6" />
@ -396,6 +406,55 @@ export const Velxio2Page: React.FC = () => {
</div>
</section>
{/* ── Multi-board ── */}
<section className="seo-section">
<h2>Multiple boards in one circuit</h2>
<p className="lead">
Most simulators limit you to one board at a time. Velxio lets you place multiple boards
on the same canvas and wire them together just like a real workbench.
</p>
<div className="v2-multiboard">
<div className="v2-multiboard-visual">
<div className="v2-mb-node" style={{ borderColor: '#c8701a' }}>
<img src="/boards/esp32-devkit-c-v4.svg" alt="ESP32" />
<span>ESP32</span>
</div>
<div className="v2-mb-wire">
<svg width="60" height="24" viewBox="0 0 60 24">
<path d="M0 12 H20 Q30 12 30 4 Q30 12 40 12 H60" stroke="#484848" strokeWidth="2" fill="none" strokeDasharray="4 3" />
</svg>
</div>
<div className="v2-mb-node" style={{ borderColor: '#a8192a' }}>
<img src="/boards/pi-pico.svg" alt="Raspberry Pi Pico" />
<span>Pi Pico</span>
</div>
<div className="v2-mb-wire">
<svg width="60" height="24" viewBox="0 0 60 24">
<path d="M0 12 H20 Q30 12 30 20 Q30 12 40 12 H60" stroke="#484848" strokeWidth="2" fill="none" strokeDasharray="4 3" />
</svg>
</div>
<div className="v2-mb-node" style={{ borderColor: '#0071e3' }}>
<img src="/boards/arduino-uno.svg" alt="Arduino Uno" />
<span>Arduino</span>
</div>
</div>
<div className="seo-grid" style={{ marginTop: 24 }}>
<div className="seo-card">
<h3>Mix architectures</h3>
<p>Connect an ESP32 to a Raspberry Pi 3, wire three Arduinos together, or combine a Pico with an ESP32-C3. Any combination works.</p>
</div>
<div className="seo-card">
<h3>Real inter-board communication</h3>
<p>Boards communicate through wired connections UART, I2C, SPI, or simple GPIO signals, just like real hardware.</p>
</div>
<div className="seo-card">
<h3>No other simulator does this</h3>
<p>Traditional simulators emulate one board in isolation. Velxio simulates entire systems with multiple boards running simultaneously.</p>
</div>
</div>
</div>
</section>
{/* ── Changelog ── */}
<section className="seo-section">
<h2>What's new in 2.0</h2>