3131 const elDeviceBezel = document . getElementById ( "device-bezel" ) ;
3232 const elCanvas = document . getElementById ( "display_canvas" ) ;
3333 const elToast = document . getElementById ( "sim-toast" ) ;
34+ const elReplForm = document . getElementById ( "repl-form" ) ;
35+ const elReplInput = document . getElementById ( "repl-input" ) ;
36+ const elReplPrompt = document . getElementById ( "repl-prompt" ) ;
37+
38+ const replHistory = [ ] ;
39+ let replHistoryIdx = - 1 ;
3440
3541 // =========================================================================
3642 // Status & Console Helpers
5460 else if ( type === "warn" ) span . className = "log-warn" ;
5561 else if ( type === "error" ) span . className = "log-err" ;
5662 else if ( type === "dim" ) span . className = "log-dim" ;
63+ else if ( type === "prompt" ) span . className = "log-prompt" ;
64+ else if ( type === "repl-out" ) span . className = "log-repl-out" ;
5765
5866 span . textContent = text ;
5967 elConsole . appendChild ( span ) ;
@@ -519,6 +527,97 @@ if "display_driver" in sys.modules:
519527 applyTheme ( next ) ;
520528 }
521529
530+ // =========================================================================
531+ // Interactive REPL Logic (code.InteractiveConsole)
532+ // =========================================================================
533+
534+ let isMoreLines = false ;
535+
536+ async function handleReplSubmit ( e ) {
537+ if ( e ) e . preventDefault ( ) ;
538+ if ( ! elReplInput ) return ;
539+
540+ const line = elReplInput . value ;
541+ const trimmed = line . trim ( ) ;
542+
543+ if ( trimmed ) {
544+ replHistory . push ( line ) ;
545+ replHistoryIdx = replHistory . length ;
546+ }
547+ elReplInput . value = "" ;
548+
549+ const promptStr = isMoreLines ? "... " : ">>> " ;
550+ logConsole ( `${ promptStr } ${ line } \n` , "prompt" ) ;
551+
552+ try {
553+ const pyodide = await getPyodide ( ) ;
554+
555+ const pyWrapper = `
556+ import sys, code
557+
558+ _line_input = ${ JSON . stringify ( line ) }
559+ _main_mod = sys.modules.get("__main__")
560+ _main_dict = _main_mod.__dict__ if _main_mod else globals()
561+
562+ if "_pydevices_console" not in globals() or _pydevices_console is None or _pydevices_console.locals is not _main_dict:
563+ _pydevices_console = code.InteractiveConsole(locals=_main_dict)
564+
565+ _more = _pydevices_console.push(_line_input)
566+
567+ # Refresh display if LVGL is active
568+ try:
569+ if "lvgl" in sys.modules:
570+ _lv = sys.modules["lvgl"]
571+ if _lv.is_initialized():
572+ _lv.task_handler()
573+ if "display_driver" in sys.modules:
574+ _dd = sys.modules["display_driver"]
575+ for _drv in getattr(_dd, "_drivers", []):
576+ if hasattr(_drv, "display_drv") and hasattr(_drv.display_drv, "show"):
577+ _drv.display_drv.show()
578+ except Exception:
579+ pass
580+
581+ _more
582+ ` ;
583+ const more = await pyodide . runPythonAsync ( pyWrapper ) ;
584+ isMoreLines = Boolean ( more ) ;
585+
586+ if ( elReplPrompt ) {
587+ elReplPrompt . textContent = isMoreLines ? "..." : ">>>" ;
588+ }
589+ if ( elReplInput ) {
590+ elReplInput . placeholder = isMoreLines
591+ ? "Continue block (press Enter on empty line to execute)..."
592+ : "Type Python expression or command (e.g. dir(), lv.screen_active()...)" ;
593+ }
594+ } catch ( err ) {
595+ logConsole ( `${ err } \n` , "error" ) ;
596+ isMoreLines = false ;
597+ if ( elReplPrompt ) elReplPrompt . textContent = ">>>" ;
598+ }
599+ }
600+
601+ function handleReplKeydown ( e ) {
602+ if ( e . key === "ArrowUp" ) {
603+ if ( replHistory . length === 0 ) return ;
604+ e . preventDefault ( ) ;
605+ if ( replHistoryIdx > 0 ) replHistoryIdx -- ;
606+ else replHistoryIdx = 0 ;
607+ elReplInput . value = replHistory [ replHistoryIdx ] || "" ;
608+ } else if ( e . key === "ArrowDown" ) {
609+ if ( replHistory . length === 0 ) return ;
610+ e . preventDefault ( ) ;
611+ if ( replHistoryIdx < replHistory . length - 1 ) {
612+ replHistoryIdx ++ ;
613+ elReplInput . value = replHistory [ replHistoryIdx ] || "" ;
614+ } else {
615+ replHistoryIdx = replHistory . length ;
616+ elReplInput . value = "" ;
617+ }
618+ }
619+ }
620+
522621 // =========================================================================
523622 // DOM Event Bindings
524623 // =========================================================================
@@ -531,6 +630,13 @@ if "display_driver" in sys.modules:
531630 if ( elShareBtn ) elShareBtn . addEventListener ( "click" , shareCode ) ;
532631 if ( elClearConsoleBtn ) elClearConsoleBtn . addEventListener ( "click" , clearConsole ) ;
533632
633+ if ( elReplForm ) {
634+ elReplForm . addEventListener ( "submit" , handleReplSubmit ) ;
635+ }
636+ if ( elReplInput ) {
637+ elReplInput . addEventListener ( "keydown" , handleReplKeydown ) ;
638+ }
639+
534640 if ( elTemplateSelect ) {
535641 elTemplateSelect . addEventListener ( "change" , ( e ) => loadTemplate ( e . target . value ) ) ;
536642 }
0 commit comments