diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js index 33063d18..df66cbd8 100644 --- a/frontend/eslint.config.js +++ b/frontend/eslint.config.js @@ -21,5 +21,24 @@ export default defineConfig([ ecmaVersion: 2020, globals: globals.browser, }, + rules: { + '@typescript-eslint/no-explicit-any': 'warn', + '@typescript-eslint/no-unused-vars': [ + 'warn', + { argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_' }, + ], + '@typescript-eslint/no-namespace': 'off', + '@typescript-eslint/no-empty-object-type': 'warn', + '@typescript-eslint/ban-ts-comment': 'warn', + 'react-hooks/set-state-in-effect': 'warn', + 'react-hooks/purity': 'warn', + 'no-unused-vars': 'off', + 'react-hooks/exhaustive-deps': 'warn', + 'react-refresh/only-export-components': 'warn', + 'no-empty': ['warn', { allowEmptyCatch: true }], + 'no-irregular-whitespace': 'warn', + 'no-control-regex': 'off', + 'prefer-const': 'warn', + }, }, ]) diff --git a/frontend/src/__tests__/attiny85-simulation.test.ts b/frontend/src/__tests__/attiny85-simulation.test.ts index d5fdacf7..1f5892d5 100644 --- a/frontend/src/__tests__/attiny85-simulation.test.ts +++ b/frontend/src/__tests__/attiny85-simulation.test.ts @@ -39,10 +39,7 @@ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import { spawnSync } from 'child_process'; -import { - mkdtempSync, writeFileSync, readFileSync, - rmSync, mkdirSync, readdirSync, -} from 'fs'; +import { mkdtempSync, writeFileSync, readFileSync, rmSync, mkdirSync, readdirSync } from 'fs'; import { tmpdir } from 'os'; import { join } from 'path'; @@ -56,7 +53,11 @@ beforeEach(() => { let counter = 0; let depth = 0; vi.stubGlobal('requestAnimationFrame', (cb: FrameRequestCallback) => { - if (depth === 0) { depth++; cb(0); depth--; } + if (depth === 0) { + depth++; + cb(0); + depth--; + } return ++counter; }); vi.stubGlobal('cancelAnimationFrame', vi.fn()); @@ -77,18 +78,14 @@ const EMPTY_HEX = ':00000001FF\n'; * TINY85_BLINK_HEX — sets PB1 HIGH then loops. * Uses ATtiny85 DDRB (I/O 0x17 → mem 0x37) and PORTB (I/O 0x18 → mem 0x38). */ -const TINY85_BLINK_HEX = - ':0A0000000FEF07BB02E008BBFFCFC3\n' + - ':00000001FF\n'; +const TINY85_BLINK_HEX = ':0A0000000FEF07BB02E008BBFFCFC3\n' + ':00000001FF\n'; /** * TINY85_PB0_HEX — sets PB0 HIGH (bit 0). * LDI r16, 0xFF → OUT DDRB → LDI r16, 0x01 → OUT PORTB → RJMP .-2 * Checksum: 0xC4 */ -const TINY85_PB0_HEX = - ':0A0000000FEF07BB01E008BBFFCFC4\n' + - ':00000001FF\n'; +const TINY85_PB0_HEX = ':0A0000000FEF07BB01E008BBFFCFC4\n' + ':00000001FF\n'; /** * TINY85_PWM_HEX — writes 0x80 to OCR0B (I/O 0x3C → mem 0x5C) to test PWM. @@ -99,9 +96,7 @@ const TINY85_PB0_HEX = * word = 1011 1 11 10000 1100 = 0xBF0C → bytes 0x0C 0xBF * Checksum: 0xB5 */ -const TINY85_PWM_HEX = - ':0A0000000FEF07BB00E80CBFFFCFB5\n' + - ':00000001FF\n'; +const TINY85_PWM_HEX = ':0A0000000FEF07BB00E80CBFFFCFB5\n' + ':00000001FF\n'; // ─── Lifecycle ──────────────────────────────────────────────────────────────── @@ -279,11 +274,11 @@ describe('ATtiny85 — PWM monitoring', () => { sim.loadHex(EMPTY_HEX); const pwmCb = vi.fn(); - pm.onPwmChange(1, pwmCb); // PB1 = OCR0B pin + pm.onPwmChange(1, pwmCb); // PB1 = OCR0B pin // Directly write a PWM value to OCR0B in CPU data memory const cpu = (sim as any).cpu; - cpu.data[0x5C] = 128; // 50% duty cycle + cpu.data[0x5c] = 128; // 50% duty cycle sim.start(); sim.stop(); @@ -297,7 +292,7 @@ describe('ATtiny85 — PWM monitoring', () => { sim.loadHex(EMPTY_HEX); const pwmCb = vi.fn(); - pm.onPwmChange(0, pwmCb); // PB0 = OCR0A pin + pm.onPwmChange(0, pwmCb); // PB0 = OCR0A pin const cpu = (sim as any).cpu; cpu.data[0x56] = 64; @@ -311,9 +306,9 @@ describe('ATtiny85 — PWM monitoring', () => { it('ATtiny85 PWM covers 4 pins (OCR0A/OCR0B/OCR1A/OCR1B)', () => { const TINY85_PWM_MAP = [ { addr: 0x56, pin: 0 }, // OCR0A → PB0 - { addr: 0x5C, pin: 1 }, // OCR0B → PB1 - { addr: 0x4E, pin: 1 }, // OCR1A → PB1 - { addr: 0x4B, pin: 4 }, // OCR1B → PB4 + { addr: 0x5c, pin: 1 }, // OCR0B → PB1 + { addr: 0x4e, pin: 1 }, // OCR1A → PB1 + { addr: 0x4b, pin: 4 }, // OCR1B → PB4 ]; const pm = new PinManager(); @@ -408,17 +403,21 @@ describe('ATtiny85 — end-to-end compilation', () => { const tmpDir = mkdtempSync(join(tmpdir(), 'attiny85-e2e-')); const sketchDir = join(tmpDir, 'sketch'); mkdirSync(sketchDir); - writeFileSync(join(sketchDir, 'sketch.ino'), ` + writeFileSync( + join(sketchDir, 'sketch.ino'), + ` void setup() { pinMode(1, OUTPUT); digitalWrite(1, HIGH); } void loop() {} -`); +`, + ); try { const result = spawnSync( - 'arduino-cli', ['compile', '--fqbn', FQBN, '--output-dir', sketchDir, sketchDir], + 'arduino-cli', + ['compile', '--fqbn', FQBN, '--output-dir', sketchDir, sketchDir], { encoding: 'utf8', timeout: 60000 }, ); expect(result.status).toBe(0); diff --git a/frontend/src/__tests__/spice-mosfet-diag.test.ts b/frontend/src/__tests__/spice-mosfet-diag.test.ts index c37ad218..298c5927 100644 --- a/frontend/src/__tests__/spice-mosfet-diag.test.ts +++ b/frontend/src/__tests__/spice-mosfet-diag.test.ts @@ -78,11 +78,11 @@ describe('MOSFET PWM LED — scheduler key diagnostic', () => { { timeout: 30_000 }, async () => { const { netlist } = buildNetlist(buildInput(1.0)); // 100% duty = full on - + console.log('\n=== NETLIST ===\n' + netlist + '\n=============='); const cooked = await runNetlist(netlist); - + console.log('RAW variableNames:', cooked.variableNames); // Mirror CircuitScheduler.drain() exactly @@ -94,9 +94,9 @@ describe('MOSFET PWM LED — scheduler key diagnostic', () => { if (Number.isFinite(i)) branchCurrents[src] = i; } } - + console.log('branchCurrents keys (scheduler-filtered):', Object.keys(branchCurrents)); - + console.log('v_led1_sense:', branchCurrents['v_led1_sense']); expect(Object.keys(branchCurrents)).toContain('v_led1_sense'); diff --git a/frontend/src/__tests__/spice-npn-switch-diag.test.ts b/frontend/src/__tests__/spice-npn-switch-diag.test.ts index 4310fe6a..fb9e96a8 100644 --- a/frontend/src/__tests__/spice-npn-switch-diag.test.ts +++ b/frontend/src/__tests__/spice-npn-switch-diag.test.ts @@ -89,9 +89,9 @@ async function solveAndGetLedCurrent( describe('NPN Transistor Switch — diagnostic', () => { it('pin 9 HIGH: LED conducts ~20 mA', { timeout: 30_000 }, async () => { const { current, netlist } = await solveAndGetLedCurrent('HIGH'); - + console.log('\n=== NETLIST (pin9=HIGH) ===\n' + netlist); - + console.log('LED current (HIGH):', current); expect(current).toBeGreaterThan(5e-3); }); @@ -101,9 +101,9 @@ describe('NPN Transistor Switch — diagnostic', () => { { timeout: 30_000 }, async () => { const { current, netlist } = await solveAndGetLedCurrent('LOW'); - + console.log('\n=== NETLIST (pin9=LOW) ===\n' + netlist); - + console.log('LED current (LOW):', current); // If this fails with >1µA, we have reproduced the bug. expect(current).toBeLessThan(1e-6); diff --git a/frontend/src/__tests__/spice-npn-switch-integration.test.ts b/frontend/src/__tests__/spice-npn-switch-integration.test.ts index c248359c..47e440b8 100644 --- a/frontend/src/__tests__/spice-npn-switch-integration.test.ts +++ b/frontend/src/__tests__/spice-npn-switch-integration.test.ts @@ -148,20 +148,20 @@ describe('NPN LED switch — integration pipeline', () => { avr.runCycles(1); if (pm.getPinState(9)) highCycle = i; } - + console.log('pin 9 went HIGH after', highCycle, 'cycles'); expect(highCycle).toBeGreaterThanOrEqual(0); // Solve with pin 9 HIGH and assert LED conducts. const highPins = collectPinStates(pm, npnSwitchWires()); - + console.log('pinStates after HIGH write:', highPins); expect(highPins['9']).toEqual({ type: 'digital', v: 5 }); const { netlist: nlHigh } = buildNetlist(npnSwitchInput(highPins)); const cookedHigh = await runNetlist(nlHigh); const iHigh = Math.abs(cookedHigh.dcValue('i(v_led1_sense)')); - + console.log('HIGH: LED current =', iHigh, 'A'); expect(iHigh).toBeGreaterThan(5e-3); @@ -171,21 +171,21 @@ describe('NPN LED switch — integration pipeline', () => { avr.runCycles(1); if (!pm.getPinState(9)) lowCycle = i; } - + console.log('pin 9 went LOW after', lowCycle, 'additional cycles'); expect(lowCycle).toBeGreaterThanOrEqual(0); const lowPins = collectPinStates(pm, npnSwitchWires()); - + console.log('pinStates after LOW write:', lowPins); expect(lowPins['9']).toBeUndefined(); const { netlist: nlLow } = buildNetlist(npnSwitchInput(lowPins)); - + console.log('\n=== NETLIST (after AVR LOW) ===\n' + nlLow); const cookedLow = await runNetlist(nlLow); const iLow = Math.abs(cookedLow.dcValue('i(v_led1_sense)')); - + console.log('LOW: LED current =', iLow, 'A'); expect(iLow).toBeLessThan(1e-6); }, diff --git a/frontend/src/components/examples/ExamplesGallery.tsx b/frontend/src/components/examples/ExamplesGallery.tsx index d4f7a701..fb72598d 100644 --- a/frontend/src/components/examples/ExamplesGallery.tsx +++ b/frontend/src/components/examples/ExamplesGallery.tsx @@ -30,7 +30,7 @@ const BOARD_TABS: BoardTab[] = [ { id: 'raspberry-pi-pico', label: 'Pico', color: '#ffffff', bg: '#c11c31' }, { id: 'esp32', label: 'ESP32 (Xtensa)', color: '#ffffff', bg: '#e77d11' }, { id: 'esp32-c3', label: 'ESP32-C3 (RISC-V)', color: '#ffffff', bg: '#27ae60' }, - { id: 'attiny85', label: 'ATtiny85', color: '#ffffff', bg: '#5d4037' }, + { id: 'attiny85', label: 'ATtiny85', color: '#ffffff', bg: '#5d4037' }, { id: 'multi', label: 'Multi-Board', color: '#ffffff', bg: '#7b2d8b' }, { id: 'analog', label: 'Analog', color: '#ffffff', bg: '#0ea5a5' }, ]; diff --git a/frontend/src/components/raspberry-pi/PiTerminal.tsx b/frontend/src/components/raspberry-pi/PiTerminal.tsx index 4ef0a76c..eb7968c0 100644 --- a/frontend/src/components/raspberry-pi/PiTerminal.tsx +++ b/frontend/src/components/raspberry-pi/PiTerminal.tsx @@ -83,7 +83,6 @@ export const PiTerminal: React.FC = ({ boardId }) => { termRef.current = null; fitAddonRef.current = null; }; - }, [boardId]); // ResizeObserver → refit terminal when container size changes diff --git a/frontend/src/components/simulator/Oscilloscope.tsx b/frontend/src/components/simulator/Oscilloscope.tsx index 65e64c59..5d71105c 100644 --- a/frontend/src/components/simulator/Oscilloscope.tsx +++ b/frontend/src/components/simulator/Oscilloscope.tsx @@ -221,7 +221,6 @@ const ChannelCanvas: React.FC = ({ const ctx = canvas.getContext('2d'); if (ctx) ctx.scale(window.devicePixelRatio, window.devicePixelRatio); drawWaveform(canvas, samples, channel.color, windowEndMs, windowMs); - }, [samples, channel.color, windowEndMs, windowMs]); return ( @@ -256,7 +255,6 @@ const RulerCanvas: React.FC = ({ windowEndMs, windowMs, timeDi const ctx = canvas.getContext('2d'); if (ctx) ctx.scale(window.devicePixelRatio, window.devicePixelRatio); drawRuler(canvas, windowEndMs, windowMs, timeDivMs); - }, [windowEndMs, windowMs, timeDivMs]); return ( diff --git a/frontend/src/components/simulator/SimulatorCanvas.tsx b/frontend/src/components/simulator/SimulatorCanvas.tsx index 5d9cd657..50a93f20 100644 --- a/frontend/src/components/simulator/SimulatorCanvas.tsx +++ b/frontend/src/components/simulator/SimulatorCanvas.tsx @@ -1287,7 +1287,6 @@ export const SimulatorCanvas = () => { }, 150); return () => clearTimeout(timer); - }, [components.length]); // Render component using dynamic renderer diff --git a/frontend/src/data/examples.ts b/frontend/src/data/examples.ts index c1a051f9..788bd276 100644 --- a/frontend/src/data/examples.ts +++ b/frontend/src/data/examples.ts @@ -6221,11 +6221,17 @@ void loop() { { id: 'attiny85-blink', title: 'ATtiny85: Blink LED', - description: 'Classic blink on the ATtiny85 built-in LED (PB1). Great first sketch for this tiny 8-bit chip.', + description: + 'Classic blink on the ATtiny85 built-in LED (PB1). Great first sketch for this tiny 8-bit chip.', category: 'basics', difficulty: 'beginner', boardFilter: 'attiny85', - boards: [{ boardKind: 'attiny85', x: 120, y: 160, code: `// ATtiny85 — Blink built-in LED on PB1 + boards: [ + { + boardKind: 'attiny85', + x: 120, + y: 160, + code: `// ATtiny85 — Blink built-in LED on PB1 // Pin 1 = PB1 = OC0B/OC1A (also Digispark LED) void setup() { @@ -6237,7 +6243,9 @@ void loop() { delay(500); digitalWrite(1, LOW); delay(500); -}` }], +}`, + }, + ], code: '', components: [], wires: [], @@ -6246,11 +6254,17 @@ void loop() { { id: 'attiny85-button-led', title: 'ATtiny85: Button + LED', - description: 'Press the button on PB0 to light the LED on PB1. Uses internal pull-up resistor — no external resistor needed.', + description: + 'Press the button on PB0 to light the LED on PB1. Uses internal pull-up resistor — no external resistor needed.', category: 'basics', difficulty: 'beginner', boardFilter: 'attiny85', - boards: [{ boardKind: 'attiny85', x: 100, y: 150, code: `// ATtiny85 — Button on PB0, LED on PB1 + boards: [ + { + boardKind: 'attiny85', + x: 100, + y: 150, + code: `// ATtiny85 — Button on PB0, LED on PB1 // Button wiring: one leg to PB0, other leg to GND // INPUT_PULLUP means HIGH = released, LOW = pressed @@ -6262,28 +6276,56 @@ void setup() { void loop() { bool pressed = (digitalRead(0) == LOW); digitalWrite(1, pressed ? HIGH : LOW); -}` }], +}`, + }, + ], code: '', components: [ { type: 'wokwi-pushbutton', id: 'tiny-btn1', x: 380, y: 130, properties: { color: 'green' } }, - { type: 'wokwi-led', id: 'tiny-led1', x: 380, y: 240, properties: { color: 'red' } }, + { type: 'wokwi-led', id: 'tiny-led1', x: 380, y: 240, properties: { color: 'red' } }, ], wires: [ - { id: 'tb-pb0', start: { componentId: 'attiny85', pinName: 'PB0' }, end: { componentId: 'tiny-btn1', pinName: '1.l' }, color: '#ffcc00' }, - { id: 'tb-gnd1', start: { componentId: 'attiny85', pinName: 'GND' }, end: { componentId: 'tiny-btn1', pinName: '2.l' }, color: '#000000' }, - { id: 'tb-pb1', start: { componentId: 'attiny85', pinName: 'PB1' }, end: { componentId: 'tiny-led1', pinName: 'A' }, color: '#ff4444' }, - { id: 'tb-gnd2', start: { componentId: 'attiny85', pinName: 'GND' }, end: { componentId: 'tiny-led1', pinName: 'C' }, color: '#000000' }, + { + id: 'tb-pb0', + start: { componentId: 'attiny85', pinName: 'PB0' }, + end: { componentId: 'tiny-btn1', pinName: '1.l' }, + color: '#ffcc00', + }, + { + id: 'tb-gnd1', + start: { componentId: 'attiny85', pinName: 'GND' }, + end: { componentId: 'tiny-btn1', pinName: '2.l' }, + color: '#000000', + }, + { + id: 'tb-pb1', + start: { componentId: 'attiny85', pinName: 'PB1' }, + end: { componentId: 'tiny-led1', pinName: 'A' }, + color: '#ff4444', + }, + { + id: 'tb-gnd2', + start: { componentId: 'attiny85', pinName: 'GND' }, + end: { componentId: 'tiny-led1', pinName: 'C' }, + color: '#000000', + }, ], }, { id: 'attiny85-pwm-fade', title: 'ATtiny85: PWM LED Fade', - description: 'Smoothly fade an LED in and out using analogWrite() on PB1 (OC0B). Shows Timer0 PWM on the ATtiny85.', + description: + 'Smoothly fade an LED in and out using analogWrite() on PB1 (OC0B). Shows Timer0 PWM on the ATtiny85.', category: 'basics', difficulty: 'intermediate', boardFilter: 'attiny85', - boards: [{ boardKind: 'attiny85', x: 100, y: 150, code: `// ATtiny85 — PWM LED fade on PB1 (OC0B / Timer0) + boards: [ + { + boardKind: 'attiny85', + x: 100, + y: 150, + code: `// ATtiny85 — PWM LED fade on PB1 (OC0B / Timer0) // analogWrite() uses Timer0 on the ATtiny85 void setup() { @@ -6302,27 +6344,56 @@ void loop() { delay(6); } delay(200); -}` }], +}`, + }, + ], code: '', components: [ - { type: 'wokwi-led', id: 'tiny-fade-led', x: 380, y: 200, properties: { color: 'yellow' } }, - { type: 'wokwi-resistor', id: 'tiny-fade-r1', x: 380, y: 300, properties: { resistance: '220' } }, + { type: 'wokwi-led', id: 'tiny-fade-led', x: 380, y: 200, properties: { color: 'yellow' } }, + { + type: 'wokwi-resistor', + id: 'tiny-fade-r1', + x: 380, + y: 300, + properties: { resistance: '220' }, + }, ], wires: [ - { id: 'tf-pb1', start: { componentId: 'attiny85', pinName: 'PB1' }, end: { componentId: 'tiny-fade-led', pinName: 'A' }, color: '#ffcc00' }, - { id: 'tf-led-r', start: { componentId: 'tiny-fade-led', pinName: 'C' }, end: { componentId: 'tiny-fade-r1', pinName: '1' }, color: '#888888' }, - { id: 'tf-gnd', start: { componentId: 'attiny85', pinName: 'GND' }, end: { componentId: 'tiny-fade-r1', pinName: '2' }, color: '#000000' }, + { + id: 'tf-pb1', + start: { componentId: 'attiny85', pinName: 'PB1' }, + end: { componentId: 'tiny-fade-led', pinName: 'A' }, + color: '#ffcc00', + }, + { + id: 'tf-led-r', + start: { componentId: 'tiny-fade-led', pinName: 'C' }, + end: { componentId: 'tiny-fade-r1', pinName: '1' }, + color: '#888888', + }, + { + id: 'tf-gnd', + start: { componentId: 'attiny85', pinName: 'GND' }, + end: { componentId: 'tiny-fade-r1', pinName: '2' }, + color: '#000000', + }, ], }, { id: 'attiny85-ntc-sensor', title: 'ATtiny85: NTC Temperature Sensor', - description: 'Read temperature from an NTC thermistor on PB3 (ADC3). The LED on PB1 blinks faster as temperature rises.', + description: + 'Read temperature from an NTC thermistor on PB3 (ADC3). The LED on PB1 blinks faster as temperature rises.', category: 'sensors', difficulty: 'intermediate', boardFilter: 'attiny85', - boards: [{ boardKind: 'attiny85', x: 100, y: 150, code: `// ATtiny85 — NTC Thermistor on PB3 (ADC3) + boards: [ + { + boardKind: 'attiny85', + x: 100, + y: 150, + code: `// ATtiny85 — NTC Thermistor on PB3 (ADC3) // Wiring: NTC between VCC and PB3, 10k resistor between PB3 and GND // LED on PB1: blinks faster when temperature increases @@ -6358,21 +6429,53 @@ void loop() { delay(blinkDelay / 2); digitalWrite(LED_PIN, LOW); delay(blinkDelay / 2); -}` }], +}`, + }, + ], code: '', components: [ - { type: 'wokwi-ntc-temperature-sensor', id: 'tiny-ntc1', x: 380, y: 150, properties: { temperature: '25' } }, - { type: 'wokwi-led', id: 'tiny-ntc-led', x: 380, y: 300, properties: { color: 'red' } }, + { + type: 'wokwi-ntc-temperature-sensor', + id: 'tiny-ntc1', + x: 380, + y: 150, + properties: { temperature: '25' }, + }, + { type: 'wokwi-led', id: 'tiny-ntc-led', x: 380, y: 300, properties: { color: 'red' } }, ], wires: [ - { id: 'tn-vcc', start: { componentId: 'attiny85', pinName: 'VCC' }, end: { componentId: 'tiny-ntc1', pinName: 'VCC' }, color: '#ff4444' }, - { id: 'tn-gnd', start: { componentId: 'attiny85', pinName: 'GND' }, end: { componentId: 'tiny-ntc1', pinName: 'GND' }, color: '#000000' }, - { id: 'tn-out', start: { componentId: 'attiny85', pinName: 'PB3' }, end: { componentId: 'tiny-ntc1', pinName: 'OUT' }, color: '#aa44ff' }, - { id: 'tn-led', start: { componentId: 'attiny85', pinName: 'PB1' }, end: { componentId: 'tiny-ntc-led', pinName: 'A' }, color: '#ff4444' }, - { id: 'tn-lgnd', start: { componentId: 'attiny85', pinName: 'GND' }, end: { componentId: 'tiny-ntc-led', pinName: 'C' }, color: '#000000' }, + { + id: 'tn-vcc', + start: { componentId: 'attiny85', pinName: 'VCC' }, + end: { componentId: 'tiny-ntc1', pinName: 'VCC' }, + color: '#ff4444', + }, + { + id: 'tn-gnd', + start: { componentId: 'attiny85', pinName: 'GND' }, + end: { componentId: 'tiny-ntc1', pinName: 'GND' }, + color: '#000000', + }, + { + id: 'tn-out', + start: { componentId: 'attiny85', pinName: 'PB3' }, + end: { componentId: 'tiny-ntc1', pinName: 'OUT' }, + color: '#aa44ff', + }, + { + id: 'tn-led', + start: { componentId: 'attiny85', pinName: 'PB1' }, + end: { componentId: 'tiny-ntc-led', pinName: 'A' }, + color: '#ff4444', + }, + { + id: 'tn-lgnd', + start: { componentId: 'attiny85', pinName: 'GND' }, + end: { componentId: 'tiny-ntc-led', pinName: 'C' }, + color: '#000000', + }, ], }, - ]; // Merge legacy examples with circuit-focused examples (analog, digital gates, diff --git a/frontend/src/simulation/parts/ComplexParts.ts b/frontend/src/simulation/parts/ComplexParts.ts index 106e9a0f..f277aa2f 100644 --- a/frontend/src/simulation/parts/ComplexParts.ts +++ b/frontend/src/simulation/parts/ComplexParts.ts @@ -350,7 +350,7 @@ PartSimulationRegistry.register('servo', { const pinManager = (avrSimulator as any).pinManager as | import('../PinManager').PinManager | undefined; - + const hasCpuCycles = typeof (avrSimulator as any).getCurrentCycles === 'function' && // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -381,14 +381,14 @@ PartSimulationRegistry.register('servo', { | undefined; if (pinManager) { let riseTime = -1; // cycle count at last rising edge - + const getCycles = () => typeof (avrSimulator as any).getCurrentCycles === 'function' ? // eslint-disable-next-line @typescript-eslint/no-explicit-any ((avrSimulator as any).getCurrentCycles() as number) : // eslint-disable-next-line @typescript-eslint/no-explicit-any (((avrSimulator as any).cpu?.cycles ?? 0) as number); - + const clockHz = typeof (avrSimulator as any).getClockHz === 'function' ? // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/frontend/src/simulation/parts/ProtocolParts.ts b/frontend/src/simulation/parts/ProtocolParts.ts index b2d64bd5..7de6188e 100644 --- a/frontend/src/simulation/parts/ProtocolParts.ts +++ b/frontend/src/simulation/parts/ProtocolParts.ts @@ -638,7 +638,7 @@ PartSimulationRegistry.register('dht22', { // After the MCU releases DATA HIGH and we begin responding, we ignore all // pin-change callbacks until the full waveform has been emitted. // DHT22 response is ~5 ms; gate for ~12.5 ms scaled to the CPU clock. - + const clockHz: number = typeof (simulator as any).getClockHz === 'function' ? // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/frontend/src/simulation/parts/SensorParts.ts b/frontend/src/simulation/parts/SensorParts.ts index 8e6f4767..b88cbafc 100644 --- a/frontend/src/simulation/parts/SensorParts.ts +++ b/frontend/src/simulation/parts/SensorParts.ts @@ -645,7 +645,7 @@ PartSimulationRegistry.register('hc-sr04', { let distanceCm = parseFloat(el.distance) || 10; // default distance in cm // ── ESP32 path: delegate protocol to backend QEMU worker ── - + const handledNatively = typeof (simulator as any).registerSensor === 'function' && // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -679,7 +679,6 @@ PartSimulationRegistry.register('hc-sr04', { const cleanup = simulator.pinManager.onPinChange(trigPin, (_: number, state: boolean) => { if (!state) return; // only react on TRIG HIGH if (typeof simulator.schedulePinChange === 'function') { - const clockHz: number = typeof (simulator as any).getClockHz === 'function' ? // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/frontend/src/simulation/spice/SpiceEngine.ts b/frontend/src/simulation/spice/SpiceEngine.ts index 73410984..809b5e62 100644 --- a/frontend/src/simulation/spice/SpiceEngine.ts +++ b/frontend/src/simulation/spice/SpiceEngine.ts @@ -48,7 +48,7 @@ async function bootEngine(): Promise { await sim.start(); singleton = sim; const dt = performance.now() - t0; - + console.log(`[SpiceEngine] boot in ${dt.toFixed(0)}ms`); return sim; })(); @@ -82,7 +82,6 @@ export async function runNetlist(netlist: string): Promise { const dt = performance.now() - t0; if (dt > 200) { - console.log(`[SpiceEngine] slow solve: ${dt.toFixed(0)}ms`); } diff --git a/frontend/src/utils/loadExample.ts b/frontend/src/utils/loadExample.ts index 9e089804..6856c706 100644 --- a/frontend/src/utils/loadExample.ts +++ b/frontend/src/utils/loadExample.ts @@ -90,7 +90,7 @@ export async function loadExample( if (eb.code) { const AVR_BOARDS = ['arduino-uno', 'arduino-nano', 'arduino-mega', 'attiny85']; - const filename = AVR_BOARDS.includes(boardId) ? 'sketch.ino' : 'main.cpp'; + const filename = AVR_BOARDS.includes(boardId) ? 'sketch.ino' : 'main.cpp'; useEditorStore.getState().setActiveGroup(board.activeFileGroupId); useEditorStore.getState().loadFiles([{ name: filename, content: eb.code }]); } diff --git a/wokwi-libs/qemu-lcgamboa b/wokwi-libs/qemu-lcgamboa index 092ad433..81f87f15 160000 --- a/wokwi-libs/qemu-lcgamboa +++ b/wokwi-libs/qemu-lcgamboa @@ -1 +1 @@ -Subproject commit 092ad4333d148ac57d28035ee5ae3e8f9b819313 +Subproject commit 81f87f158e954fa68969425226a7c08dac200ead