diff --git a/backend/managers/file_manager.py b/backend/managers/file_manager.py index e69de29..e392dfc 100644 --- a/backend/managers/file_manager.py +++ b/backend/managers/file_manager.py @@ -0,0 +1,30 @@ +import streamlit as st +from pathlib import Path + +WORKSPACE = Path("workspace") +WORKSPACE.mkdir(exist_ok=True) + +class FileManager: + def __init__(self, base_path=Path("workspace")): + self.base_path = Path(base_path) + self.base_path.mkdir(exist_ok=True) + + # read_file content + + # save_file content + + def get_file_tree(self): + def build_tree(path: Path): + + tree = {} + + for item in sorted(path.iterdir()): + if item.is_dir(): + tree[item.name] = build_tree(item) + else: + tree[item.name] = None + return tree + return build_tree(self.base_path) + +if __name__ == "__main__": + FileManager() \ No newline at end of file diff --git a/frontend/chat.py b/frontend/chat.py index 1a06046..7070c73 100644 --- a/frontend/chat.py +++ b/frontend/chat.py @@ -3,19 +3,30 @@ import streamlit as st def render_chat(): st.subheader("Chat with AI Assistant") - if st.session_state.chat_history: - for message in st.session_state.chat_history: - st.markdown(f"**{message['role'].capitalize()}:** {message['content']}") + chat_section = st.container() + setup_section = st.container() + + with chat_section: + if st.session_state.chat_history: + for message in st.session_state.chat_history: + st.markdown(f"**{message['role'].capitalize()}:** {message['content']}") + + user_input = st.text_input("Type your message here:", key="chat_input") + + if st.button("Send", key="send_button") and user_input: + st.session_state.chat_history.append({"role": "user", "content": user_input}) + # Here you would typically call your AI assistant to get a response + # For demonstration, we'll just echo the user's message + ai_response = f"Echo: {user_input}" + st.session_state.chat_history.append({"role": "assistant", "content": ai_response}) + st.session_state.chat_input = "" # Clear input after sending - user_input = st.text_input("Type your message here:", key="chat_input") + with setup_section: + st.info("This is where you can set up your AI assistant. For now, this section is just a placeholder.") + st.toggle("Use debug system prompt", key="use_system_prompt", value=True) - if st.button("Send", key="send_button") and user_input: - st.session_state.chat_history.append({"role": "user", "content": user_input}) - # Here you would typically call your AI assistant to get a response - # For demonstration, we'll just echo the user's message - ai_response = f"Echo: {user_input}" - st.session_state.chat_history.append({"role": "assistant", "content": ai_response}) - st.session_state.chat_input = "" # Clear input after sending + # Here you could add options to configure the AI assistant, such as selecting a model, setting parameters, etc. + if __name__ == "__main__": render_chat() \ No newline at end of file diff --git a/frontend/editor.py b/frontend/editor.py index 5020986..7a22c59 100644 --- a/frontend/editor.py +++ b/frontend/editor.py @@ -1,17 +1,65 @@ import streamlit as st +import streamlit_ace as st_ace +from pathlib import Path +from backend.managers.file_manager import FileManager def render_editor(): + LANG_MAP = { + ".py": "python", ".tex": "latex", ".js": "javascript", + ".html": "html", ".css": "css", ".sh": "bash", + ".json": "json", ".yaml": "yaml", ".yml": "yaml" + } + st.subheader("Code Editor") - - if st.session_state.selected_file: - st.text_area( - "Edit your code here:", - value=st.session_state.file_content, - height=400, - key="code_editor" - ) - else: + + if not st.session_state.open_files: st.info("Please select a file to edit.") + return + + fm = FileManager() + + 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]: + 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(file_path) + + file_language = LANG_MAP.get(file_path.suffix, "text") + + code = st_ace( + value=st.session_state.files_content[file_path], + language=file_language, + theme="monokai", + key=f"code_editor_{file_path}" + ) + + st.session_state.files_content[file_path] = code + + col1, col2 = st.columns([1, 1]) + with col1: + if st.button("Save Changes", key=f"save_{file_path}"): + fm.save_file(file_path, st.session_state.files_content[file_path]) + st.success("File saved successfully!") + + with col2: + 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 + if __name__ == "__main__": render_editor() \ No newline at end of file diff --git a/frontend/sidebar.py b/frontend/sidebar.py index 7e26017..98dfd26 100644 --- a/frontend/sidebar.py +++ b/frontend/sidebar.py @@ -1,4 +1,55 @@ import streamlit as st +from pathlib import Path +from backend.managers.file_manager import FileManager + +fm = FileManager() + +def render_filetree(tree, parent_path=Path()): + for name, content in sorted(tree.items()): + full_path = parent_path / name + + if isinstance(content, dict): # Directory + with st.sidebar.expander(f"📁 {name}", expanded=False): + 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]: + show_options_key = f"show_options_{full_path}" + if st.button("...", key=show_options_key): + st.session_state[show_options_key] = not st.session_state.get(show_options_key, False) + + if st.session_state.get(show_options_key, False): + action = st.selectbox("", ["", "Rename", "Delete"], key=f"action_{full_path}", index=0) + + if action == "Delete": + 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: + st.warning("Do not include slashes in names!") + elif new_name: + new_path = (fm.base_path / full_path.parent / new_name) + try: + (fm.base_path / full_path).rename(new_path) + st.experimental_rerun() + except Exception as e: + st.error(f"Error renaming file: {e}") def render_sidebar(): st.sidebar.title("Navigation") @@ -16,9 +67,36 @@ def render_sidebar(): with file_explorer_section: st.sidebar.subheader("File Explorer") - st.sidebar.info("File Explorer functionality is not implemented yet.") - # selected_file = st.sidebar.file_uploader("Upload a file", type=["py", "txt", "md"]) + 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 diff --git a/frontend/state.py b/frontend/state.py index a340bb5..0b0f27d 100644 --- a/frontend/state.py +++ b/frontend/state.py @@ -1,20 +1,27 @@ import streamlit as st def init_state(): - if "selected_file" not in st.session_state: - st.session_state.selected_file = None + # Editor state initialization + if "open_files" not in st.session_state: + st.session_state.open_files = [] - if "file_content" not in st.session_state: - st.session_state.file_content = "" + if "files_content" not in st.session_state: + st.session_state.files_content = {} + + if "active_file" not in st.session_state: + st.session_state.active_file = None if "is_editing" not in st.session_state: st.session_state.is_editing = False + if "code_suggestions" not in st.session_state: + st.session_state.code_suggestions = [] + + # Chat state initialization if "chat_history" not in st.session_state: st.session_state.chat_history = [] - if "code_suggestions" not in st.session_state: - st.session_state.code_suggestions = [] + if __name__ == "__main__": init_state() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 5882de9..ada1cab 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,3 +18,6 @@ pytest-cov>=4.0.0 # Development & Utilities python-dotenv>=1.0.0 + +#For code editor functionality +streamlit-ace>=0.1.0