diff --git a/frontend/src/components/editor/EditorToolbar.tsx b/frontend/src/components/editor/EditorToolbar.tsx
index 876d78a..07cbb6a 100644
--- a/frontend/src/components/editor/EditorToolbar.tsx
+++ b/frontend/src/components/editor/EditorToolbar.tsx
@@ -11,7 +11,7 @@ import { InstallLibrariesModal } from '../simulator/InstallLibrariesModal';
import { parseCompileResult } from '../../utils/compilationLogger';
import type { CompilationLog } from '../../utils/compilationLogger';
import { exportToWokwiZip, importFromWokwiZip } from '../../utils/wokwiZip';
-import { trackCompileCode, trackRunSimulation } from '../../utils/analytics';
+import { trackCompileCode, trackRunSimulation, trackStopSimulation, trackResetSimulation, trackOpenLibraryManager } from '../../utils/analytics';
import './EditorToolbar.css';
interface EditorToolbarProps {
@@ -160,6 +160,7 @@ export const EditorToolbar = ({ consoleOpen, setConsoleOpen, compileLogs: _compi
const board = boards.find((b) => b.id === activeBoardId);
const isQemuBoard = board?.boardKind === 'raspberry-pi-3' || board?.boardKind === 'esp32' || board?.boardKind === 'esp32-s3';
if (isQemuBoard || board?.compiledProgram) {
+ trackRunSimulation(board?.boardKind);
startBoard(activeBoardId);
setMessage(null);
return;
@@ -176,12 +177,14 @@ export const EditorToolbar = ({ consoleOpen, setConsoleOpen, compileLogs: _compi
};
const handleStop = () => {
+ trackStopSimulation();
if (activeBoardId) stopBoard(activeBoardId);
else stopSimulation();
setMessage(null);
};
const handleReset = () => {
+ trackResetSimulation();
if (activeBoardId) resetBoard(activeBoardId);
else resetSimulation();
setMessage(null);
@@ -431,7 +434,7 @@ export const EditorToolbar = ({ consoleOpen, setConsoleOpen, compileLogs: _compi
{/* Library Manager — always visible with label */}
- Open Emulator →
+ trackClickCTA('arduino-emulator', '/editor')}>Open Emulator →
Emulation Details
Free & open-source · Built on avr8js & rp2040js · No signup required
@@ -171,7 +172,7 @@ export const ArduinoEmulatorPage: React.FC = () => {
Start emulating Arduino today
Open the editor and execute your firmware against a real emulated CPU — no hardware purchase, no cloud, no limits.
-
Launch Emulator →
+
trackClickCTA('arduino-emulator', '/editor')}>Launch Emulator →
Arduino Simulator
ATmega328P Simulator
diff --git a/frontend/src/pages/ArduinoMegaSimulatorPage.tsx b/frontend/src/pages/ArduinoMegaSimulatorPage.tsx
index 54647b4..84a8927 100644
--- a/frontend/src/pages/ArduinoMegaSimulatorPage.tsx
+++ b/frontend/src/pages/ArduinoMegaSimulatorPage.tsx
@@ -8,6 +8,7 @@ import { Link } from 'react-router-dom';
import { AppHeader } from '../components/layout/AppHeader';
import { useSEO } from '../utils/useSEO';
import { getSeoMeta } from '../seoRoutes';
+import { trackClickCTA } from '../utils/analytics';
import './SEOPage.css';
const META = getSeoMeta('/arduino-mega-simulator')!;
@@ -86,7 +87,7 @@ export const ArduinoMegaSimulatorPage: React.FC = () => {
pins, 16 analog inputs, 4 serial ports, and 6 timers. Free and open-source.
- Open Mega 2560 Simulator →
+ trackClickCTA('arduino-mega-simulator', '/editor')}>Open Mega 2560 Simulator →
Browse Examples
Free & open-source · No signup required · Full ATmega2560 emulation
@@ -168,7 +169,7 @@ export const ArduinoMegaSimulatorPage: React.FC = () => {
Simulate your Arduino Mega project
Select the Mega 2560 board in the editor and start simulating — full ATmega2560 emulation, no hardware purchase needed.
-
Launch Mega 2560 Simulator →
+
trackClickCTA('arduino-mega-simulator', '/editor')}>Launch Mega 2560 Simulator →
Arduino Simulator
Arduino Emulator
diff --git a/frontend/src/pages/ArduinoSimulatorPage.tsx b/frontend/src/pages/ArduinoSimulatorPage.tsx
index 916c3ac..120182e 100644
--- a/frontend/src/pages/ArduinoSimulatorPage.tsx
+++ b/frontend/src/pages/ArduinoSimulatorPage.tsx
@@ -8,6 +8,7 @@ import { Link } from 'react-router-dom';
import { AppHeader } from '../components/layout/AppHeader';
import { useSEO } from '../utils/useSEO';
import { getSeoMeta } from '../seoRoutes';
+import { trackClickCTA } from '../utils/analytics';
import './SEOPage.css';
const META = getSeoMeta('/arduino-simulator')!;
@@ -86,7 +87,7 @@ export const ArduinoSimulatorPage: React.FC = () => {
electronic components. No install, no cloud, no account required.
- Open Arduino Simulator →
+ trackClickCTA('arduino-simulator', '/editor')}>Open Arduino Simulator →
Browse Examples
Free & open-source · No signup required · Runs 100% in your browser
@@ -167,7 +168,7 @@ export const ArduinoSimulatorPage: React.FC = () => {
Ready to simulate your Arduino?
Open the editor and start coding in seconds — no setup, no install, no account needed.
-
Launch Arduino Simulator →
+
trackClickCTA('arduino-simulator', '/editor')}>Launch Arduino Simulator →
Example Projects
Arduino Emulator
diff --git a/frontend/src/pages/AtmegaSimulatorPage.tsx b/frontend/src/pages/AtmegaSimulatorPage.tsx
index ca3b473..7068462 100644
--- a/frontend/src/pages/AtmegaSimulatorPage.tsx
+++ b/frontend/src/pages/AtmegaSimulatorPage.tsx
@@ -8,6 +8,7 @@ import { Link } from 'react-router-dom';
import { AppHeader } from '../components/layout/AppHeader';
import { useSEO } from '../utils/useSEO';
import { getSeoMeta } from '../seoRoutes';
+import { trackClickCTA } from '../utils/analytics';
import './SEOPage.css';
const META = getSeoMeta('/atmega328p-simulator')!;
@@ -86,7 +87,7 @@ export const AtmegaSimulatorPage: React.FC = () => {
16 MHz with full GPIO, timer, ADC, and USART emulation. No hardware, no install.
- Open ATmega328P Simulator →
+ trackClickCTA('atmega-simulator', '/editor')}>Open ATmega328P Simulator →
Technical Details
Free & open-source · Genuine AVR8 emulation · Runs 100% in your browser
@@ -172,7 +173,7 @@ export const AtmegaSimulatorPage: React.FC = () => {
Simulate your ATmega328P code now
Open the editor, paste your sketch, and click Simulate — no setup, no hardware purchase required.
-
Launch ATmega328P Simulator →
+
trackClickCTA('atmega-simulator', '/editor')}>Launch ATmega328P Simulator →
Arduino Simulator
Arduino Emulator
diff --git a/frontend/src/pages/Esp32C3SimulatorPage.tsx b/frontend/src/pages/Esp32C3SimulatorPage.tsx
index 61f7910..cf93d4d 100644
--- a/frontend/src/pages/Esp32C3SimulatorPage.tsx
+++ b/frontend/src/pages/Esp32C3SimulatorPage.tsx
@@ -8,6 +8,7 @@ import { Link } from 'react-router-dom';
import { AppHeader } from '../components/layout/AppHeader';
import { useSEO } from '../utils/useSEO';
import { getSeoMeta } from '../seoRoutes';
+import { trackClickCTA } from '../utils/analytics';
import esp32C3SvgUrl from '../../../wokwi-libs/wokwi-boards/boards/esp32-c3-devkitm-1/board.svg?url';
import './SEOPage.css';
@@ -86,7 +87,7 @@ export const Esp32C3SimulatorPage: React.FC = () => {
RV32IMC at 160 MHz with 48+ interactive components.
- Open ESP32-C3 Simulator →
+ trackClickCTA('esp32-c3-simulator', '/editor')}>Open ESP32-C3 Simulator →
C3 Examples
Free & open-source · 100% browser-native · No backend required
@@ -168,7 +169,7 @@ export const Esp32C3SimulatorPage: React.FC = () => {
Ready to simulate ESP32-C3?
Open the editor, pick an ESP32-C3 board, and start coding — runs instantly in your browser.
-
Launch ESP32-C3 Simulator →
+
trackClickCTA('esp32-c3-simulator', '/editor')}>Launch ESP32-C3 Simulator →
ESP32 Simulator
ESP32-S3 Simulator
diff --git a/frontend/src/pages/Esp32S3SimulatorPage.tsx b/frontend/src/pages/Esp32S3SimulatorPage.tsx
index 611740d..d51b423 100644
--- a/frontend/src/pages/Esp32S3SimulatorPage.tsx
+++ b/frontend/src/pages/Esp32S3SimulatorPage.tsx
@@ -8,6 +8,7 @@ import { Link } from 'react-router-dom';
import { AppHeader } from '../components/layout/AppHeader';
import { useSEO } from '../utils/useSEO';
import { getSeoMeta } from '../seoRoutes';
+import { trackClickCTA } from '../utils/analytics';
import esp32S3SvgUrl from '../../../wokwi-libs/wokwi-boards/boards/esp32-s3-devkitc-1/board.svg?url';
import './SEOPage.css';
@@ -86,7 +87,7 @@ export const Esp32S3SimulatorPage: React.FC = () => {
USB OTG, vector extensions, 45 GPIOs. Write, compile, and run in seconds.
- Open ESP32-S3 Simulator →
+ trackClickCTA('esp32-s3-simulator', '/editor')}>Open ESP32-S3 Simulator →
Browse Examples
Free & open-source · QEMU Xtensa LX7 · No account needed
@@ -150,7 +151,7 @@ export const Esp32S3SimulatorPage: React.FC = () => {
Ready to simulate ESP32-S3?
Open the editor, select an ESP32-S3 board, and run your code instantly.
-
Launch ESP32-S3 Simulator →
+
trackClickCTA('esp32-s3-simulator', '/editor')}>Launch ESP32-S3 Simulator →
ESP32 Simulator
ESP32-C3 Simulator
diff --git a/frontend/src/pages/Esp32SimulatorPage.tsx b/frontend/src/pages/Esp32SimulatorPage.tsx
index 0397890..4e7dcb5 100644
--- a/frontend/src/pages/Esp32SimulatorPage.tsx
+++ b/frontend/src/pages/Esp32SimulatorPage.tsx
@@ -8,6 +8,7 @@ import { Link } from 'react-router-dom';
import { AppHeader } from '../components/layout/AppHeader';
import { useSEO } from '../utils/useSEO';
import { getSeoMeta } from '../seoRoutes';
+import { trackClickCTA } from '../utils/analytics';
import esp32SvgUrl from '../../../wokwi-libs/wokwi-boards/boards/esp32-devkit-v1/board.svg?url';
import './SEOPage.css';
@@ -88,7 +89,7 @@ export const Esp32SimulatorPage: React.FC = () => {
48+ interactive components, Serial Monitor, no install required.
- Open ESP32 Simulator →
+ trackClickCTA('esp32-simulator', '/editor')}>Open ESP32 Simulator →
ESP32 Examples
Free & open-source · No signup · QEMU-powered emulation
@@ -183,7 +184,7 @@ export const Esp32SimulatorPage: React.FC = () => {
Ready to simulate your ESP32?
Open the editor, select an ESP32 board, and start coding — no setup, no install, no account needed.
-
Launch ESP32 Simulator →
+
trackClickCTA('esp32-simulator', '/editor')}>Launch ESP32 Simulator →
Example Projects
ESP32 Docs
diff --git a/frontend/src/pages/ExamplesPage.tsx b/frontend/src/pages/ExamplesPage.tsx
index 5f67d6d..934357e 100644
--- a/frontend/src/pages/ExamplesPage.tsx
+++ b/frontend/src/pages/ExamplesPage.tsx
@@ -38,9 +38,6 @@ export const ExamplesPage: React.FC = () => {
const missing = libs.filter((l) => !installedNames.has(l.toLowerCase()));
if (missing.length === 0) return;
- const handleLoadExample = (example: ExampleProject) => {
- console.log('Loading example:', example.title);
- trackOpenExample(example.title);
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] });
@@ -54,6 +51,7 @@ export const ExamplesPage: React.FC = () => {
};
const handleLoadExample = async (example: ExampleProject) => {
+ trackOpenExample(example.title);
// Auto-install required libraries before loading
if (example.libraries && example.libraries.length > 0) {
await ensureLibraries(example.libraries);
diff --git a/frontend/src/pages/LandingPage.tsx b/frontend/src/pages/LandingPage.tsx
index 7e63170..fb58336 100644
--- a/frontend/src/pages/LandingPage.tsx
+++ b/frontend/src/pages/LandingPage.tsx
@@ -1,7 +1,7 @@
import { useState, useRef, useEffect } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { useAuthStore } from '../store/useAuthStore';
-import { trackVisitGitHub } from '../utils/analytics';
+import { trackVisitGitHub, trackClickCTA } from '../utils/analytics';
import { AppHeader } from '../components/layout/AppHeader';
import { useSEO } from '../utils/useSEO';
import { getSeoMeta } from '../seoRoutes';
@@ -496,7 +496,7 @@ export const LandingPage: React.FC = () => {
Raspberry Pi Pico, Raspberry Pi 3, and more. No hardware, no cloud, no limits.
-
+
trackClickCTA('landing', '/editor')}>
Try Simulator Free →
diff --git a/frontend/src/pages/LoginPage.tsx b/frontend/src/pages/LoginPage.tsx
index 2a7a5d4..89b07db 100644
--- a/frontend/src/pages/LoginPage.tsx
+++ b/frontend/src/pages/LoginPage.tsx
@@ -3,6 +3,7 @@ import { Link, useNavigate, useSearchParams } from 'react-router-dom';
import { login, initiateGoogleLogin } from '../services/authService';
import { useAuthStore } from '../store/useAuthStore';
import { useSEO } from '../utils/useSEO';
+import { trackLogin } from '../utils/analytics';
export const LoginPage: React.FC = () => {
useSEO({
@@ -25,6 +26,7 @@ export const LoginPage: React.FC = () => {
setLoading(true);
try {
const user = await login(email, password);
+ trackLogin('email');
setUser(user);
navigate(searchParams.get('redirect') || '/');
} catch (err: any) {
@@ -71,7 +73,7 @@ export const LoginPage: React.FC = () => {
or
-
+ { trackLogin('google'); initiateGoogleLogin(); }} className="ap-btn-white">