22
33// --- 1. GET REFERENCES TO OUR HTML ELEMENTS ---
44const runButton = document . getElementById ( "run-button" ) ;
5+ const clearButton = document . getElementById ( "clear-btn" ) ;
56const editorContainer = document . getElementById ( "editor-container" ) ;
67const drawingCanvas = document . getElementById ( "drawing-canvas" ) ;
78const outputArea = document . getElementById ( "output-area" ) ;
@@ -17,6 +18,68 @@ const visualPanel = document.querySelector(".visual-panel");
1718// --- Key for browser's local storage ---
1819const KIDCODE_STORAGE_KEY = "kidcode.savedCode" ;
1920
21+ const speedRange = document . getElementById ( "speedRange" ) ;
22+ const speedLabel = document . getElementById ( "speedLabel" ) ;
23+
24+ const speedText = {
25+ "0" : "Step-by-Step" ,
26+ "1" : "Normal" ,
27+ "2" : "Fast" ,
28+ } ;
29+
30+ function updateSpeedUI ( ) {
31+ if ( ! speedRange || ! speedLabel ) return ;
32+ speedLabel . textContent = speedText [ speedRange . value ] || "Normal" ;
33+ }
34+
35+ if ( speedRange ) {
36+ speedRange . addEventListener ( "input" , updateSpeedUI ) ;
37+ updateSpeedUI ( ) ;
38+ }
39+
40+ // Step-by-step control using keyboard
41+ let nextResolve = null ;
42+ let stepModalShown = false ;
43+
44+
45+ function waitForNextKey ( ) {
46+ return new Promise ( ( resolve ) => {
47+ nextResolve = resolve ;
48+ } ) ;
49+ }
50+
51+
52+ // Step modal elements
53+ const stepModal = document . getElementById ( "step-modal" ) ;
54+ const closeStepModalBtn = document . getElementById ( "close-step-modal" ) ;
55+
56+ if ( closeStepModalBtn ) {
57+ const closeModal = ( ) => {
58+ stepModal . classList . add ( "hidden" ) ;
59+ stepModal . dispatchEvent ( new Event ( "closed" ) ) ;
60+ } ;
61+
62+ closeStepModalBtn . addEventListener ( "click" , closeModal ) ;
63+
64+ window . addEventListener ( "keydown" , ( e ) => {
65+ if ( ( e . key === "Enter" || e . key === "Escape" ) && ! stepModal . classList . contains ( "hidden" ) ) {
66+ closeModal ( ) ;
67+ }
68+ } ) ;
69+ }
70+
71+
72+ window . addEventListener ( "keydown" , ( e ) => {
73+ const isInMonaco = document . activeElement ?. closest ( '.monaco-editor' ) ; // detect if typing in editor
74+
75+ if ( e . key === "Enter" && nextResolve && ! isInMonaco ) {
76+ e . preventDefault ( ) ;
77+ nextResolve ( ) ;
78+ nextResolve = null ;
79+ }
80+ } ) ;
81+
82+
2083// --- MONACO: Global variable to hold the editor instance ---
2184let editor ;
2285let validationTimeout ;
@@ -174,13 +237,26 @@ function initializeExamples() {
174237 } ) ;
175238}
176239
240+ let isExecuting = false ;
241+
177242// --- 2. ADD EVENT LISTENER TO THE RUN BUTTON ---
178243// --- 1️⃣ Event listener for Run button ---
179244runButton . addEventListener ( "click" , async ( ) => {
245+ if ( isExecuting ) {
246+ logToOutput ( "Execution already in progress. Please wait." , "error" ) ;
247+ return ;
248+ }
249+ isExecuting = true ;
250+ runButton . disabled = true ;
251+ if ( clearButton ) clearButton . disabled = true ;
252+
180253 const code = editor . getValue ( ) ;
181254
182- // ✅ Always start with a fresh canvas before execution
255+ // Always start with a fresh canvas before execution
183256 clearCanvas ( ) ;
257+ drawnLines = [ ] ;
258+ codyState = { x : 250 , y : 250 , direction : 0 , color : "blue" } ;
259+ stepModalShown = false ;
184260 outputArea . textContent = "" ;
185261
186262 try {
@@ -195,12 +271,31 @@ runButton.addEventListener("click", async () => {
195271 }
196272
197273 const events = await response . json ( ) ;
198- renderEvents ( events ) ;
274+ await renderEvents ( events ) ;
199275 } catch ( error ) {
200276 logToOutput ( `Network or server error: ${ error . message } ` , "error" ) ;
277+ } finally {
278+ isExecuting = false ;
279+ nextResolve = null ;
280+ runButton . disabled = false ;
281+ if ( clearButton ) clearButton . disabled = false ;
282+ editor . focus ( ) ;
283+ }
284+ } ) ;
285+
286+ clearButton . addEventListener ( "click" , ( ) => {
287+ try {
288+ clearCanvas ( ) ; // wipes Cody's canvas
289+ outputArea . textContent = "" ; // clears the log area
290+ drawnLines = [ ] ;
291+ codyState = { x : 250 , y : 250 , direction : 0 , color : "blue" } ;
292+ logToOutput ( "Canvas cleared" ) ;
293+ } catch ( error ) {
294+ logToOutput ( `Error while clearing: ${ error . message } ` , "error" ) ;
201295 }
202296} ) ;
203297
298+
204299// --- NEW: Event listener for Download button ---
205300if ( downloadButton ) {
206301 downloadButton . addEventListener ( "click" , ( ) => {
@@ -282,46 +377,94 @@ function logToOutput(message, type = "info") {
282377let drawnLines = [ ] ;
283378let codyState = { x : 250 , y : 250 , direction : 0 , color : "blue" } ;
284379
285- function renderEvents ( events ) {
286- if ( ! events || events . length === 0 ) return ;
287-
288- for ( const event of events ) {
289- switch ( event . type ) {
290- case "ClearEvent" :
291- drawnLines = [ ] ;
292- codyState = { x : 250 , y : 250 , direction : 0 , color : "blue" } ;
293- break ;
294- case "MoveEvent" :
295- if (
296- event . isPenDown &&
297- ( event . fromX !== event . toX || event . fromY !== event . toY )
298- ) {
299- drawnLines . push ( {
300- fromX : event . fromX ,
301- fromY : event . fromY ,
302- toX : event . toX ,
303- toY : event . toY ,
304- color : event . color ,
305- } ) ;
306- }
307- codyState = {
308- x : event . toX ,
309- y : event . toY ,
310- direction : event . newDirection ,
311- color : event . color ,
380+
381+
382+ async function renderEvents ( events ) {
383+
384+ try {
385+ if ( ! events || events . length === 0 ) return ;
386+ const initialSpeed = parseInt ( speedRange . value , 10 ) ;
387+ if ( initialSpeed === 0 && stepModal && ! stepModalShown ) {
388+ stepModalShown = true ;
389+ stepModal . classList . remove ( "hidden" ) ;
390+
391+ // Wait for the modal to be closed (event-driven)
392+ await new Promise ( ( resolve ) => {
393+ const onClose = ( ) => {
394+ stepModal . removeEventListener ( "closed" , onClose ) ;
395+ resolve ( ) ;
312396 } ;
313- break ;
314- case "SayEvent" :
315- logToOutput ( `Cody says: ${ event . message } ` ) ;
316- break ;
317- case "ErrorEvent" :
318- logToOutput ( `ERROR: ${ event . errorMessage } ` , "error" ) ;
319- break ;
397+ stepModal . addEventListener ( "closed" , onClose , { once : true } ) ;
398+ } ) ;
320399 }
400+
401+ for ( const event of events ) {
402+ const speed = parseInt ( speedRange . value , 10 ) ;
403+ const delay = speed === 0 ? null : ( speed === 1 ? 300 : 80 ) ;
404+ switch ( event . type ) {
405+ case "ClearEvent" :
406+ drawnLines = [ ] ;
407+ codyState = { x : 250 , y : 250 , direction : 0 , color : "blue" } ;
408+ break ;
409+
410+ case "MoveEvent" :
411+ if (
412+ event . isPenDown &&
413+ ( event . fromX !== event . toX || event . fromY !== event . toY )
414+ ) {
415+ drawnLines . push ( {
416+ fromX : event . fromX ,
417+ fromY : event . fromY ,
418+ toX : event . toX ,
419+ toY : event . toY ,
420+ color : event . color ,
421+ } ) ;
422+ }
423+ codyState = {
424+ x : event . toX ,
425+ y : event . toY ,
426+ direction : event . newDirection ,
427+ color : event . color ,
428+ } ;
429+ break ;
430+
431+ case "SayEvent" :
432+ logToOutput ( `Cody says: ${ event . message } ` ) ;
433+ break ;
434+
435+ case "ErrorEvent" :
436+ logToOutput ( `ERROR: ${ event . errorMessage } ` , "error" ) ;
437+ break ;
438+ }
439+
440+ redrawCanvas ( ) ;
441+
442+ if ( speed === 0 ) {
443+ // Show step modal if user switched to step mode mid-execution
444+ if ( stepModal && ! stepModalShown ) {
445+ stepModalShown = true ;
446+ stepModal . classList . remove ( "hidden" ) ;
447+ await new Promise ( ( resolve ) => {
448+ const onClose = ( ) => {
449+ stepModal . removeEventListener ( "closed" , onClose ) ;
450+ resolve ( ) ;
451+ } ;
452+ stepModal . addEventListener ( "closed" , onClose , { once : true } ) ;
453+ } ) ;
454+ }
455+ await waitForNextKey ( ) ; // step mode
456+ } else {
457+ await new Promise ( ( resolve ) => setTimeout ( resolve , delay ) ) ;
458+ }
459+ }
321460 }
322- redrawCanvas ( ) ;
461+ catch ( error ) {
462+ logToOutput ( `Rendering error: ${ error . message } ` , "error" ) ;
463+ throw error ;
464+ }
323465}
324466
467+
325468function redrawCanvas ( ) {
326469 ctx . clearRect ( 0 , 0 , drawingCanvas . width , drawingCanvas . height ) ;
327470 drawnLines . forEach ( ( line ) => {
0 commit comments