Merge pull request #70 from davidmonterocrespo24/website

Website
pull/74/head
David Montero Crespo 2026-03-24 18:21:12 -03:00 committed by GitHub
commit 5cac073757
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 79 additions and 19 deletions

View File

@ -77,18 +77,10 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
RUN curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh \ RUN curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh \
| BINDIR=/usr/local/bin sh | BINDIR=/usr/local/bin sh
# Initialize arduino-cli config, add board manager URLs, then install cores # Only install arduino-cli binary here. Core installation (arduino:avr,
# - RP2040: earlephilhower fork for Raspberry Pi Pico # rp2040:rp2040, esp32:esp32) is done at first boot by entrypoint.sh
# - ESP32: Espressif official (covers ESP32, ESP32-S3, ESP32-C3, etc.) # and persisted in the mounted /root/.arduino15 volume.
RUN arduino-cli config init \ # This keeps the image small (~500MB vs ~2.5GB) and builds fast.
&& arduino-cli config add board_manager.additional_urls \
https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json \
&& arduino-cli config add board_manager.additional_urls \
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json \
&& arduino-cli core update-index \
&& arduino-cli core install arduino:avr \
&& arduino-cli core install rp2040:rp2040 \
&& arduino-cli core install esp32:esp32
WORKDIR /app WORKDIR /app

View File

@ -48,6 +48,8 @@ export interface ExampleProject {
color: string; color: string;
}>; }>;
thumbnail?: string; thumbnail?: string;
/** Arduino libraries required by this example (auto-installed when loading). */
libraries?: string[];
} }
export const exampleProjects: ExampleProject[] = [ export const exampleProjects: ExampleProject[] = [
@ -635,6 +637,7 @@ void loop() {
id: 'tft-display', id: 'tft-display',
title: 'TFT ILI9341 Display', title: 'TFT ILI9341 Display',
description: 'Color TFT display demo: fills, text, and a bouncing ball animation using the Adafruit ILI9341 library (240x320)', 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', category: 'displays',
difficulty: 'intermediate', difficulty: 'intermediate',
code: `// TFT ILI9341 Display Demo (240x320) code: `// TFT ILI9341 Display Demo (240x320)
@ -3134,7 +3137,8 @@ void loop() {
{ {
id: 'uno-dht22', id: 'uno-dht22',
title: 'Uno: DHT22 Temperature & Humidity', 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', category: 'sensors',
difficulty: 'beginner', difficulty: 'beginner',
boardFilter: 'arduino-uno', boardFilter: 'arduino-uno',
@ -3427,7 +3431,8 @@ void loop() {
{ {
id: 'pico-dht22', id: 'pico-dht22',
title: 'Pico: DHT22 Temperature & Humidity', 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', category: 'sensors',
difficulty: 'beginner', difficulty: 'beginner',
boardType: 'raspberry-pi-pico', boardType: 'raspberry-pi-pico',
@ -3705,7 +3710,8 @@ void loop() {
{ {
id: 'esp32-dht22', id: 'esp32-dht22',
title: 'ESP32: DHT22 Temperature & Humidity', 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', category: 'sensors',
difficulty: 'beginner', difficulty: 'beginner',
boardType: 'esp32', boardType: 'esp32',
@ -3803,7 +3809,8 @@ void loop() {
{ {
id: 'esp32-mpu6050', id: 'esp32-mpu6050',
title: 'ESP32: MPU-6050 Accelerometer', 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', category: 'sensors',
difficulty: 'intermediate', difficulty: 'intermediate',
boardType: 'esp32', boardType: 'esp32',
@ -3908,6 +3915,7 @@ void loop() {
id: 'esp32-servo', id: 'esp32-servo',
title: 'ESP32: Servo Motor + Potentiometer', title: 'ESP32: Servo Motor + Potentiometer',
description: 'Control a servo motor angle directly with a potentiometer. The servo follows the pot position in real time.', description: 'Control a servo motor angle directly with a potentiometer. The servo follows the pot position in real time.',
libraries: ['ESP32Servo'],
category: 'robotics', category: 'robotics',
difficulty: 'beginner', difficulty: 'beginner',
boardType: 'esp32', boardType: 'esp32',
@ -4004,6 +4012,7 @@ void loop() {
id: 'c3-dht22', id: 'c3-dht22',
title: 'ESP32-C3: DHT22 Temperature & Humidity', title: 'ESP32-C3: DHT22 Temperature & Humidity',
description: 'Read temperature and humidity with a DHT22 sensor on GPIO3 of the ESP32-C3 RISC-V board.', description: 'Read temperature and humidity with a DHT22 sensor on GPIO3 of the ESP32-C3 RISC-V board.',
libraries: ['DHT sensor library'],
category: 'sensors', category: 'sensors',
difficulty: 'beginner', difficulty: 'beginner',
boardType: 'esp32-c3', boardType: 'esp32-c3',
@ -4148,6 +4157,7 @@ void loop() {
id: 'c3-servo', id: 'c3-servo',
title: 'ESP32-C3: Servo Motor Sweep', title: 'ESP32-C3: Servo Motor Sweep',
description: 'Sweep a servo motor from 0° to 180° and back on the ESP32-C3 using GPIO10 (PWM).', description: 'Sweep a servo motor from 0° to 180° and back on the ESP32-C3 using GPIO10 (PWM).',
libraries: ['ESP32Servo'],
category: 'robotics', category: 'robotics',
difficulty: 'beginner', difficulty: 'beginner',
boardType: 'esp32-c3', boardType: 'esp32-c3',

View File

@ -4,7 +4,7 @@
* Displays the examples gallery * Displays the examples gallery
*/ */
import React from 'react'; import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { ExamplesGallery } from '../components/examples/ExamplesGallery'; import { ExamplesGallery } from '../components/examples/ExamplesGallery';
import { AppHeader } from '../components/layout/AppHeader'; import { AppHeader } from '../components/layout/AppHeader';
@ -13,6 +13,7 @@ import { useEditorStore } from '../store/useEditorStore';
import { useSimulatorStore } from '../store/useSimulatorStore'; import { useSimulatorStore } from '../store/useSimulatorStore';
import { useVfsStore } from '../store/useVfsStore'; import { useVfsStore } from '../store/useVfsStore';
import { isBoardComponent } from '../utils/boardPinMapping'; import { isBoardComponent } from '../utils/boardPinMapping';
import { getInstalledLibraries, installLibrary } from '../services/libraryService';
import type { ExampleProject } from '../data/examples'; import type { ExampleProject } from '../data/examples';
import type { BoardKind } from '../types/board'; import type { BoardKind } from '../types/board';
@ -27,9 +28,36 @@ export const ExamplesPage: React.FC = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const { setCode } = useEditorStore(); const { setCode } = useEditorStore();
const { setComponents, setWires, setBoardType, activeBoardId, boards, addBoard, removeBoard, setActiveBoardId } = useSimulatorStore(); 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) => { /** Install any missing libraries required by the example (non-blocking UI). */
console.log('Loading example:', example.title); 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) { if (example.boards && example.boards.length > 0) {
// ── Multi-board loading ─────────────────────────────────────────────── // ── Multi-board loading ───────────────────────────────────────────────
@ -150,6 +178,36 @@ export const ExamplesPage: React.FC = () => {
<div style={{ display: 'flex', flexDirection: 'column', minHeight: '100vh', background: '#1e1e1e' }}> <div style={{ display: 'flex', flexDirection: 'column', minHeight: '100vh', background: '#1e1e1e' }}>
<AppHeader /> <AppHeader />
<ExamplesGallery onLoadExample={handleLoadExample} /> <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> </div>
); );
}; };