change UI, Filetree

This commit is contained in:
Irina Rueegg 2026-04-09 16:24:52 +02:00
parent 3ce4ef970b
commit b7bbd63bb3
4 changed files with 213 additions and 205 deletions

View File

@ -22,19 +22,15 @@ def main():
init_state() init_state()
left, right = st.columns([1, 3]) render_sidebar()
with left: show_editor = st.session_state.get("show_Editor", False)
choice = render_sidebar() show_chat = st.session_state.get("show_Chat", False)
with right: if show_editor:
if "Chat with AI Assistant" in choice: render_editor()
render_chat() if show_chat:
if "Code Editor" in choice: render_chat()
render_editor()
#elif choice == "File Explorer":
# st.subheader("File Explorer")
# st.info("File Explorer functionality is not implemented yet.")
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -43,109 +43,114 @@ def render_editor():
if not st.session_state.open_files: if not st.session_state.open_files:
st.info("Please select a file to edit.") st.info("Please select a file to edit.")
return return
else:
fm = FileManager()
fm = FileManager() try:
active_index = st.session_state.open_files.index(st.session_state.active_file)
except (ValueError, KeyError):
active_index = 0
tab_names = [Path(f).name for f in st.session_state.open_files] tab_names = [Path(f).name for f in st.session_state.open_files]
tabs = st.tabs(tab_names) tabs = st.tabs(tab_names)
for idx, file_path in enumerate(st.session_state.open_files): for idx, file_path in enumerate(st.session_state.open_files):
with tabs[idx]: with tabs[idx]:
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(Path(file_path)) st.session_state.files_content[file_path] = fm.read_file(Path(file_path))
file_language = LANG_MAP.get(Path(file_path).suffix, "text") file_language = LANG_MAP.get(Path(file_path).suffix, "text")
code = st_ace.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, auto_update=True,
height=400, height=400,
tab_size=4, # tab_size=4,
font_size=14, # font_size=14,
show_gutter=True, # show_gutter=True,
show_print_margin=False, # show_print_margin=False,
wrap=True # wrap=True
) )
st.session_state.files_content[file_path] = code if code != st.session_state.files_content[file_path]:
st.session_state.files_content[file_path] = code
cols = st.columns([1, 1, 1, 1]) cols = st.columns([1, 1, 1, 1])
with cols[0]: with cols[0]:
if st.button("Save Changes", key=f"save_{file_path}"): if st.button("Save Changes", key=f"save_{file_path}"):
content = st.session_state.files_content[file_path] content = st.session_state.files_content[file_path]
if fm.save_file(file_path, content): if fm.save_file(file_path, content):
st.success("File saved successfully!") st.success("File saved successfully!")
with cols[1]: 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]
st.success("File closed successfully!") st.success("File closed successfully!")
if st.session_state.active_file == file_path: if st.session_state.active_file == file_path:
st.session_state.active_file = ( st.session_state.active_file = (
st.session_state.open_files[0] st.session_state.open_files[0]
if st.session_state.open_files if st.session_state.open_files
else None else None
) )
st.rerun() # Refresh the page to update the UI st.rerun() # Refresh the page to update the UI
with cols[2]: with cols[2]:
if st.button("Rename File", key=f"rename_{file_path}"): if st.button("Rename File", key=f"rename_{file_path}"):
new_name = st.text_input("New File Name", key=f"new_name_{file_path}") new_name = st.text_input("New File Name", key=f"new_name_{file_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:
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: try:
fm.rename_file(file_path, new_name) fm.delete_file(file_path)
st.success(f"File renamed to '{new_name}' successfully!") st.success(f"File '{Path(file_path).name}' deleted successfully!")
st.rerun() st.rerun()
except Exception as e: except Exception as e:
st.error(f"Error renaming file: {e}") st.error(f"Error deleting file: {e}")
with cols[3]: if st.button("▶ Run Code", key="run_code"):
if st.button("Delete File", key=f"delete_{file_path}"): result = run_active_file()
try: if not result:
fm.delete_file(file_path) st.stop()
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"): st.subheader("Execution Output")
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["return_code"] == 0: if result["stdout"]:
st.success(f"Exit code: {result['return_code']}") st.text_area(
else: "Standard Output",
st.error(f"Exit code: {result['return_code']}") value=result["stdout"],
height=200,
disabled=True,
key="run_stdout")
if result["stdout"]: if result["stderr"]:
st.text_area( st.text_area(
"Standard Output", "Standard Error",
value=result["stdout"], value=result["stderr"],
height=200, height=200,
disabled=True, disabled=True,
key="run_stdout") key="run_stderr")
if not result["stdout"] and not result["stderr"]:
if result["stderr"]: st.info("No output produced by the code execution.")
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.")

View File

@ -1,4 +1,5 @@
import streamlit as st import streamlit as st
from streamlit_arborist import tree_view
from pathlib import Path from pathlib import Path
from backend.managers.file_manager import FileManager from backend.managers.file_manager import FileManager
@ -23,98 +24,45 @@ SUFFIX_MAP = {
"default": "📄" # Unbekannt "default": "📄" # Unbekannt
} }
def render_filetree(tree, parent_path=Path()): def build_arborist_tree(tree, parent_path=Path()):
nodes = []
for name, content in sorted(tree.items()): for name, content in sorted(tree.items()):
full_path = parent_path / name full_path = parent_path / name
node_id = str(full_path.as_posix())
if isinstance(content, dict): # Directory if isinstance(content, dict):
disp_name = name if len(name) <= 13 else (name[:10] + "...") nodes.append({
"id": node_id,
"name": f"{name}",
"children": build_arborist_tree(content, full_path)
})
else:
suffix = Path(name).suffix
icon = SUFFIX_MAP.get(suffix, SUFFIX_MAP["default"])
with st.expander(f"📁 {disp_name}", expanded=False): nodes.append({
render_filetree(content, full_path) "id": node_id,
with st.popover("+", key=f"popover_{full_path}"): "name": f"{icon} {name}",
st.write(f"**Create in {name}**") "key": f"key_{node_id}"
action = st.radio("Action", ["New File", "New Folder"], key=f"radio_{full_path}", label_visibility="collapsed") })
new_name = st.text_input("Name", key=f"input_{full_path}")
if st.button("Create", key=f"btn_{full_path}"): return nodes
if "/" in new_name or "\\" in new_name:
st.warning("Do not include slashes in names!")
elif new_name and action == "New Folder":
fm.create_folder(str(full_path), new_name)
st.success(f"Folder {new_name} created!")
st.rerun()
elif new_name and action == "New File":
fm.create_file(str(full_path), new_name)
st.success(f"File {new_name} created!")
st.rerun()
else: # File def render_filetree_arborist(tree):
suffix = Path(name).suffix data = build_arborist_tree(tree)
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") selected = tree_view(
data=data,
icons={"open": "📂", "closed": "📁"},
height=200,
selection=None,
select_internal_nodes=True,
open_by_default=False
)
with col_file: return selected
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")
@ -129,11 +77,69 @@ def render_sidebar():
add_more = st.container() add_more = st.container()
with workspace: with workspace:
if not tree: with st.container(border=False):
st.info("Workspace is empty.") if not tree:
st.info("Workspace is empty.")
else:
selected = render_filetree_arborist(tree)
else: if selected:
render_filetree(tree) selected_path = selected.get("id")
if st.session_state.last_selected != selected_path:
st.session_state.last_selected = selected_path
abs_path = fm.base_path / selected_path
if abs_path.is_file():
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()
elif abs_path.is_dir():
st.session_state.selected_folder = str(abs_path)
selected_folder = st.session_state.selected_folder
with st.popover(
f"📁 {Path(selected_folder).name}",
use_container_width=True,
key=f"{selected_folder}_popover_options"):
action = st.radio(
"Action",
["New File", "New Folder"],
key=f"radio_folder_options",
label_visibility="collapsed")
new_name = st.text_input("Name", key=f"input_folder_options")
btn_cols = st.columns([1,1,1])
with btn_cols[0]:
if st.button("Create", key=f"btn_create_subfile_or_subfolder"):
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(selected_path, new_name)
else:
fm.create_file(selected_path, new_name)
st.success(f"{action} created!")
st.session_state.selected_folder = None
st.rerun()
with btn_cols[1]:
if st.button("Cancel", key=f"cancel_folder_options"):
st.session_state.selected_folder = None
st.rerun()
with btn_cols[2]:
if st.button("Delete Folder", key=f"delete_folder"):
try:
fm.delete_folder(selected_path)
st.success(f"Folder '{Path(selected_folder).name}' deleted successfully!")
st.session_state.selected_folder = None
st.rerun()
except Exception as e:
st.error(f"Error deleting folder: {e}")
with add_more: with add_more:
with st.popover("⚙️ Explorer_Options", key=f"popover_options"): with st.popover("⚙️ Explorer_Options", key=f"popover_options"):
@ -160,13 +166,10 @@ def render_sidebar():
with navigation_section: with navigation_section:
st.sidebar.subheader("Options") st.sidebar.subheader("Options")
options = ["Code Editor", "Chat with AI Assistant"] st.sidebar.checkbox("Code Editor", key="show_Editor")
choice = [] st.sidebar.checkbox("Chat with AI Assistant", key="show_Chat")
for option in options: return
if st.sidebar.checkbox(option):
choice.append(option)
return choice
if __name__ == "__main__": if __name__ == "__main__":
render_sidebar() render_sidebar()

View File

@ -1,8 +1,12 @@
import streamlit as st import streamlit as st
def init_state(): def init_state():
# Sidebar state initialization # Sidebar
if "last_selected" not in st.session_state:
st.session_state.last_selected = None
if "selected_folder" not in st.session_state:
st.session_state.selected_folder = None
# Editor state initialization # Editor state initialization
if "open_files" not in st.session_state: if "open_files" not in st.session_state: