Debug feats: editor, file_manager
This commit is contained in:
parent
e59cd2849b
commit
3ce4ef970b
3
.gitignore
vendored
3
.gitignore
vendored
@ -47,3 +47,6 @@ htmlcov/
|
|||||||
# Data
|
# Data
|
||||||
data/processed/
|
data/processed/
|
||||||
data/raw/
|
data/raw/
|
||||||
|
|
||||||
|
# Workspace
|
||||||
|
workspace/
|
||||||
|
|||||||
@ -0,0 +1,9 @@
|
|||||||
|
class DebugLogger:
|
||||||
|
def __init__(self):
|
||||||
|
self.logs = []
|
||||||
|
|
||||||
|
def log(self, message):
|
||||||
|
self.logs.append(message)
|
||||||
|
|
||||||
|
def get_logs(self):
|
||||||
|
return self.logs
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
RUN_TIMEOUT = 30 # seconds
|
||||||
|
|
||||||
|
class ExecutionEngine:
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def run_code(self, active_file: Path) -> dict:
|
||||||
|
suffix = active_file.suffix
|
||||||
|
current_dir = active_file.parent.resolve()
|
||||||
|
|
||||||
|
if suffix == ".py":
|
||||||
|
cmd = ["py", active_file.name]
|
||||||
|
elif suffix == ".tex":
|
||||||
|
cmd = [
|
||||||
|
"pdflatex",
|
||||||
|
"-interaction=nonstopmode",
|
||||||
|
f"-output-directory={current_dir}",
|
||||||
|
active_file.name,
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
return {"stdout": "", "stderr": f"Unsupported file type: {suffix}", "rc": 1}
|
||||||
|
|
||||||
|
try:
|
||||||
|
proc = subprocess.run(
|
||||||
|
cmd,
|
||||||
|
cwd=current_dir,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=RUN_TIMEOUT,
|
||||||
|
)
|
||||||
|
return {"stdout": proc.stdout, "stderr": proc.stderr, "rc": proc.returncode}
|
||||||
|
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
return {"stdout": "", "stderr": f"Timed out after {RUN_TIMEOUT}s", "rc": -1}
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
return {"stdout": "", "stderr": str(e), "rc": -1}
|
||||||
|
except Exception as e:
|
||||||
|
return {"stdout": "", "stderr": str(e), "rc": -1}
|
||||||
@ -5,13 +5,150 @@ WORKSPACE = Path("workspace")
|
|||||||
WORKSPACE.mkdir(exist_ok=True)
|
WORKSPACE.mkdir(exist_ok=True)
|
||||||
|
|
||||||
class FileManager:
|
class FileManager:
|
||||||
def __init__(self, base_path=Path("workspace")):
|
def __init__(self, base_path=Path("workspace")) -> None:
|
||||||
self.base_path = Path(base_path)
|
self.base_path = Path(base_path)
|
||||||
self.base_path.mkdir(exist_ok=True)
|
self.base_path.mkdir(exist_ok=True)
|
||||||
|
|
||||||
# read_file content
|
def create_folder(self, relative_path: str, name: str) -> bool:
|
||||||
|
if not name:
|
||||||
|
st.error(f"Invalid folder name: {name}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if "/" in name or "\\" in name:
|
||||||
|
st.error(f"Invalid folder name (no slashes allowed): {name}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
name = Path(name)
|
||||||
|
if relative_path:
|
||||||
|
relative_path = Path(relative_path)
|
||||||
|
else:
|
||||||
|
relative_path = Path()
|
||||||
|
|
||||||
# save_file content
|
folder_path = (self.base_path / relative_path / name).resolve()
|
||||||
|
|
||||||
|
if not str(folder_path).startswith(str(self.base_path.resolve())):
|
||||||
|
st.error(f"Access denied: {relative_path}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
folder_path.mkdir(exist_ok=False)
|
||||||
|
return True
|
||||||
|
except FileExistsError:
|
||||||
|
st.warning(f"Folder already exists: {relative_path}")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
st.error(f"Error creating folder {relative_path}: {str(e)}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def create_file(self, relative_path: str, name: str) -> bool:
|
||||||
|
if not name or name.strip() == "" :
|
||||||
|
st.error(f"Invalid file name: {name}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
name = Path(name)
|
||||||
|
if not name.suffix:
|
||||||
|
name = name.with_suffix(".txt") # Default to .txt if no extension provided
|
||||||
|
|
||||||
|
if relative_path:
|
||||||
|
relative_path = Path(relative_path)
|
||||||
|
else:
|
||||||
|
relative_path = Path()
|
||||||
|
|
||||||
|
file_path = (self.base_path / relative_path / name).resolve()
|
||||||
|
|
||||||
|
if not str(file_path).startswith(str(self.base_path.resolve())):
|
||||||
|
st.error(f"Access denied: {relative_path}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
file_path.touch(exist_ok=False)
|
||||||
|
return True
|
||||||
|
except FileExistsError:
|
||||||
|
st.warning(f"File already exists: {relative_path}")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
st.error(f"Error creating file {relative_path}: {str(e)}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def read_file(self, relative_path: Path) -> str:
|
||||||
|
file_path = (relative_path).resolve()
|
||||||
|
|
||||||
|
if not file_path.exists():
|
||||||
|
st.error(f"File not found: {relative_path}")
|
||||||
|
return ""
|
||||||
|
if not file_path.is_file():
|
||||||
|
st.error(f"Path is not a file: {relative_path}")
|
||||||
|
return ""
|
||||||
|
if not str(file_path).startswith(str(self.base_path.resolve())):
|
||||||
|
st.error(f"Access denied: {relative_path}")
|
||||||
|
return ""
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(file_path, "r") as f:
|
||||||
|
return f.read()
|
||||||
|
except FileNotFoundError:
|
||||||
|
st.error(f"File not found: {relative_path}")
|
||||||
|
return ""
|
||||||
|
except Exception as e:
|
||||||
|
st.error(f"Error reading file {relative_path}: {str(e)}")
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def save_file(self, relative_path: str, content: str):
|
||||||
|
file_path = (Path(relative_path)).resolve()
|
||||||
|
|
||||||
|
if not str(file_path).startswith(str(self.base_path.resolve())):
|
||||||
|
st.error(f"Access denied: {relative_path}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(file_path, "w") as f:
|
||||||
|
f.write(content)
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
st.error(f"Error saving file {relative_path}: {str(e)}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def rename_file(self, old_relative_path: str, new_name: str) -> bool:
|
||||||
|
if not new_name or new_name.strip() == "":
|
||||||
|
st.error(f"Invalid file name: {new_name}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
file_type = Path(old_relative_path).suffix
|
||||||
|
new_name = Path(new_name)
|
||||||
|
|
||||||
|
if not Path(new_name).suffix == file_type:
|
||||||
|
new_name = Path(new_name).with_suffix(file_type) # Ensure the file extension remains the same
|
||||||
|
|
||||||
|
old_file_path = (self.base_path / Path(old_relative_path)).resolve()
|
||||||
|
new_file_path = old_file_path.parent / new_name
|
||||||
|
|
||||||
|
if not str(old_file_path).startswith(str(self.base_path.resolve())) or not str(new_file_path).startswith(str(self.base_path.resolve())):
|
||||||
|
st.error(f"Access denied: {old_relative_path}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
old_file_path.rename(new_file_path)
|
||||||
|
return True
|
||||||
|
except FileNotFoundError:
|
||||||
|
st.error(f"File not found: {old_relative_path}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def delete_file(self, relative_path):
|
||||||
|
file_path = (self.base_path / relative_path).resolve()
|
||||||
|
|
||||||
|
if not str(file_path).startswith(str(self.base_path.resolve())):
|
||||||
|
st.error(f"Access denied: {relative_path}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
file_path.unlink()
|
||||||
|
return True
|
||||||
|
except FileNotFoundError:
|
||||||
|
st.error(f"File not found: {relative_path}")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
st.error(f"Error deleting file {relative_path}: {str(e)}")
|
||||||
|
return False
|
||||||
|
|
||||||
def get_file_tree(self):
|
def get_file_tree(self):
|
||||||
def build_tree(path: Path):
|
def build_tree(path: Path):
|
||||||
|
|||||||
@ -1,15 +1,43 @@
|
|||||||
import streamlit as st
|
import streamlit as st
|
||||||
import streamlit_ace as st_ace
|
import streamlit_ace as st_ace
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from backend.managers.file_manager import FileManager
|
|
||||||
|
|
||||||
def render_editor():
|
from backend.managers.file_manager import FileManager
|
||||||
LANG_MAP = {
|
from backend.managers.execution_engine import ExecutionEngine
|
||||||
|
from backend.managers.debug_logger import DebugLogger
|
||||||
|
|
||||||
|
LANG_MAP = {
|
||||||
".py": "python", ".tex": "latex", ".js": "javascript",
|
".py": "python", ".tex": "latex", ".js": "javascript",
|
||||||
".html": "html", ".css": "css", ".sh": "bash",
|
".html": "html", ".css": "css", ".sh": "bash",
|
||||||
".json": "json", ".yaml": "yaml", ".yml": "yaml"
|
".json": "json", ".yaml": "yaml", ".yml": "yaml"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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")
|
st.subheader("Code Editor")
|
||||||
|
|
||||||
if not st.session_state.open_files:
|
if not st.session_state.open_files:
|
||||||
@ -26,26 +54,34 @@ def render_editor():
|
|||||||
st.session_state.active_file = file_path
|
st.session_state.active_file = file_path
|
||||||
|
|
||||||
if file_path not in st.session_state.files_content:
|
if file_path not in st.session_state.files_content:
|
||||||
st.session_state.files_content[file_path] = fm.read_file(file_path)
|
st.session_state.files_content[file_path] = fm.read_file(Path(file_path))
|
||||||
|
|
||||||
file_language = LANG_MAP.get(file_path.suffix, "text")
|
file_language = LANG_MAP.get(Path(file_path).suffix, "text")
|
||||||
|
|
||||||
code = st_ace(
|
code = st_ace.st_ace(
|
||||||
value=st.session_state.files_content[file_path],
|
value=st.session_state.files_content[file_path],
|
||||||
language=file_language,
|
language=file_language,
|
||||||
theme="monokai",
|
theme="monokai",
|
||||||
key=f"code_editor_{file_path}"
|
key=f"code_editor_{file_path}",
|
||||||
|
auto_update=True,
|
||||||
|
height=400,
|
||||||
|
tab_size=4,
|
||||||
|
font_size=14,
|
||||||
|
show_gutter=True,
|
||||||
|
show_print_margin=False,
|
||||||
|
wrap=True
|
||||||
)
|
)
|
||||||
|
|
||||||
st.session_state.files_content[file_path] = code
|
st.session_state.files_content[file_path] = code
|
||||||
|
|
||||||
col1, col2 = st.columns([1, 1])
|
cols = st.columns([1, 1, 1, 1])
|
||||||
with col1:
|
with cols[0]:
|
||||||
if st.button("Save Changes", key=f"save_{file_path}"):
|
if st.button("Save Changes", key=f"save_{file_path}"):
|
||||||
fm.save_file(file_path, st.session_state.files_content[file_path])
|
content = st.session_state.files_content[file_path]
|
||||||
st.success("File saved successfully!")
|
if fm.save_file(file_path, content):
|
||||||
|
st.success("File saved successfully!")
|
||||||
|
|
||||||
with col2:
|
with cols[1]:
|
||||||
if st.button("Close File", key=f"close_{file_path}"):
|
if st.button("Close File", key=f"close_{file_path}"):
|
||||||
st.session_state.open_files.remove(file_path)
|
st.session_state.open_files.remove(file_path)
|
||||||
del st.session_state.files_content[file_path]
|
del st.session_state.files_content[file_path]
|
||||||
@ -59,6 +95,58 @@ def render_editor():
|
|||||||
)
|
)
|
||||||
|
|
||||||
st.rerun() # Refresh the page to update the UI
|
st.rerun() # Refresh the page to update the UI
|
||||||
|
with cols[2]:
|
||||||
|
if st.button("Rename File", key=f"rename_{file_path}"):
|
||||||
|
new_name = st.text_input("New File Name", key=f"new_name_{file_path}")
|
||||||
|
if "/" in new_name or "\\" in new_name:
|
||||||
|
st.warning("Do not include slashes in names!")
|
||||||
|
elif new_name:
|
||||||
|
try:
|
||||||
|
fm.rename_file(file_path, new_name)
|
||||||
|
st.success(f"File renamed to '{new_name}' successfully!")
|
||||||
|
st.rerun()
|
||||||
|
except Exception as e:
|
||||||
|
st.error(f"Error renaming file: {e}")
|
||||||
|
|
||||||
|
with cols[3]:
|
||||||
|
if st.button("Delete File", key=f"delete_{file_path}"):
|
||||||
|
try:
|
||||||
|
fm.delete_file(file_path)
|
||||||
|
st.success(f"File '{Path(file_path).name}' deleted successfully!")
|
||||||
|
st.rerun()
|
||||||
|
except Exception as e:
|
||||||
|
st.error(f"Error deleting file: {e}")
|
||||||
|
|
||||||
|
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__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@ -4,100 +4,168 @@ from backend.managers.file_manager import FileManager
|
|||||||
|
|
||||||
fm = FileManager()
|
fm = FileManager()
|
||||||
|
|
||||||
|
SUFFIX_MAP = {
|
||||||
|
".py": "🐍", # Python
|
||||||
|
".js": "🟨", # JavaScript (Gelbes Quadrat/Logo)
|
||||||
|
".html": "🌐", # HTML (Web)
|
||||||
|
".css": "🎨", # CSS (Styling)
|
||||||
|
".json": "📦", # JSON (Datenpaket)
|
||||||
|
".yaml": "⚙️", # YAML (Konfiguration)
|
||||||
|
".yml": "⚙️", # YAML
|
||||||
|
".sh": "🐚", # Bash/Shell (Shell-Icon)
|
||||||
|
".md": "📝", # Markdown
|
||||||
|
".txt": "📄", # Text
|
||||||
|
".tex": "📑", # LaTeX
|
||||||
|
".c": "🔵", # C (Blaues Icon)
|
||||||
|
".cpp": "🔷", # C++
|
||||||
|
".java": "☕", # Java
|
||||||
|
"folder": "📁", # Ordner
|
||||||
|
"default": "📄" # Unbekannt
|
||||||
|
}
|
||||||
|
|
||||||
def render_filetree(tree, parent_path=Path()):
|
def render_filetree(tree, parent_path=Path()):
|
||||||
for name, content in sorted(tree.items()):
|
for name, content in sorted(tree.items()):
|
||||||
full_path = parent_path / name
|
full_path = parent_path / name
|
||||||
|
|
||||||
if isinstance(content, dict): # Directory
|
if isinstance(content, dict): # Directory
|
||||||
with st.sidebar.expander(f"📁 {name}", expanded=False):
|
disp_name = name if len(name) <= 13 else (name[:10] + "...")
|
||||||
render_filetree(content, full_path)
|
|
||||||
else: # File
|
|
||||||
cols= st.sidebar.columns([3, 1])
|
|
||||||
with cols[0]:
|
|
||||||
if st.sidebar.button(f"📄 {name}", key=str(full_path)):
|
|
||||||
abs_path = fm.base_path / full_path
|
|
||||||
|
|
||||||
if str(abs_path) not in st.session_state.open_files:
|
|
||||||
st.session_state.open_files.append(str(abs_path))
|
|
||||||
|
|
||||||
st.session_state.active_file = str(abs_path)
|
|
||||||
|
|
||||||
with cols[1]:
|
with st.expander(f"📁 {disp_name}", expanded=False):
|
||||||
show_options_key = f"show_options_{full_path}"
|
render_filetree(content, full_path)
|
||||||
if st.button("...", key=show_options_key):
|
with st.popover("+", key=f"popover_{full_path}"):
|
||||||
st.session_state[show_options_key] = not st.session_state.get(show_options_key, False)
|
st.write(f"**Create in {name}**")
|
||||||
|
action = st.radio("Action", ["New File", "New Folder"], key=f"radio_{full_path}", label_visibility="collapsed")
|
||||||
if st.session_state.get(show_options_key, False):
|
new_name = st.text_input("Name", key=f"input_{full_path}")
|
||||||
action = st.selectbox("", ["", "Rename", "Delete"], key=f"action_{full_path}", index=0)
|
|
||||||
|
|
||||||
if action == "Delete":
|
if st.button("Create", key=f"btn_{full_path}"):
|
||||||
st.warning("Are you sure you want to delete this file?")
|
|
||||||
if st.button("Yes, delete file", key=f"confirm_delete_{full_path}"):
|
|
||||||
try:
|
|
||||||
(fm.base_path / full_path).unlink()
|
|
||||||
st.experimental_rerun()
|
|
||||||
except Exception as e:
|
|
||||||
st.error(f"Error deleting file: {e}")
|
|
||||||
|
|
||||||
elif action == "Rename":
|
|
||||||
new_name = st.text_input("New Name", key=f"rename_{full_path}")
|
|
||||||
if "/" in new_name or "\\" in new_name:
|
if "/" in new_name or "\\" in new_name:
|
||||||
st.warning("Do not include slashes in names!")
|
st.warning("Do not include slashes in names!")
|
||||||
elif new_name:
|
elif new_name and action == "New Folder":
|
||||||
new_path = (fm.base_path / full_path.parent / new_name)
|
fm.create_folder(str(full_path), new_name)
|
||||||
try:
|
st.success(f"Folder {new_name} created!")
|
||||||
(fm.base_path / full_path).rename(new_path)
|
st.rerun()
|
||||||
st.experimental_rerun()
|
elif new_name and action == "New File":
|
||||||
except Exception as e:
|
fm.create_file(str(full_path), new_name)
|
||||||
st.error(f"Error renaming file: {e}")
|
st.success(f"File {new_name} created!")
|
||||||
|
st.rerun()
|
||||||
|
|
||||||
|
else: # File
|
||||||
|
suffix = Path(name).suffix
|
||||||
|
icon = SUFFIX_MAP.get(suffix, SUFFIX_MAP["default"])
|
||||||
|
disp_name = name if len(name) <= 13 else (name[:10] + "...")
|
||||||
|
|
||||||
|
col_file, col_opt = st.columns([0.85, 0.15], gap="small")
|
||||||
|
|
||||||
|
with col_file:
|
||||||
|
if st.button(f"{icon} {disp_name}", key=str(full_path)):
|
||||||
|
abs_path = fm.base_path / full_path
|
||||||
|
|
||||||
|
if str(abs_path) not in st.session_state.open_files:
|
||||||
|
st.session_state.open_files.append(str(abs_path))
|
||||||
|
|
||||||
|
st.session_state.active_file = str(abs_path)
|
||||||
|
st.rerun()
|
||||||
|
|
||||||
|
with col_opt:
|
||||||
|
with st.popover("⋮", key=f"opt_{full_path}"):
|
||||||
|
|
||||||
|
delete_key = f"confirm_delete_{full_path}"
|
||||||
|
if delete_key not in st.session_state:
|
||||||
|
st.session_state[delete_key] = False
|
||||||
|
|
||||||
|
if st.button(" **Delete** 🗑️", key=f"del_{full_path}"):
|
||||||
|
st.session_state[delete_key] = True
|
||||||
|
|
||||||
|
if st.session_state[delete_key]:
|
||||||
|
st.warning(f"Delete {name}?")
|
||||||
|
|
||||||
|
confirm_col, deny_col = st.columns([1, 1], gap="small")
|
||||||
|
with confirm_col:
|
||||||
|
if st.button("✔", key=f"yes_{full_path}"):
|
||||||
|
try:
|
||||||
|
fm.delete_file(str(full_path))
|
||||||
|
st.session_state[delete_key] = False
|
||||||
|
st.rerun()
|
||||||
|
except Exception as e:
|
||||||
|
st.error(f"Error deleting file: {e}")
|
||||||
|
with deny_col:
|
||||||
|
if st.button("✖", key=f"cancel_{full_path}"):
|
||||||
|
st.session_state[delete_key] = False
|
||||||
|
|
||||||
|
rename_key = f"rename_mode_{full_path}"
|
||||||
|
if rename_key not in st.session_state:
|
||||||
|
st.session_state[rename_key] = False
|
||||||
|
|
||||||
|
if st.button("**Rename** ✏️", key=f"ren_{full_path}"):
|
||||||
|
st.session_state[rename_key] = True
|
||||||
|
|
||||||
|
if st.session_state[rename_key]:
|
||||||
|
new_name = st.text_input("New Name", key=f"input_ren_{full_path}")
|
||||||
|
|
||||||
|
apply_col, cancel_col = st.columns([1, 1], gap="small")
|
||||||
|
with apply_col:
|
||||||
|
if st.button("Apply", key=f"apply_ren_{full_path}"):
|
||||||
|
try:
|
||||||
|
fm.rename_file(str(full_path), new_name)
|
||||||
|
st.session_state[rename_key] = False
|
||||||
|
st.rerun()
|
||||||
|
except Exception as e:
|
||||||
|
st.error(f"Error renaming file: {e}")
|
||||||
|
|
||||||
|
with cancel_col:
|
||||||
|
if st.button("Cancel", key=f"cancel_ren_{full_path}"):
|
||||||
|
st.session_state[rename_key] = False
|
||||||
|
|
||||||
def render_sidebar():
|
def render_sidebar():
|
||||||
st.sidebar.title("Navigation")
|
st.sidebar.title("Navigation")
|
||||||
|
|
||||||
navigation_section = st.sidebar.container()
|
|
||||||
file_explorer_section = st.sidebar.container()
|
file_explorer_section = st.sidebar.container()
|
||||||
|
navigation_section = st.sidebar.container()
|
||||||
|
|
||||||
|
with file_explorer_section:
|
||||||
|
st.subheader("File Explorer")
|
||||||
|
tree = fm.get_file_tree()
|
||||||
|
workspace = st.container()
|
||||||
|
add_more = st.container()
|
||||||
|
|
||||||
|
with workspace:
|
||||||
|
if not tree:
|
||||||
|
st.info("Workspace is empty.")
|
||||||
|
|
||||||
|
else:
|
||||||
|
render_filetree(tree)
|
||||||
|
|
||||||
|
with add_more:
|
||||||
|
with st.popover("⚙️ Explorer_Options", key=f"popover_options"):
|
||||||
|
action = st.radio(
|
||||||
|
"Action",
|
||||||
|
["New File", "New Folder"],
|
||||||
|
key=f"radio_explorer_options",
|
||||||
|
label_visibility="collapsed")
|
||||||
|
|
||||||
|
new_name = st.text_input("Name", key=f"input_explorer_options")
|
||||||
|
|
||||||
|
if st.button("Create", key=f"btn_create_file_or_folder"):
|
||||||
|
if "/" in new_name or "\\" in new_name:
|
||||||
|
st.warning("Do not include slashes in names!")
|
||||||
|
elif new_name:
|
||||||
|
if action == "New Folder":
|
||||||
|
fm.create_folder("", new_name)
|
||||||
|
else:
|
||||||
|
fm.create_file("", new_name)
|
||||||
|
st.success(f"{action} created!")
|
||||||
|
st.rerun()
|
||||||
|
|
||||||
|
st.sidebar.markdown("---")
|
||||||
|
|
||||||
with navigation_section:
|
with navigation_section:
|
||||||
|
st.sidebar.subheader("Options")
|
||||||
options = ["Code Editor", "Chat with AI Assistant"]
|
options = ["Code Editor", "Chat with AI Assistant"]
|
||||||
choice = []
|
choice = []
|
||||||
|
|
||||||
for option in options:
|
for option in options:
|
||||||
if st.sidebar.checkbox(option):
|
if st.sidebar.checkbox(option):
|
||||||
choice.append(option)
|
choice.append(option)
|
||||||
|
|
||||||
with file_explorer_section:
|
|
||||||
st.sidebar.subheader("File Explorer")
|
|
||||||
tree = fm.get_file_tree()
|
|
||||||
if not tree:
|
|
||||||
st.sidebar.info("Workspace is empty.")
|
|
||||||
else:
|
|
||||||
render_filetree(tree)
|
|
||||||
|
|
||||||
st.sidebar.markdown("---")
|
|
||||||
st.sidebar.write("Manage Files & Folders")
|
|
||||||
|
|
||||||
# Create Folder
|
|
||||||
new_folder_name = st.sidebar.text_input("New Folder Name", key="new_folder_name")
|
|
||||||
if "/" in new_folder_name or "\\" in new_folder_name:
|
|
||||||
st.warning("Do not include slashes in names!")
|
|
||||||
elif st.sidebar.button("Create Folder", key="create_folder") and new_folder_name:
|
|
||||||
new_folder_path = fm.base_path / new_folder_name
|
|
||||||
new_folder_path.mkdir(exist_ok=True)
|
|
||||||
st.success(f"Folder '{new_folder_name}' created successfully!")
|
|
||||||
st.experimental_rerun()
|
|
||||||
|
|
||||||
# Create File
|
|
||||||
new_file_name = st.sidebar.text_input("New File Name", key="new_file_name")
|
|
||||||
if "/" in new_file_name or "\\" in new_file_name:
|
|
||||||
st.warning("Do not include slashes in names!")
|
|
||||||
elif st.sidebar.button("Create File", key="create_file") and new_file_name:
|
|
||||||
new_file_path = fm.base_path / new_file_name
|
|
||||||
new_file_path.touch(exist_ok=True)
|
|
||||||
st.success(f"File '{new_file_name}' created successfully!")
|
|
||||||
st.experimental_rerun()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return choice
|
return choice
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
import streamlit as st
|
import streamlit as st
|
||||||
|
|
||||||
def init_state():
|
def init_state():
|
||||||
|
# Sidebar state initialization
|
||||||
|
|
||||||
|
|
||||||
# Editor state initialization
|
# Editor state initialization
|
||||||
if "open_files" not in st.session_state:
|
if "open_files" not in st.session_state:
|
||||||
st.session_state.open_files = []
|
st.session_state.open_files = []
|
||||||
@ -16,6 +19,9 @@ def init_state():
|
|||||||
|
|
||||||
if "code_suggestions" not in st.session_state:
|
if "code_suggestions" not in st.session_state:
|
||||||
st.session_state.code_suggestions = []
|
st.session_state.code_suggestions = []
|
||||||
|
|
||||||
|
if "code_execution_output" not in st.session_state:
|
||||||
|
st.session_state.code_execution_output = ""
|
||||||
|
|
||||||
# Chat state initialization
|
# Chat state initialization
|
||||||
if "chat_history" not in st.session_state:
|
if "chat_history" not in st.session_state:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user