import streamlit as st import streamlit_ace as st_ace from pathlib import Path from backend.managers.file_manager import FileManager from backend.managers.execution_engine import ExecutionEngine from backend.managers.debug_logger import DebugLogger LANG_MAP = { ".py": "python", ".tex": "latex", ".js": "javascript", ".html": "html", ".css": "css", ".sh": "bash", ".json": "json", ".yaml": "yaml", ".yml": "yaml" } # ── Modals ──────────────────────────────────────────────────────────────────── @st.dialog("Rename File") def _rename_dialog(file_path: str): fm = FileManager() st.write(f"Current name: **{Path(file_path).name}**") new_name = st.text_input("New name:", value=Path(file_path).stem) col1, col2 = st.columns(2) with col1: if st.button("Confirm", type="primary", use_container_width=True): if not new_name.strip(): st.warning("Please enter a name.") elif "/" in new_name or "\\" in new_name: st.warning("Name must not contain slashes.") else: if fm.rename_file(file_path, new_name.strip()): ext = Path(file_path).suffix new_file_path = str(Path(file_path).parent / (Path(new_name.strip()).stem + ext)) i = st.session_state.open_files.index(file_path) st.session_state.open_files[i] = new_file_path st.session_state.files_content[new_file_path] = \ st.session_state.files_content.pop(file_path) if st.session_state.active_file == file_path: st.session_state.active_file = new_file_path st.rerun() else: st.error("Rename failed. Check that the file still exists.") with col2: if st.button("Cancel", use_container_width=True): st.rerun() @st.dialog("Delete File") def _delete_dialog(file_path: str): fm = FileManager() st.warning(f"Delete **{Path(file_path).name}**? This cannot be undone.") col1, col2 = st.columns(2) with col1: if st.button("Delete", type="primary", use_container_width=True): if fm.delete_file(file_path): st.session_state.open_files.remove(file_path) st.session_state.files_content.pop(file_path, None) if st.session_state.active_file == file_path: st.session_state.active_file = ( st.session_state.open_files[0] if st.session_state.open_files else None ) st.rerun() else: st.error("Delete failed. Check that the file still exists.") with col2: if st.button("Cancel", use_container_width=True): st.rerun() def run_active_file(): active_file = st.session_state.active_file if not active_file: st.warning("No active file to run.") return execution_engine = ExecutionEngine() debug_logger = DebugLogger() debug_logger.log(f"Executing code from {active_file}...") with st.spinner(f"Running {Path(active_file).name}..."): output = execution_engine.run_code(Path(active_file)) debug_logger.log("Execution completed.") st.session_state.code_execution_output = { "stdout": output["stdout"], "stderr": output["stderr"], "return_code": output["rc"] } result = st.session_state.code_execution_output return result def render_editor(): st.subheader("Code Editor") if not st.session_state.open_files: st.info("Please select a file to edit.") return fm = FileManager() # ── Tab bar via st.tabs() ───────────────────────────────────────────────── tab_names = [Path(f).name for f in st.session_state.open_files] tabs = st.tabs(tab_names) for idx, file_path in enumerate(st.session_state.open_files): with tabs[idx]: if file_path not in st.session_state.files_content: st.session_state.files_content[file_path] = fm.read_file(Path(file_path)) file_language = LANG_MAP.get(Path(file_path).suffix, "text") code = st_ace.st_ace( value=st.session_state.files_content[file_path], language=file_language, theme="monokai", key=f"code_editor_{file_path}", auto_update=True, height=400, ) if code != st.session_state.files_content[file_path]: st.session_state.files_content[file_path] = code cols = st.columns([1, 1, 1, 1]) with cols[0]: if st.button("Save Changes", key=f"save_{file_path}"): if fm.save_file(file_path, code): st.success("File saved successfully!") with cols[1]: if st.button("Close File", key=f"close_{file_path}"): st.session_state.open_files.remove(file_path) st.session_state.files_content.pop(file_path, None) st.session_state.active_file = ( st.session_state.open_files[0] if st.session_state.open_files else None ) st.rerun() with cols[2]: if st.button("Rename File", key=f"rename_{file_path}"): _rename_dialog(file_path) with cols[3]: if st.button("Delete File", key=f"delete_{file_path}"): _delete_dialog(file_path) if st.button("▶ Run Code", key="run_code"): result = run_active_file() if not result: st.stop() st.subheader("Execution Output") if result["return_code"] == 0: st.success(f"Exit code: {result['return_code']}") else: st.error(f"Exit code: {result['return_code']}") if result["stdout"]: st.text_area( "Standard Output", value=result["stdout"], height=200, disabled=True, key="run_stdout") if result["stderr"]: st.text_area( "Standard Error", value=result["stderr"], height=200, disabled=True, key="run_stderr") if not result["stdout"] and not result["stderr"]: st.info("No output produced by the code execution.") if __name__ == "__main__": render_editor()