feat: add multi-board support section to Velxio 2.0 landing page and update sitemap generation script
parent
3e77c1a546
commit
1c1775c327
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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}`);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in New Issue