feat: STM32 (Blue Pill / Black Pill) QEMU emulation + Pro board gating
STM32 emulation (open-core, runs via libqemu-arm in the backend worker):
- backend: stm32_lib_manager + stm32_worker (GPIO, USART, I2C/SPI device models
reusing the ESP32 slaves, live sensor updates), arduino_cli STM32 branch,
start_stm32 simulation route.
- frontend: Stm32Bridge + Stm32BluePill(/BlackPill) web components (Wokwi SVGs),
board kinds, Interconnect/boardPinMapping/boardProtocols wiring, example
projects (blink, serial, I2C BMP280/MPU6050/DS1307/SSD1306/weather, 7-seg,
RGB, button, switch, stepper, cross-board interconnect).
- Raspberry Pi 4/5 board elements + thumbnails.
Pro board gating (generic OSS->Pro seam; entitlement logic lives in the overlay):
- lib/proBoardGate.ts: isProBoardKind (STM32 + every QEMU Raspberry Pi),
installBoardGateImpl/boardGateDecision, triggerProUpgradePrompt.
- PRO badge on those boards in the component picker; gate at the picker add +
the run backstop (startBoard).
- backend/app/services/board_access.py: server-side enforcement seam for the
simulation WebSocket; STM32/Pi unavailable -> Pro-framed message.
- desktop: generic QemuDownloadPrompt + Stm32QemuPrompt (download-behind-license,
mirrors the ESP32 prompt).
- .gitignore: never ship libqemu-* binaries in the public image.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 05:06:14 +07:00
|
|
|
/**
|
|
|
|
|
* Pro board-gate registry.
|
|
|
|
|
*
|
|
|
|
|
* STM32 and the QEMU-backed Raspberry Pi family are Pro-only emulation
|
|
|
|
|
* features. The OSS app knows *which* boards are Pro (public information —
|
|
|
|
|
* `isProBoardKind`) and renders a PRO badge for them, but it does NOT know
|
|
|
|
|
* the current user's entitlement. The pro overlay installs the real gate via
|
|
|
|
|
* `installBoardGateImpl`, deciding whether a given add/run is allowed for the
|
|
|
|
|
* signed-in user on the web.
|
|
|
|
|
*
|
|
|
|
|
* Mirrors the other OSS->Pro seams (`proSaveAction.ts`, `proSession.ts`,
|
|
|
|
|
* `proRoutes.ts`): the OSS app defines a stable doorbell; the overlay plugs in.
|
|
|
|
|
*
|
|
|
|
|
* - OSS without an overlay -> default impl returns 'allow'. Self-hosted
|
|
|
|
|
* deployments don't block in the UI; the missing emulation binary plus a
|
|
|
|
|
* Pro-framed backend message handle availability (see stm32_lib_manager /
|
|
|
|
|
* the Pi boot-image provider).
|
|
|
|
|
* - With the pro overlay -> installBoardGateImpl() returns 'block' for a
|
|
|
|
|
* non-paid user on the web, and the caller fires the upgrade prompt.
|
|
|
|
|
* - Desktop (Tauri) -> overlay returns 'allow'; the per-board QEMU
|
|
|
|
|
* download prompt (Stm32QemuPrompt / RaspberryPiQemuPrompt) handles it.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import type { BoardKind } from '../types/board';
|
|
|
|
|
import { isPiBoardKind, isStm32BoardKind } from '../types/board';
|
2026-08-26 00:22:29 +07:00
|
|
|
import { getProBoard } from './proBoardRegistry';
|
feat: STM32 (Blue Pill / Black Pill) QEMU emulation + Pro board gating
STM32 emulation (open-core, runs via libqemu-arm in the backend worker):
- backend: stm32_lib_manager + stm32_worker (GPIO, USART, I2C/SPI device models
reusing the ESP32 slaves, live sensor updates), arduino_cli STM32 branch,
start_stm32 simulation route.
- frontend: Stm32Bridge + Stm32BluePill(/BlackPill) web components (Wokwi SVGs),
board kinds, Interconnect/boardPinMapping/boardProtocols wiring, example
projects (blink, serial, I2C BMP280/MPU6050/DS1307/SSD1306/weather, 7-seg,
RGB, button, switch, stepper, cross-board interconnect).
- Raspberry Pi 4/5 board elements + thumbnails.
Pro board gating (generic OSS->Pro seam; entitlement logic lives in the overlay):
- lib/proBoardGate.ts: isProBoardKind (STM32 + every QEMU Raspberry Pi),
installBoardGateImpl/boardGateDecision, triggerProUpgradePrompt.
- PRO badge on those boards in the component picker; gate at the picker add +
the run backstop (startBoard).
- backend/app/services/board_access.py: server-side enforcement seam for the
simulation WebSocket; STM32/Pi unavailable -> Pro-framed message.
- desktop: generic QemuDownloadPrompt + Stm32QemuPrompt (download-behind-license,
mirrors the ESP32 prompt).
- .gitignore: never ship libqemu-* binaries in the public image.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 05:06:14 +07:00
|
|
|
|
|
|
|
|
export type BoardGateDecision = 'allow' | 'block';
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Static, public predicate: which board kinds are Pro emulation features.
|
|
|
|
|
* Used for the PRO badge (always shown for these) and as the precondition the
|
|
|
|
|
* overlay's gate checks. STM32 (libqemu-arm) + every QEMU Raspberry Pi
|
|
|
|
|
* (Zero/1/2/3/4/5; excludes the Pico, which is browser emulation).
|
|
|
|
|
*/
|
|
|
|
|
export function isProBoardKind(kind: BoardKind | string): boolean {
|
|
|
|
|
return isStm32BoardKind(kind) || isPiBoardKind(kind);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type BoardGateImpl = (kind: BoardKind) => BoardGateDecision;
|
|
|
|
|
|
|
|
|
|
let _impl: BoardGateImpl | null = null;
|
|
|
|
|
|
|
|
|
|
/** Installed by the pro overlay (mountPro). Pass null to clear (hot reload). */
|
|
|
|
|
export function installBoardGateImpl(impl: BoardGateImpl | null): void {
|
|
|
|
|
_impl = impl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Whether the pro overlay has installed a gate (else OSS default applies). */
|
|
|
|
|
export function hasBoardGateImpl(): boolean {
|
|
|
|
|
return _impl !== null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Decide whether adding/running a board of `kind` is allowed for the current
|
|
|
|
|
* user. Non-Pro boards are always allowed. For Pro boards, the overlay's impl
|
|
|
|
|
* decides; with no overlay the OSS default is 'allow'.
|
|
|
|
|
*/
|
|
|
|
|
export function boardGateDecision(kind: BoardKind): BoardGateDecision {
|
|
|
|
|
if (!isProBoardKind(kind)) return 'allow';
|
|
|
|
|
if (!_impl) return 'allow';
|
|
|
|
|
try {
|
|
|
|
|
return _impl(kind);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
// eslint-disable-next-line no-console
|
|
|
|
|
console.warn('[oss] board-gate impl threw:', err);
|
|
|
|
|
return 'allow';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Fire the Pro upgrade prompt. Dispatches the same CustomEvent the pro
|
|
|
|
|
* overlay's UpgradeGate listens for (`PRO_UPGRADE_EVENT` in
|
|
|
|
|
* proComponentInjector.ts). The event name is the stable contract — the OSS
|
|
|
|
|
* app does not import from the overlay.
|
|
|
|
|
*/
|
|
|
|
|
const PRO_UPGRADE_EVENT = 'velxio-pro-upgrade-prompt';
|
|
|
|
|
|
|
|
|
|
export function triggerProUpgradePrompt(featureName: string): void {
|
|
|
|
|
if (typeof window === 'undefined') return;
|
|
|
|
|
window.dispatchEvent(
|
|
|
|
|
new CustomEvent(PRO_UPGRADE_EVENT, { detail: { componentName: featureName } }),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-26 00:22:29 +07:00
|
|
|
/**
|
|
|
|
|
* Human label for the upgrade prompt.
|
|
|
|
|
*
|
|
|
|
|
* An overlay board is named by its own label, not by the family whose run
|
|
|
|
|
* path it borrows: `piFamily` routes the UNIHIKER M10 through the Raspberry
|
|
|
|
|
* Pi bridge, and the prompt told its user that "Raspberry Pi emulation is a
|
|
|
|
|
* Pro feature" about a board that is not a Raspberry Pi. The OSS families
|
|
|
|
|
* keep their family names — every kind in them really is one.
|
|
|
|
|
*/
|
feat: STM32 (Blue Pill / Black Pill) QEMU emulation + Pro board gating
STM32 emulation (open-core, runs via libqemu-arm in the backend worker):
- backend: stm32_lib_manager + stm32_worker (GPIO, USART, I2C/SPI device models
reusing the ESP32 slaves, live sensor updates), arduino_cli STM32 branch,
start_stm32 simulation route.
- frontend: Stm32Bridge + Stm32BluePill(/BlackPill) web components (Wokwi SVGs),
board kinds, Interconnect/boardPinMapping/boardProtocols wiring, example
projects (blink, serial, I2C BMP280/MPU6050/DS1307/SSD1306/weather, 7-seg,
RGB, button, switch, stepper, cross-board interconnect).
- Raspberry Pi 4/5 board elements + thumbnails.
Pro board gating (generic OSS->Pro seam; entitlement logic lives in the overlay):
- lib/proBoardGate.ts: isProBoardKind (STM32 + every QEMU Raspberry Pi),
installBoardGateImpl/boardGateDecision, triggerProUpgradePrompt.
- PRO badge on those boards in the component picker; gate at the picker add +
the run backstop (startBoard).
- backend/app/services/board_access.py: server-side enforcement seam for the
simulation WebSocket; STM32/Pi unavailable -> Pro-framed message.
- desktop: generic QemuDownloadPrompt + Stm32QemuPrompt (download-behind-license,
mirrors the ESP32 prompt).
- .gitignore: never ship libqemu-* binaries in the public image.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 05:06:14 +07:00
|
|
|
export function proBoardFeatureName(kind: BoardKind | string): string {
|
2026-08-26 00:22:29 +07:00
|
|
|
const def = typeof kind === 'string' ? getProBoard(kind) : undefined;
|
|
|
|
|
if (def) return `${def.label} emulation`;
|
feat: STM32 (Blue Pill / Black Pill) QEMU emulation + Pro board gating
STM32 emulation (open-core, runs via libqemu-arm in the backend worker):
- backend: stm32_lib_manager + stm32_worker (GPIO, USART, I2C/SPI device models
reusing the ESP32 slaves, live sensor updates), arduino_cli STM32 branch,
start_stm32 simulation route.
- frontend: Stm32Bridge + Stm32BluePill(/BlackPill) web components (Wokwi SVGs),
board kinds, Interconnect/boardPinMapping/boardProtocols wiring, example
projects (blink, serial, I2C BMP280/MPU6050/DS1307/SSD1306/weather, 7-seg,
RGB, button, switch, stepper, cross-board interconnect).
- Raspberry Pi 4/5 board elements + thumbnails.
Pro board gating (generic OSS->Pro seam; entitlement logic lives in the overlay):
- lib/proBoardGate.ts: isProBoardKind (STM32 + every QEMU Raspberry Pi),
installBoardGateImpl/boardGateDecision, triggerProUpgradePrompt.
- PRO badge on those boards in the component picker; gate at the picker add +
the run backstop (startBoard).
- backend/app/services/board_access.py: server-side enforcement seam for the
simulation WebSocket; STM32/Pi unavailable -> Pro-framed message.
- desktop: generic QemuDownloadPrompt + Stm32QemuPrompt (download-behind-license,
mirrors the ESP32 prompt).
- .gitignore: never ship libqemu-* binaries in the public image.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 05:06:14 +07:00
|
|
|
if (isStm32BoardKind(kind)) return 'STM32 emulation';
|
|
|
|
|
if (isPiBoardKind(kind)) return 'Raspberry Pi emulation';
|
|
|
|
|
return 'this board';
|
|
|
|
|
}
|