2026-02-18 15:24:00 +00:00
<!DOCTYPE html>
< html lang = "en" >
< head >
< meta charset = "UTF-8" / >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" / >
2026-04-17 10:34:46 +00:00
< title > RealRobots IDE< / title >
2026-02-18 15:24:00 +00:00
< link rel = "stylesheet" href = "/src/style.css" / >
< / head >
< body >
<!-- Top toolbar -->
< header id = "toolbar" >
< div class = "toolbar-left" >
2026-04-17 10:34:46 +00:00
< span class = "app-title" > RealRobots IDE< / span >
2026-02-20 07:27:56 +00:00
< label for = "device-select" class = "device-label" > Device< / label >
< select id = "device-select" title = "Target device (changes code generator and firmware)" >
2026-04-17 10:34:46 +00:00
< option value = "esp32s3" > ESP32< / option >
2026-02-20 07:27:56 +00:00
< option value = "microbit" > micro:bit< / option >
< option value = "rp2040" > RP2040 (Pico)< / option >
2026-02-28 15:55:30 +00:00
< option value = "arduino_uno" > Arduino Uno/Nano< / option >
2026-02-20 07:27:56 +00:00
< / select >
2026-02-18 15:24:00 +00:00
< button id = "btn-connect" title = "Connect to ESP32 via Web Serial" >
< span class = "icon" > ▶ < / span > Connect
< / button >
2026-04-17 10:34:46 +00:00
< span id = "connection-status" class = "status-disconnected" > Disconnected< / span >
2026-04-21 02:16:18 +00:00
< span id = "user-badge" class = "user-badge hidden" > < / span >
< button id = "btn-teacher-view" class = "hidden" title = "Open the teacher dashboard" > Teacher View< / button >
< button id = "btn-signin" title = "Sign in with your account (optional)" > Sign in< / button >
2026-04-17 10:34:46 +00:00
< / div >
< div class = "toolbar-actions" >
2026-02-18 15:24:00 +00:00
< button id = "btn-run" title = "Upload and run code" disabled >
< span class = "icon" > ▷ < / span > Run
< / button >
< button id = "btn-stop" title = "Stop running code" disabled >
< span class = "icon" > ■ < / span > Stop
< / button >
< button id = "btn-save" title = "Save code to device as main.py" disabled >
< span class = "icon" > 💾 < / span > Save
< / button >
2026-04-17 10:34:46 +00:00
< button id = "btn-load" title = "Load block layout from device" disabled >
< span class = "icon" > 📥 < / span > Load
2026-02-24 15:43:32 +00:00
< / button >
2026-04-17 10:34:46 +00:00
< button id = "btn-flash" title = "Flash MicroPython firmware" >
< span class = "icon" > ⚡ < / span > Flash Firmware
2026-02-25 02:07:26 +00:00
< / button >
2026-02-18 15:24:00 +00:00
< / div >
< / header >
< main id = "workspace-container" >
2026-02-20 08:13:27 +00:00
<!-- Top row: Blockly + Projects sidebar -->
< div id = "top-area" >
< div id = "blockly-area" >
< div id = "blockly-div" > < / div >
< / div >
2026-02-25 02:07:26 +00:00
<!-- Customize toolbox sidebar -->
2026-04-17 10:34:46 +00:00
< div id = "robot-panel" class = "ide-panel hidden" >
< div class = "panel-header" >
< span class = "panel-title" > Robot< / span >
< / div >
< div class = "panel-body robot-panel-body" >
< div class = "robot-toolbar" >
< button type = "button" id = "robot-new" class = "robot-tb-btn" > New< / button >
2026-04-21 03:45:12 +00:00
< button type = "button" id = "robot-open" class = "robot-tb-btn" > Open< / button >
< button type = "button" id = "robot-save" class = "robot-tb-btn" > Save< / button >
2026-04-17 10:34:46 +00:00
< button type = "button" id = "robot-apply" class = "robot-tb-btn robot-tb-primary" > Apply< / button >
< / div >
2026-04-21 03:45:12 +00:00
< ul id = "robot-file-list" class = "projects-list" > < / ul >
< div class = "projects-name-row" >
< input type = "text" id = "robot-save-name" placeholder = "Robot file name..." / >
< / div >
2026-04-17 10:34:46 +00:00
< button type = "button" id = "robot-import-ws" class = "robot-full-btn" > Import from workspace< / button >
< div id = "robot-device-note" class = "robot-note" > < / div >
< div class = "robot-add-row" >
< select id = "robot-add-select" class = "robot-add-select" aria-label = "Component to add" > < / select >
< button type = "button" id = "robot-add-btn" class = "robot-tb-btn" > Add< / button >
< / div >
< ul id = "robot-component-list" class = "robot-comp-list" aria-label = "Components" > < / ul >
< div id = "robot-editor" class = "robot-editor" > < / div >
< div id = "robot-status" class = "robot-status" role = "status" > < / div >
< input type = "file" id = "robot-file-input" accept = ".json,application/json" class = "robot-file-input-hidden" / >
< / div >
< / div >
2026-02-25 02:07:26 +00:00
< div id = "customizer-panel" class = "ide-panel hidden" >
< div class = "panel-header" >
< span class = "panel-title" > Customize Toolbox< / span >
< button id = "customizer-done" class = "cust-done-btn" title = "Exit customize mode" > Done< / button >
< / div >
< div class = "panel-body" >
< div class = "cust-presets" >
< div class = "cust-preset-row" >
< select id = "preset-select" title = "Saved presets" >
< option value = "" > — select preset —< / option >
< / select >
< button id = "preset-load" title = "Load selected preset" > Load< / button >
< button id = "preset-delete" class = "btn-danger" title = "Delete selected preset" > Del< / button >
< / div >
< div class = "cust-preset-row" >
< input type = "text" id = "preset-name" placeholder = "Preset name..." / >
< button id = "preset-save" title = "Save current settings as preset" > Save< / button >
< / div >
< div class = "cust-preset-row" >
< button id = "preset-show-all" title = "Show everything" > Show All< / button >
< button id = "preset-hide-all" title = "Hide everything" > Hide All< / button >
< / div >
< / div >
< div id = "customizer-tree" class = "customizer-tree" > < / div >
< / div >
< / div >
2026-02-20 08:13:27 +00:00
<!-- Projects right sidebar -->
2026-04-17 10:34:46 +00:00
< div id = "projects-panel" class = "ide-panel collapsed" >
2026-02-20 08:13:27 +00:00
< div class = "panel-header" >
< span class = "panel-title" > Projects< / span >
< button class = "panel-toggle" data-panel = "projects-panel" title = "Toggle panel" > ◂ < / button >
< / div >
2026-04-17 10:34:46 +00:00
< div id = "side-tools-rail" aria-label = "Tool tabs" >
< button id = "btn-projects" class = "side-rail-btn" title = "Toggle Projects panel" >
< span class = "side-rail-label" > PROJECTS< / span >
< span class = "side-rail-arrow" aria-hidden = "true" > ◂ < / span >
< / button >
< button id = "btn-robot" class = "side-rail-btn" title = "Robot hardware (pins, apply init blocks)" >
< span class = "side-rail-label" > ROBOT< / span >
< span class = "side-rail-arrow" aria-hidden = "true" > ◂ < / span >
< / button >
< button id = "btn-customize" class = "side-rail-btn" title = "Show/hide toolbox categories and blocks" >
< span class = "side-rail-label" > CUSTOMIZE< / span >
< span class = "side-rail-arrow" aria-hidden = "true" > ◂ < / span >
< / button >
< button id = "btn-addons" class = "side-rail-btn" title = "Manage addons" >
< span class = "side-rail-label" > ADDONS< / span >
< span class = "side-rail-arrow" aria-hidden = "true" > ◂ < / span >
< / button >
< / div >
2026-02-20 08:13:27 +00:00
< div class = "panel-body" >
< div class = "proj-tab-content" id = "proj-tab-browser" >
< ul class = "projects-list" id = "browser-list" > < / ul >
< div class = "projects-actions" >
< div class = "projects-name-row" >
< input type = "text" id = "browser-save-name" placeholder = "Project name..." / >
< button id = "browser-save-btn" > Save< / button >
< / div >
2026-04-21 03:45:12 +00:00
< div class = "projects-btn-row projects-btn-row-primary" >
2026-02-20 08:13:27 +00:00
< button id = "browser-load-btn" disabled > Load< / button >
< button id = "browser-delete-btn" class = "btn-danger" disabled > Delete< / button >
< / div >
2026-04-21 03:45:12 +00:00
< div class = "projects-btn-row projects-btn-row-secondary" >
< button id = "browser-move-cloud-btn" disabled > Move to cloud< / button >
< button id = "browser-download-btn" disabled > Download to computer< / button >
< / div >
< div class = "projects-btn-row projects-btn-row-secondary" >
< button id = "browser-upload-btn" > Upload from computer< / button >
< / div >
< input type = "file" id = "browser-upload-input" accept = ".blk,.json,application/json" class = "robot-file-input-hidden" / >
2026-02-20 08:13:27 +00:00
< / div >
< / div >
< / div >
< / div >
2026-02-18 15:24:00 +00:00
< / div >
<!-- Bottom panels: code preview + serial terminal -->
< div id = "bottom-panels" >
< div id = "resize-handle-h" class = "resize-handle horizontal" > < / div >
2026-02-20 08:13:27 +00:00
< div id = "code-panel" class = "ide-panel" >
< div class = "panel-header" >
< span class = "panel-title" > Generated Code< / span >
< button class = "panel-toggle" data-panel = "code-panel" title = "Toggle panel" > ▾ < / button >
< / div >
< div class = "panel-body" >
< pre id = "code-preview" > < code id = "code-output" > < / code > < / pre >
2026-02-18 15:24:00 +00:00
< / div >
< / div >
2026-02-20 08:13:27 +00:00
< div id = "terminal-panel" class = "ide-panel" >
< div class = "panel-header" >
< span class = "panel-title" > Serial Terminal< / span >
< button class = "panel-toggle" data-panel = "terminal-panel" title = "Toggle panel" > ▾ < / button >
2026-02-20 07:57:54 +00:00
< / div >
2026-02-20 08:13:27 +00:00
< div class = "panel-body" >
< div id = "terminal-output" > < / div >
< div id = "terminal-input-row" >
< input type = "text" id = "terminal-input" placeholder = "Type command..." disabled / >
2026-02-20 07:57:54 +00:00
< / div >
< / div >
< / div >
< / div >
2026-02-20 08:13:27 +00:00
< / main >
2026-02-20 07:57:54 +00:00
2026-02-18 15:39:05 +00:00
<!-- Flash progress overlay -->
< div id = "flash-overlay" class = "hidden" >
< div id = "flash-modal" >
< h3 > Flashing MicroPython Firmware< / h3 >
< div id = "flash-log" > < / div >
< div id = "flash-progress-bar" >
< div id = "flash-progress-fill" > < / div >
< / div >
< span id = "flash-progress-text" > 0%< / span >
< button id = "flash-close" class = "hidden" > Close< / button >
< / div >
< / div >
2026-02-24 09:52:12 +00:00
<!-- Send code progress modal (Run / Save) -->
< div id = "send-overlay" class = "hidden" >
< div id = "send-modal" >
< h3 id = "send-modal-title" > Sending code to device< / h3 >
< div id = "send-progress-bar" >
< div id = "send-progress-fill" > < / div >
< / div >
< span id = "send-progress-text" > 0%< / span >
< / div >
< / div >
2026-02-24 15:43:32 +00:00
<!-- Addons manager overlay -->
< div id = "addons-overlay" class = "hidden" >
< div id = "addons-modal" >
< div class = "addons-header" >
< h3 > Addons< / h3 >
< button id = "addons-close" title = "Close" > × < / button >
< / div >
< p class = "addons-description" > Upload < code > .js< / code > addon files to add new toolbox categories. Addons are saved in your browser.< / p >
< div class = "addons-upload-row" >
< input type = "file" id = "addon-file-input" accept = ".js" / >
< button id = "addon-install-btn" > Install< / button >
< / div >
< div id = "addon-status" class = "addons-status" > < / div >
< ul id = "addons-list" class = "addons-list" > < / ul >
< / div >
< / div >
2026-02-28 15:55:30 +00:00
<!-- Upload .hex overlay (Arduino STK500) -->
< div id = "hex-upload-overlay" class = "hidden" >
< div id = "hex-upload-modal" >
< div class = "board-select-header" >
< h3 > Upload .hex to Arduino< / h3 >
< button id = "hex-upload-close" title = "Close" > × < / button >
< / div >
< p class = "board-select-description" > Select your board type, choose a compiled < code > .hex< / code > file, then click Upload. The browser will prompt you to pick a serial port.< / p >
< div class = "hex-upload-fields" >
< label class = "hex-field-label" for = "hex-board-select" > Board< / label >
< select id = "hex-board-select" >
< option value = "uno" > Arduino Uno (115200 baud)< / option >
< option value = "nano" > Arduino Nano — old bootloader (57600)< / option >
< option value = "nano_new" > Arduino Nano — new bootloader (115200)< / option >
< / select >
< label class = "hex-field-label" for = "hex-file-input" > .hex file< / label >
< input type = "file" id = "hex-file-input" accept = ".hex" / >
< / div >
< div class = "board-select-actions" >
< button id = "hex-upload-btn" > Upload< / button >
< / div >
< div id = "hex-upload-status" class = "hex-upload-status" > < / div >
< / div >
< / div >
2026-04-17 10:34:46 +00:00
<!-- ESP32 firmware board picker overlay -->
< div id = "esp32-flash-overlay" class = "hidden" >
< div id = "esp32-flash-modal" >
< div class = "board-select-header" >
< h3 > Flash ESP32 MicroPython< / h3 >
< button id = "esp32-flash-close" title = "Close" > × < / button >
< / div >
< p class = "board-select-description" > Choose your ESP32 chip family, then continue to flash firmware in-browser.< / p >
< div class = "hex-upload-fields" >
< label class = "hex-field-label" for = "esp32-variant-select" > Board family< / label >
< select id = "esp32-variant-select" >
< option value = "esp32s3" > ESP32-S3< / option >
< option value = "esp32" > ESP32 (Generic)< / option >
< option value = "esp32s2" > ESP32-S2< / option >
< option value = "esp32c3" > ESP32-C3< / option >
< / select >
< / div >
< div class = "board-select-actions" >
< button id = "esp32-flash-start" > Flash< / button >
< / div >
< div id = "esp32-flash-status" class = "hex-upload-status" > < / div >
< / div >
< / div >
2026-04-19 11:28:00 +00:00
<!-- micro:bit firmware board picker overlay -->
< div id = "microbit-flash-overlay" class = "hidden" >
< div id = "microbit-flash-modal" >
< div class = "board-select-header" >
< h3 > Flash micro:bit MicroPython< / h3 >
< button id = "microbit-flash-close" title = "Close" > × < / button >
< / div >
< p class = "board-select-description" > Choose your micro:bit hardware version, then continue to flash firmware.< / p >
< div class = "hex-upload-fields" >
< label class = "hex-field-label" for = "microbit-variant-select" > Board version< / label >
< select id = "microbit-variant-select" >
< option value = "v2" > micro:bit v2< / option >
< option value = "v1" > micro:bit v1< / option >
< / select >
< / div >
< div class = "board-select-actions" >
< button id = "microbit-flash-start" > Flash< / button >
< / div >
< div id = "microbit-flash-status" class = "hex-upload-status" > < / div >
< / div >
< / div >
2026-04-21 02:16:18 +00:00
<!-- Login / Register overlay -->
< div id = "login-overlay" class = "hidden" >
< div id = "login-modal" >
< div class = "login-header" >
< h3 > Sign in< / h3 >
< button id = "login-close" title = "Close" > × < / button >
< / div >
< p class = "login-description" > Signing in is optional. It lets teachers see your workspace live and push code to you in class.< / p >
< div class = "login-tabs" >
< button type = "button" id = "login-tab-login" class = "login-tab active" > Sign in< / button >
< button type = "button" id = "login-tab-register" class = "login-tab" > Create account< / button >
< / div >
< form id = "login-form" class = "login-form" autocomplete = "off" >
< label class = "login-field-label" for = "login-username" > Username< / label >
< input type = "text" id = "login-username" autocomplete = "username" spellcheck = "false" / >
< label class = "login-field-label" for = "login-password" > Password< / label >
< input type = "password" id = "login-password" autocomplete = "current-password" / >
< div class = "login-actions" >
< button type = "submit" id = "login-submit" > Sign in< / button >
< / div >
< / form >
< div id = "login-status" class = "login-status" > < / div >
< / div >
< / div >
<!-- Toast for "teacher pushed blocks" notifications -->
< div id = "push-toast" class = "push-toast hidden" > < / div >
2026-02-18 15:24:00 +00:00
< script type = "module" src = "/src/main.js" > < / script >
< / body >
< / html >