feat(sd): boards with a built-in microSD slot take user uploads
A ProBoardDef can declare builtInSdCsPin, and the store already built a FAT card from the project files for it - but there was no way to put YOUR files on that card: the upload panel only existed for the microsd-card component and for parts with metadata.sdSlot, and boards have no properties bag. Boards now carry sdFiles (same shape the SD panel persists everywhere else): - BoardInstance.sdFiles, consumed by the store's Run path (card component still wins when one is on the canvas); - the board inspector shows the SD Card panel whenever the board kind declares builtInSdCsPin, and routes the panel's onChange to updateBoard; - .vlx round-trips it (serialisableBoard + payload type), and gallery examples can pre-load the slot via ExampleBoard.sdFiles, applied by loadExample the way the panel would. First user: the XIAO ESP32S3 Sense (CS=GPIO21), whose board def and example land in the pro overlay.
This commit is contained in:
parent
c6256cc49a
commit
cfa03aeaf9
|
|
@ -4,7 +4,7 @@ import {
|
|||
getBoardBridge,
|
||||
getBoardSimulator,
|
||||
} from '../../store/useSimulatorStore';
|
||||
import { getBoardBuiltins } from '../../lib/proBoardRegistry';
|
||||
import { getBoardBuiltins, getProBoard } from '../../lib/proBoardRegistry';
|
||||
import { useElectricalStore } from '../../store/useElectricalStore';
|
||||
import { openDeviceGateway } from '../../lib/openDeviceGateway';
|
||||
import React, { useEffect, useState, useRef, useCallback } from 'react';
|
||||
|
|
@ -67,7 +67,7 @@ import {
|
|||
} from '../../utils/wireHitDetection';
|
||||
import { useIsCoarsePointer } from '../../utils/useTouchDevice';
|
||||
import type { ComponentMetadata } from '../../types/component-metadata';
|
||||
import type { BoardKind } from '../../types/board';
|
||||
import type { BoardKind, BoardInstance } from '../../types/board';
|
||||
import { BOARD_KIND_FQBN, boardDisplayName } from '../../types/board';
|
||||
import {
|
||||
boardGateDecision,
|
||||
|
|
@ -3405,9 +3405,13 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => {
|
|||
defaultValues: {},
|
||||
pinCount: boardPins?.length ?? 0,
|
||||
tags: [],
|
||||
// A board with a BUILT-IN microSD slot (XIAO Sense) gets the
|
||||
// same SD Card upload panel a component slot gets; the files
|
||||
// live on board.sdFiles and feed the card image on Run.
|
||||
sdSlot: getProBoard(board.boardKind)?.builtInSdCsPin !== undefined,
|
||||
} as unknown as ComponentMetadata
|
||||
}
|
||||
componentProperties={{}}
|
||||
componentProperties={{ sdFiles: board.sdFiles }}
|
||||
position={{ x: boardContextMenu.x, y: boardContextMenu.y }}
|
||||
pinInfo={boardPins || []}
|
||||
previewTagName={element?.tagName.toLowerCase()}
|
||||
|
|
@ -3421,6 +3425,14 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => {
|
|||
}
|
||||
wireInProgress={Boolean(wireInProgress)}
|
||||
onClose={() => setBoardContextMenu(null)}
|
||||
onPropertyChange={(id, propName, value) => {
|
||||
// The board dialog's only editable "property" is the SD
|
||||
// slot's upload list; it lives on the board, not in a
|
||||
// component's properties bag.
|
||||
if (propName === 'sdFiles') {
|
||||
updateBoard(id, { sdFiles: value as BoardInstance['sdFiles'] });
|
||||
}
|
||||
}}
|
||||
onDelete={(id) => {
|
||||
setBoardContextMenu(null);
|
||||
setBoardToRemove(id);
|
||||
|
|
|
|||
|
|
@ -32,6 +32,10 @@ export interface ExampleBoard {
|
|||
code?: string;
|
||||
/** Files pre-loaded into the Pi VFS (path → content). Only used for raspberry-pi-3. */
|
||||
vfsFiles?: Record<string, string>;
|
||||
/** Uploads pre-loaded onto a board's BUILT-IN microSD slot (a ProBoardDef
|
||||
* with builtInSdCsPin, e.g. the XIAO ESP32S3 Sense) — the same shape the
|
||||
* SD Card panel persists on board.sdFiles. */
|
||||
sdFiles?: Array<{ name: string; contentB64: string }>;
|
||||
}
|
||||
|
||||
export interface ExampleProject {
|
||||
|
|
|
|||
|
|
@ -2251,7 +2251,12 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
|
|||
const builtInSdCs = getProBoard(board.boardKind)?.builtInSdCsPin;
|
||||
if (sdCard || builtInSdCs !== undefined) {
|
||||
try {
|
||||
const uploaded = sdCard ? decodeSdFiles(sdCard.properties.sdFiles) : [];
|
||||
// Uploads come from the card component when one is on the
|
||||
// canvas, else from the BOARD's own slot (board.sdFiles - the
|
||||
// XIAO Sense pattern, same panel, persisted on the board).
|
||||
const uploaded = sdCard
|
||||
? decodeSdFiles(sdCard.properties.sdFiles)
|
||||
: decodeSdFiles(board.sdFiles);
|
||||
const image = buildProjectSdImage(useEditorStore.getState().files, uploaded);
|
||||
esp32Bridge.sdImageB64 = bytesToB64(image);
|
||||
esp32Bridge.sdCsPin = sdCard ? undefined : builtInSdCs;
|
||||
|
|
|
|||
|
|
@ -146,6 +146,12 @@ export interface BoardInstance {
|
|||
// Types live in `./boardOptions` to avoid a circular import.
|
||||
boardOptions?: import('./boardOptions').ESP32BoardOptions;
|
||||
spiffsFiles?: import('./boardOptions').SpiffsFile[];
|
||||
/** User uploads for a board's BUILT-IN microSD slot (a ProBoardDef with
|
||||
* builtInSdCsPin, e.g. the XIAO ESP32S3 Sense). Same shape the
|
||||
* microsd-card component persists in properties.sdFiles, and consumed the
|
||||
* same way: merged into buildProjectSdImage on Run, overriding same-named
|
||||
* project files. Undefined for boards without a slot. */
|
||||
sdFiles?: Array<{ name: string; contentB64: string }>;
|
||||
// P2.4 — this board's declared library manifest (its velxio.json). The ESP32
|
||||
// compile scope: each board resolves ONLY its own declared libraries, so two
|
||||
// boards in the same project can use different (even conflicting) libraries
|
||||
|
|
|
|||
|
|
@ -175,6 +175,12 @@ export async function loadExample(
|
|||
useEditorStore.getState().loadFiles([{ name: filename, content: eb.code }]);
|
||||
}
|
||||
|
||||
// Built-in SD slot uploads (XIAO Sense): land on the board exactly as
|
||||
// the SD Card panel would put them, so the Run path finds them.
|
||||
if (eb.sdFiles?.length) {
|
||||
useSimulatorStore.getState().updateBoard(boardId, { sdFiles: eb.sdFiles });
|
||||
}
|
||||
|
||||
if (eb.vfsFiles && isPiBoardKind(eb.boardKind)) {
|
||||
// Pi example scripts go into the board's REGULAR file group — they
|
||||
// are edited in Monaco like any other board's code, and the run
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ export interface VlxPayload {
|
|||
activeFileGroupId: string;
|
||||
languageMode?: string;
|
||||
serialBaudRate?: number;
|
||||
sdFiles?: Array<{ name: string; contentB64: string }>;
|
||||
}>;
|
||||
fileGroups: Record<string, Array<{ name: string; content: string }>>;
|
||||
components: Component[];
|
||||
|
|
@ -70,6 +71,9 @@ function serialisableBoard(b: BoardInstance) {
|
|||
activeFileGroupId: b.activeFileGroupId,
|
||||
languageMode: b.languageMode,
|
||||
serialBaudRate: b.serialBaudRate,
|
||||
// Built-in SD slot uploads (XIAO Sense etc.) travel with the project,
|
||||
// exactly like a microsd-card component's properties.sdFiles do.
|
||||
sdFiles: b.sdFiles,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue