feat: enhance ExamplesPage to auto-install required libraries; update example projects with library dependencies
parent
dbf6bd986f
commit
d12a2ad7e4
|
|
@ -48,6 +48,8 @@ export interface ExampleProject {
|
|||
color: string;
|
||||
}>;
|
||||
thumbnail?: string;
|
||||
/** Arduino libraries required by this example (auto-installed when loading). */
|
||||
libraries?: string[];
|
||||
}
|
||||
|
||||
export const exampleProjects: ExampleProject[] = [
|
||||
|
|
@ -635,6 +637,7 @@ void loop() {
|
|||
id: 'tft-display',
|
||||
title: 'TFT ILI9341 Display',
|
||||
description: 'Color TFT display demo: fills, text, and a bouncing ball animation using the Adafruit ILI9341 library (240x320)',
|
||||
libraries: ['Adafruit GFX Library', 'Adafruit ILI9341'],
|
||||
category: 'displays',
|
||||
difficulty: 'intermediate',
|
||||
code: `// TFT ILI9341 Display Demo (240x320)
|
||||
|
|
@ -3134,7 +3137,8 @@ void loop() {
|
|||
{
|
||||
id: 'uno-dht22',
|
||||
title: 'Uno: DHT22 Temperature & Humidity',
|
||||
description: 'Read temperature and humidity using a DHT22 sensor on pin 7. Requires the Adafruit DHT sensor library.',
|
||||
description: 'Read temperature and humidity using a DHT22 sensor on pin 7.',
|
||||
libraries: ['DHT sensor library'],
|
||||
category: 'sensors',
|
||||
difficulty: 'beginner',
|
||||
boardFilter: 'arduino-uno',
|
||||
|
|
@ -3427,7 +3431,8 @@ void loop() {
|
|||
{
|
||||
id: 'pico-dht22',
|
||||
title: 'Pico: DHT22 Temperature & Humidity',
|
||||
description: 'Read temperature and humidity from a DHT22 sensor on GP7 using the Raspberry Pi Pico. Requires Adafruit DHT library.',
|
||||
description: 'Read temperature and humidity from a DHT22 sensor on GP7 using the Raspberry Pi Pico.',
|
||||
libraries: ['DHT sensor library'],
|
||||
category: 'sensors',
|
||||
difficulty: 'beginner',
|
||||
boardType: 'raspberry-pi-pico',
|
||||
|
|
@ -3705,7 +3710,8 @@ void loop() {
|
|||
{
|
||||
id: 'esp32-dht22',
|
||||
title: 'ESP32: DHT22 Temperature & Humidity',
|
||||
description: 'Read temperature and humidity from a DHT22 sensor on GPIO4 of the ESP32. Requires Adafruit DHT library.',
|
||||
description: 'Read temperature and humidity from a DHT22 sensor on GPIO4 of the ESP32.',
|
||||
libraries: ['DHT sensor library'],
|
||||
category: 'sensors',
|
||||
difficulty: 'beginner',
|
||||
boardType: 'esp32',
|
||||
|
|
@ -3803,7 +3809,8 @@ void loop() {
|
|||
{
|
||||
id: 'esp32-mpu6050',
|
||||
title: 'ESP32: MPU-6050 Accelerometer',
|
||||
description: 'Read 3-axis acceleration and gyroscope data from an MPU-6050 over I2C (SDA=D21, SCL=D22). Requires the Adafruit MPU6050 library.',
|
||||
description: 'Read 3-axis acceleration and gyroscope data from an MPU-6050 over I2C (SDA=D21, SCL=D22).',
|
||||
libraries: ['Adafruit MPU6050', 'Adafruit Unified Sensor'],
|
||||
category: 'sensors',
|
||||
difficulty: 'intermediate',
|
||||
boardType: 'esp32',
|
||||
|
|
@ -3908,6 +3915,7 @@ void loop() {
|
|||
id: 'esp32-servo',
|
||||
title: 'ESP32: Servo Motor + Potentiometer',
|
||||
description: 'Control a servo motor angle directly with a potentiometer. The servo follows the pot position in real time.',
|
||||
libraries: ['ESP32Servo'],
|
||||
category: 'robotics',
|
||||
difficulty: 'beginner',
|
||||
boardType: 'esp32',
|
||||
|
|
@ -4004,6 +4012,7 @@ void loop() {
|
|||
id: 'c3-dht22',
|
||||
title: 'ESP32-C3: DHT22 Temperature & Humidity',
|
||||
description: 'Read temperature and humidity with a DHT22 sensor on GPIO3 of the ESP32-C3 RISC-V board.',
|
||||
libraries: ['DHT sensor library'],
|
||||
category: 'sensors',
|
||||
difficulty: 'beginner',
|
||||
boardType: 'esp32-c3',
|
||||
|
|
@ -4148,6 +4157,7 @@ void loop() {
|
|||
id: 'c3-servo',
|
||||
title: 'ESP32-C3: Servo Motor Sweep',
|
||||
description: 'Sweep a servo motor from 0° to 180° and back on the ESP32-C3 using GPIO10 (PWM).',
|
||||
libraries: ['ESP32Servo'],
|
||||
category: 'robotics',
|
||||
difficulty: 'beginner',
|
||||
boardType: 'esp32-c3',
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
* Displays the examples gallery
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { ExamplesGallery } from '../components/examples/ExamplesGallery';
|
||||
import { AppHeader } from '../components/layout/AppHeader';
|
||||
|
|
@ -13,6 +13,7 @@ import { useEditorStore } from '../store/useEditorStore';
|
|||
import { useSimulatorStore } from '../store/useSimulatorStore';
|
||||
import { useVfsStore } from '../store/useVfsStore';
|
||||
import { isBoardComponent } from '../utils/boardPinMapping';
|
||||
import { getInstalledLibraries, installLibrary } from '../services/libraryService';
|
||||
import type { ExampleProject } from '../data/examples';
|
||||
import type { BoardKind } from '../types/board';
|
||||
|
||||
|
|
@ -27,9 +28,36 @@ export const ExamplesPage: React.FC = () => {
|
|||
const navigate = useNavigate();
|
||||
const { setCode } = useEditorStore();
|
||||
const { setComponents, setWires, setBoardType, activeBoardId, boards, addBoard, removeBoard, setActiveBoardId } = useSimulatorStore();
|
||||
const [installing, setInstalling] = useState<{ total: number; done: number; current: string } | null>(null);
|
||||
|
||||
const handleLoadExample = (example: ExampleProject) => {
|
||||
console.log('Loading example:', example.title);
|
||||
/** Install any missing libraries required by the example (non-blocking UI). */
|
||||
const ensureLibraries = async (libs: string[]): Promise<void> => {
|
||||
if (libs.length === 0) return;
|
||||
try {
|
||||
const installed = await getInstalledLibraries();
|
||||
const installedNames = new Set(
|
||||
installed.map((l) => (l.library?.name ?? l.name ?? '').toLowerCase())
|
||||
);
|
||||
const missing = libs.filter((l) => !installedNames.has(l.toLowerCase()));
|
||||
if (missing.length === 0) return;
|
||||
|
||||
setInstalling({ total: missing.length, done: 0, current: missing[0] });
|
||||
for (let i = 0; i < missing.length; i++) {
|
||||
setInstalling({ total: missing.length, done: i, current: missing[i] });
|
||||
await installLibrary(missing[i]);
|
||||
}
|
||||
setInstalling(null);
|
||||
} catch {
|
||||
// If install fails (e.g. offline), continue anyway — compile will show the error
|
||||
setInstalling(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleLoadExample = async (example: ExampleProject) => {
|
||||
// Auto-install required libraries before loading
|
||||
if (example.libraries && example.libraries.length > 0) {
|
||||
await ensureLibraries(example.libraries);
|
||||
}
|
||||
|
||||
if (example.boards && example.boards.length > 0) {
|
||||
// ── Multi-board loading ───────────────────────────────────────────────
|
||||
|
|
@ -150,6 +178,36 @@ export const ExamplesPage: React.FC = () => {
|
|||
<div style={{ display: 'flex', flexDirection: 'column', minHeight: '100vh', background: '#1e1e1e' }}>
|
||||
<AppHeader />
|
||||
<ExamplesGallery onLoadExample={handleLoadExample} />
|
||||
|
||||
{/* Library install overlay */}
|
||||
{installing && (
|
||||
<div style={{
|
||||
position: 'fixed', inset: 0, zIndex: 9999,
|
||||
background: 'rgba(0,0,0,0.7)', backdropFilter: 'blur(4px)',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
}}>
|
||||
<div style={{
|
||||
background: '#1e1e1e', border: '1px solid #333', borderRadius: 12,
|
||||
padding: '28px 36px', textAlign: 'center', maxWidth: 360,
|
||||
}}>
|
||||
<div style={{ fontSize: 14, color: '#ccc', marginBottom: 12 }}>
|
||||
Installing libraries ({installing.done + 1}/{installing.total})
|
||||
</div>
|
||||
<div style={{ fontSize: 16, color: '#00e5ff', fontWeight: 600, marginBottom: 16 }}>
|
||||
{installing.current}
|
||||
</div>
|
||||
<div style={{
|
||||
height: 4, borderRadius: 2, background: '#333', overflow: 'hidden',
|
||||
}}>
|
||||
<div style={{
|
||||
height: '100%', borderRadius: 2, background: '#00b8d4',
|
||||
width: `${((installing.done + 1) / installing.total) * 100}%`,
|
||||
transition: 'width 0.3s ease',
|
||||
}} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue