2026-04-10 00:13:52 +02:00

201 lines
7.6 KiB
Python

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
else:
fm = FileManager()
try:
active_index = st.session_state.open_files.index(st.session_state.active_file)
except (ValueError, KeyError):
active_index = 0
st.session_state.active_tab = active_index
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 idx == st.session_state.active_tab:
st.session_state.active_file = file_path
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}"):
content = st.session_state.files_content[file_path]
if fm.save_file(file_path, content):
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)
del st.session_state.files_content[file_path]
st.success("File closed successfully!")
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() # Refresh the page to update the UI
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()