feat(editor): M5Stack section (Cardputer ADV, Core) ahead of STM32 in the starter dialog
The New Project dialog now lists an M5Stack section — M5 Cardputer ADV first, then M5Stack Core — between ESP32 and STM32. Both kinds are overlay-registered, so the section only renders when the overlay mounted (an OSS build shows no empty block). Selecting a card loads the board's on-LCD hello example (cardputer-adv-hello / m5stack-core-m5-helloworld), which also provides the card thumbnail. The sections builder is extracted as buildStarterSections(defs) and covered by a unit test that pins the display order.
This commit is contained in:
parent
15126cd27a
commit
0e107fafe6
|
|
@ -0,0 +1,54 @@
|
|||
/**
|
||||
* NewProjectDialog starter sections — display order and overlay cards.
|
||||
*
|
||||
* The dialog hardcodes its sections; overlay-registered kinds (M5Stack,
|
||||
* XIAO C6, XIAO RP2040) surface only when the private overlay registered
|
||||
* them. Guards the operator's requested order: M5Stack (Cardputer ADV, then
|
||||
* Core) sits ahead of STM32, and an OSS build shows no empty M5Stack block.
|
||||
*/
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { buildStarterSections } from '../components/editor/NewProjectDialog';
|
||||
import type { ProBoardDef } from '../lib/proBoardRegistry';
|
||||
|
||||
const def = (kind: string, label: string, description: string): ProBoardDef => ({
|
||||
kind,
|
||||
label,
|
||||
description,
|
||||
fqbn: null,
|
||||
tag: `velxio-${kind}`,
|
||||
size: { w: 100, h: 100 },
|
||||
});
|
||||
|
||||
const titles = (defs: ProBoardDef[]) =>
|
||||
buildStarterSections(defs)
|
||||
.filter((s) => s.entries.length > 0)
|
||||
.map((s) => s.title);
|
||||
|
||||
describe('NewProjectDialog starter sections', () => {
|
||||
it('OSS build (no overlay boards) shows no M5Stack section', () => {
|
||||
expect(titles([])).toEqual(['Arduino', 'ESP32', 'STM32', 'Raspberry Pi']);
|
||||
});
|
||||
|
||||
it('with the M5Stack overlay boards registered, M5Stack comes before STM32', () => {
|
||||
const defs = [
|
||||
def('m5stack-core', 'M5Stack Core', 'ESP32 all-in-one'),
|
||||
def('cardputer-adv', 'M5 Cardputer ADV', 'ESP32-S3 card computer'),
|
||||
];
|
||||
const order = titles(defs);
|
||||
expect(order).toEqual(['Arduino', 'ESP32', 'M5Stack', 'STM32', 'Raspberry Pi']);
|
||||
expect(order.indexOf('M5Stack')).toBeLessThan(order.indexOf('STM32'));
|
||||
|
||||
const m5 = buildStarterSections(defs).find((s) => s.title === 'M5Stack')!;
|
||||
// Cardputer ADV first, then the Core — regardless of registration order.
|
||||
expect(m5.entries.map((e) => e.kind)).toEqual(['cardputer-adv', 'm5stack-core']);
|
||||
// Card blurb comes from the registered definition, not a hardcoded string.
|
||||
expect(m5.entries[0].blurb).toBe('ESP32-S3 card computer');
|
||||
});
|
||||
|
||||
it('a partial overlay (only one M5 kind) still renders the section with that card', () => {
|
||||
const m5 = buildStarterSections([def('m5stack-core', 'M5Stack Core', 'x')]).find(
|
||||
(s) => s.title === 'M5Stack',
|
||||
)!;
|
||||
expect(m5.entries.map((e) => e.kind)).toEqual(['m5stack-core']);
|
||||
});
|
||||
});
|
||||
|
|
@ -4,9 +4,10 @@
|
|||
* Shown (a) on a pristine `/editor` visit (over an emptied canvas), and
|
||||
* (b) from the "New workspace" button / File menu entry. Offers a blank
|
||||
* workspace plus a ready-to-run Blink starter per board family — Arduino,
|
||||
* ESP32 (one card per chip generation, XIAO variant preferred), STM32,
|
||||
* Raspberry Pi — each card carrying the same circuit thumbnail the examples
|
||||
* gallery uses (/examples-thumbs/<id>.webp, CircuitPreview fallback).
|
||||
* ESP32 (one card per chip generation, XIAO variant preferred), M5Stack
|
||||
* (overlay-registered all-in-ones, listed ahead of STM32), STM32, Raspberry
|
||||
* Pi — each card carrying the same circuit thumbnail the examples gallery
|
||||
* uses (/examples-thumbs/<id>.webp, CircuitPreview fallback).
|
||||
*
|
||||
* Selecting a board loads its gallery Blink example when one exists (full
|
||||
* wiring: 220Ω resistor + LED); boards without one get a fresh board whose
|
||||
|
|
@ -29,6 +30,7 @@ import {
|
|||
listProBoards,
|
||||
subscribeProBoards,
|
||||
getProBoardsVersion,
|
||||
type ProBoardDef,
|
||||
} from '../../lib/proBoardRegistry';
|
||||
import { useSimulatorStore, DEFAULT_BOARD_POSITION } from '../../store/useSimulatorStore';
|
||||
import { useProjectStore } from '../../store/useProjectStore';
|
||||
|
|
@ -150,6 +152,10 @@ const PREFERRED_BLINK_EXAMPLE: Record<string, string> = {
|
|||
'stm32-f4-discovery': 'stm32-f4-discovery-blink',
|
||||
'stm32-olimex-h405': 'stm32-olimex-h405-blink',
|
||||
'stm32-netduino-plus2': 'stm32-netduino-plus2-blink',
|
||||
// M5Stack all-in-ones (overlay kinds): no LED to blink — the on-LCD hello
|
||||
// is the first run. Both ids carry a captured gallery thumb.
|
||||
'cardputer-adv': 'cardputer-adv-hello',
|
||||
'm5stack-core': 'm5stack-core-m5-helloworld',
|
||||
};
|
||||
|
||||
/** Dynamic import keeps the (large) gallery data out of the editor bundle
|
||||
|
|
@ -207,6 +213,62 @@ async function applyStarter(kind: string | 'blank'): Promise<void> {
|
|||
}
|
||||
}
|
||||
|
||||
export interface StarterSection {
|
||||
title: string;
|
||||
entries: Array<{ kind: string; blurb: string }>;
|
||||
}
|
||||
|
||||
/**
|
||||
* The card sections, in display order. Overlay-registered kinds only exist
|
||||
* when the private overlay mounted — an OSS build simply doesn't show those
|
||||
* cards, and a section left with no entries is not rendered at all.
|
||||
*/
|
||||
export function buildStarterSections(defs: ProBoardDef[]): StarterSection[] {
|
||||
const oss = (k: BoardKind) => ({ kind: k as string, blurb: BOARD_BLURBS[k] ?? '' });
|
||||
const pro = (k: string) => {
|
||||
const d = defs.find((x) => x.kind === k);
|
||||
return d ? [{ kind: d.kind, blurb: d.description }] : [];
|
||||
};
|
||||
return [
|
||||
{
|
||||
title: 'Arduino',
|
||||
entries: [
|
||||
oss('arduino-uno'),
|
||||
oss('arduino-mega'),
|
||||
oss('arduino-nano'),
|
||||
oss('arduino-nano-esp32'),
|
||||
],
|
||||
},
|
||||
// One card per ESP32 chip generation, XIAO variant preferred where
|
||||
// Seeed makes one: classic → DevKit V1, S3/C3 → XIAO, C6 → XIAO (overlay).
|
||||
{
|
||||
title: 'ESP32',
|
||||
entries: [
|
||||
oss('esp32'),
|
||||
oss('esp32-cam'),
|
||||
oss('xiao-esp32-s3'),
|
||||
oss('xiao-esp32-c3'),
|
||||
...pro('xiao-esp32c6'),
|
||||
],
|
||||
},
|
||||
// M5Stack all-in-ones (overlay): ahead of STM32 by operator request —
|
||||
// they run on Free and are a friendlier first pick than the Pro-gated
|
||||
// STM32 family. Cardputer first, then the Core.
|
||||
{ title: 'M5Stack', entries: [...pro('cardputer-adv'), ...pro('m5stack-core')] },
|
||||
{ title: 'STM32', entries: STM32_BOARDS.map(oss) },
|
||||
{
|
||||
title: 'Raspberry Pi',
|
||||
entries: [
|
||||
oss('raspberry-pi-pico'),
|
||||
...pro('xiao-rp2040'),
|
||||
oss('raspberry-pi-3'),
|
||||
oss('raspberry-pi-4'),
|
||||
oss('raspberry-pi-5'),
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const ProPill: React.FC = () => (
|
||||
<span className="new-project-pro" title="Pro feature — paid plan or Velxio Desktop">
|
||||
PRO
|
||||
|
|
@ -224,51 +286,8 @@ export const NewProjectDialog: React.FC<NewProjectDialogProps> = ({ isOpen, onCl
|
|||
getProBoardsVersion,
|
||||
);
|
||||
|
||||
const sections = useMemo(() => {
|
||||
const defs = listProBoards();
|
||||
const oss = (k: BoardKind) => ({ kind: k as string, blurb: BOARD_BLURBS[k] ?? '' });
|
||||
// Overlay-registered kinds only exist when the private overlay mounted —
|
||||
// an OSS build simply doesn't show those cards.
|
||||
const pro = (k: string) => {
|
||||
const d = defs.find((x) => x.kind === k);
|
||||
return d ? [{ kind: d.kind, blurb: d.description }] : [];
|
||||
};
|
||||
return [
|
||||
{
|
||||
title: 'Arduino',
|
||||
entries: [
|
||||
oss('arduino-uno'),
|
||||
oss('arduino-mega'),
|
||||
oss('arduino-nano'),
|
||||
oss('arduino-nano-esp32'),
|
||||
],
|
||||
},
|
||||
// One card per ESP32 chip generation, XIAO variant preferred where
|
||||
// Seeed makes one: classic → DevKit V1, S3/C3 → XIAO, C6 → XIAO (overlay).
|
||||
{
|
||||
title: 'ESP32',
|
||||
entries: [
|
||||
oss('esp32'),
|
||||
oss('esp32-cam'),
|
||||
oss('xiao-esp32-s3'),
|
||||
oss('xiao-esp32-c3'),
|
||||
...pro('xiao-esp32c6'),
|
||||
],
|
||||
},
|
||||
{ title: 'STM32', entries: STM32_BOARDS.map(oss) },
|
||||
{
|
||||
title: 'Raspberry Pi',
|
||||
entries: [
|
||||
oss('raspberry-pi-pico'),
|
||||
...pro('xiao-rp2040'),
|
||||
oss('raspberry-pi-3'),
|
||||
oss('raspberry-pi-4'),
|
||||
oss('raspberry-pi-5'),
|
||||
],
|
||||
},
|
||||
];
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [proBoardsVersion]);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const sections = useMemo(() => buildStarterSections(listProBoards()), [proBoardsVersion]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) return;
|
||||
|
|
|
|||
Loading…
Reference in New Issue