velxio/frontend/src/lib/proSdCardGate.ts

50 lines
1.8 KiB
TypeScript
Raw Normal View History

feat(microsd): SD-over-SPI card storage for AVR, RP2040 and ESP32 Add a working microSD card part backed by a FAT16 image, following the Wokwi storage model: the project's own workspace files are auto-copied onto the card (free), and an optional "SD Card" panel uploads extra files (gated as a paid feature by the velxio.dev overlay; OSS default allows it). Frontend (in-browser AVR / RP2040): - ProtocolParts.ts: rewrite the microsd-card part from a handshake stub into a real SD-over-SPI device (reply-first Ncr timing, SDSC byte addressing, single/multi-block read+write, CSD/CID, full CMD set). - utils/fatImage.ts: dependency-free FAT16 super-floppy builder (8.3 + LFN). - utils/sdCardFiles.ts: assemble the card image from workspace files plus uploaded files; base64 helpers. - components/simulator/SdCardPanel.tsx + ComponentPropertyDialog: upload UI. - DynamicComponent + useSimulatorStore: build and inject the image on run. - lib/proSdCardGate.ts: overlay-installable gate for the upload action. - data/examples-storage-microsd.ts: Arduino Uno + ESP32 gallery examples. Backend (ESP32 via QEMU): - services/esp32_sd_slave.py: synchronous SD-over-SPI slave (Python port of the browser part) with a sparse backing store, idle-state R1 tracking and real CRC16 on data blocks when the host enables CRC (CMD59) -- both required by ESP-IDF's sdspi driver. - esp32_worker.py: route SPI bytes to the slave (returns MISO synchronously) and feed write-only bulk transfers. - esp32_lib_manager.py + routes/simulation.py: forward the FAT image (sd_card.image_b64) from the start config into the worker. Tested: - frontend: protocol-parts, fat-image, sd-card-gate and microsd-real-firmware (real Arduino SD.h on avr8js) -- 86 passing. - backend: test_esp32_sd_slave (10) covering the ESP-IDF init sequence and CRC16; validated end to end by running a real SD.h sketch in libqemu-xtensa (mount, directory listing, read and write-readback).
2026-06-11 08:59:53 +07:00
/**
* Pro microSD upload-gate registry.
*
* Uploading files to the microSD card (the "SD Card" panel) is a paid feature
* (any paid plan: Maker or above) mirrors Wokwi. The OSS app owns the panel
* UI but does NOT know the user's entitlement; the pro overlay installs the
* real gate (paid-subscriber check) via `installSdCardUploadGate`.
*
* Mirrors the other OSS->Pro seams (`proBoardGate.ts`, `proSaveAction.ts`):
* - OSS without an overlay -> default 'allow' (self-host has no accounts).
* - With the pro overlay -> the impl returns false for non-paid users on
* the web, and the caller fires the upgrade prompt.
*
* Note: this gates the UPLOAD only. Placing the component and the free
* auto-copy of project files are always allowed.
*/
let _impl: (() => boolean) | null = null;
/** Installed by the pro overlay (mountPro). Pass null to clear (hot reload). */
export function installSdCardUploadGate(impl: (() => boolean) | null): void {
_impl = impl;
}
/** Whether the current user may upload files to the microSD card. */
export function sdCardUploadAllowed(): boolean {
if (!_impl) return true; // OSS self-host: no accounts -> allow
try {
return _impl();
} catch (err) {
// eslint-disable-next-line no-console
console.warn('[oss] sd-card upload-gate impl threw:', err);
return true;
}
}
/**
* Fire the Pro upgrade prompt. Dispatches the stable CustomEvent the pro
* overlay's UpgradeGate listens for the OSS app never imports from the
* overlay, only the event name is the contract.
*/
export function triggerSdCardUpgradePrompt(): void {
if (typeof window === 'undefined') return;
window.dispatchEvent(
new CustomEvent('velxio-pro-upgrade-prompt', {
detail: { componentName: 'microSD file upload' },
}),
);
}