Skip to content

Commit 58b8721

Browse files
committed
feat(simulator): add interactive Monaco-based Python simulator at /simulator/
1 parent 3c7afec commit 58b8721

4 files changed

Lines changed: 1419 additions & 0 deletions

File tree

simulator/index.html

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<script>try{if(localStorage.getItem('pydevices-theme')==='light'){document.documentElement.setAttribute('data-theme','light');}}catch(e){}</script>
6+
<meta name="viewport" content="width=device-width, initial-scale=1">
7+
<title>PyDevices Simulator — Live Python GUI & Embedded Playground</title>
8+
<meta name="description" content="Interactive browser-based Python playground for LVGL v9, pdwidgets, pygraphics, and displaydev on MicroPython and Pyodide.">
9+
<link rel="icon" type="image/svg+xml" href="https://pydevices.github.io/img/logo.svg">
10+
<link rel="preconnect" href="https://fonts.googleapis.com">
11+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
12+
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Space+Grotesk:wght@500;600;700&display=swap">
13+
<link rel="stylesheet" href="./simulator.css">
14+
<!-- LZ-String for compressed URL snippet sharing -->
15+
<script src="https://cdnjs.cloudflare.com/ajax/libs/lz-string/1.4.4/lz-string.min.js"></script>
16+
<!-- Monaco Editor AMD Loader -->
17+
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.45.0/min/vs/loader.js"></script>
18+
</head>
19+
<body>
20+
<!-- Top Action & Navigation Header -->
21+
<header class="sim-header">
22+
<div class="sim-header-left">
23+
<a href="https://pydevices.github.io/" class="sim-brand" title="PyDevices Home">
24+
<img src="https://pydevices.github.io/img/logo.svg" alt="PyDevices Logo" width="28" height="28">
25+
<span>PyDevices</span>
26+
</a>
27+
<span class="sim-badge">Simulator</span>
28+
</div>
29+
30+
<div class="sim-header-center">
31+
<!-- Preset Template Selector -->
32+
<div class="sim-select-wrap">
33+
<select id="template-select" aria-label="Select Code Template">
34+
<optgroup label="LVGL v9">
35+
<option value="lvgl-counter" selected>LVGL v9: Interactive Counter</option>
36+
<option value="lvgl-thermostat">LVGL v9: Smart Thermostat Arc</option>
37+
</optgroup>
38+
<optgroup label="pdwidgets">
39+
<option value="pdwidgets-dashboard">pdwidgets: Sensor Deck Dashboard</option>
40+
</optgroup>
41+
<optgroup label="pygraphics">
42+
<option value="pygraphics-shapes">pygraphics: Vector & FrameBuffer Art</option>
43+
</optgroup>
44+
<optgroup label="displaydev">
45+
<option value="displaydev-raw">displaydev: Direct Pixel Painter</option>
46+
</optgroup>
47+
</select>
48+
</div>
49+
50+
<!-- Resolution & Shape Selector -->
51+
<div class="sim-select-wrap">
52+
<select id="resolution-select" aria-label="Select Screen Resolution">
53+
<option value="320x240" selected>320 × 240 (QVGA Landscape)</option>
54+
<option value="240x320">240 × 320 (QVGA Portrait)</option>
55+
<option value="240x240-square">240 × 240 (Square Watch)</option>
56+
<option value="240x240-round">240 × 240 (Round Watch)</option>
57+
<option value="480x320">480 × 320 (HVGA Landscape)</option>
58+
<option value="320x480">320 × 480 (HVGA Portrait)</option>
59+
<option value="800x480">800 × 480 (WVGA Landscape)</option>
60+
<option value="custom">Custom...</option>
61+
</select>
62+
</div>
63+
64+
<!-- Runtime Engine Selector -->
65+
<div class="sim-select-wrap">
66+
<select id="runtime-select" aria-label="Select Execution Runtime">
67+
<option value="pyodide" selected>Pyodide (CPython 3.12)</option>
68+
<option value="mpy">MicroPython WASM</option>
69+
</select>
70+
</div>
71+
</div>
72+
73+
<div class="sim-header-right">
74+
<!-- Run / Restart Button -->
75+
<button type="button" class="sim-btn sim-btn-primary" id="run-btn" title="Run Script (Ctrl+Enter)">
76+
<svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><polygon points="5 3 19 12 5 21 5 3"/></svg>
77+
<span>Run</span>
78+
<kbd>Ctrl+↵</kbd>
79+
</button>
80+
81+
<!-- Share URL Button -->
82+
<button type="button" class="sim-btn" id="share-btn" title="Share Interactive Code Link">
83+
<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/></svg>
84+
<span>Share</span>
85+
</button>
86+
87+
<!-- Theme Toggle -->
88+
<button type="button" class="sim-btn sim-icon-btn" id="theme-toggle" aria-label="Toggle Color Theme" title="Toggle Light/Dark Theme">
89+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>
90+
</button>
91+
</div>
92+
</header>
93+
94+
<!-- Main Resizable Workspace -->
95+
<main class="sim-workspace" id="workspace">
96+
<!-- Left Pane: Monaco Code Editor -->
97+
<section class="sim-editor-pane" id="editor-pane" aria-label="Code Editor">
98+
<div class="sim-pane-header">
99+
<div class="sim-tab">
100+
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>
101+
<span>main.py</span>
102+
</div>
103+
<div class="sim-status-indicator">
104+
<span class="sim-status-dot is-ready" id="status-dot"></span>
105+
<span id="status-text">Ready</span>
106+
</div>
107+
</div>
108+
<div class="sim-editor-container" id="editor-container"></div>
109+
</section>
110+
111+
<!-- Vertical Drag Splitter -->
112+
<div class="sim-splitter" id="splitter-v" role="separator" aria-orientation="vertical"></div>
113+
114+
<!-- Right Pane: Simulator & Console -->
115+
<section class="sim-preview-pane" id="preview-pane" aria-label="Simulator and Console Output">
116+
<!-- Top Sub-Pane: Hardware Canvas Bezel Stage -->
117+
<div class="sim-device-stage" id="device-stage">
118+
<div class="sim-device-bezel" id="device-bezel">
119+
<canvas id="display_canvas" width="320" height="240" tabindex="0"></canvas>
120+
<canvas id="aux_canvas" width="1" height="1" tabindex="0"></canvas>
121+
</div>
122+
</div>
123+
124+
<!-- Horizontal Drag Splitter -->
125+
<div class="sim-splitter-h" id="splitter-h" role="separator" aria-orientation="horizontal"></div>
126+
127+
<!-- Bottom Sub-Pane: Console / REPL Stream -->
128+
<section class="sim-console-pane" id="console-pane" aria-label="Console Output">
129+
<div class="sim-pane-header">
130+
<div class="sim-tab">
131+
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/></svg>
132+
<span>Console Output</span>
133+
</div>
134+
<button type="button" class="sim-btn sim-icon-btn" id="clear-console-btn" title="Clear Console">
135+
<svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
136+
</button>
137+
</div>
138+
<div class="sim-console-body" id="console-log"></div>
139+
</section>
140+
</section>
141+
</main>
142+
143+
<!-- Toast Notification -->
144+
<div class="sim-toast" id="sim-toast">✓ Link copied to clipboard!</div>
145+
146+
<!-- Simulator Application Scripts -->
147+
<script src="./templates.js"></script>
148+
<script src="./simulator.js"></script>
149+
</body>
150+
</html>

0 commit comments

Comments
 (0)