From efdd9351b3b94302336edaf8d782065230a66ef9 Mon Sep 17 00:00:00 2001 From: Livio Meuli Date: Mon, 25 May 2026 19:02:20 +0200 Subject: [PATCH] improvments --- frontend/app.py | 2 +- frontend/chat.py | 38 +++++++++++++++++++++++++++++++------- frontend/editor.py | 2 +- frontend/sidebar.py | 19 +++++++++++++++++-- frontend/state.py | 4 ++++ 5 files changed, 54 insertions(+), 11 deletions(-) diff --git a/frontend/app.py b/frontend/app.py index 0c5c5e9..0625395 100644 --- a/frontend/app.py +++ b/frontend/app.py @@ -39,7 +39,7 @@ def main(): """ """, unsafe_allow_html=True, diff --git a/frontend/chat.py b/frontend/chat.py index 4a78f1d..89bb0b6 100644 --- a/frontend/chat.py +++ b/frontend/chat.py @@ -1,18 +1,17 @@ """Chat view — renders both the normal chat interface and the Coding Agent mode.""" import asyncio +import json from pathlib import Path import streamlit as st from backend.managers.chat_manager import ChatManager from backend.managers.system_prompter import SystemPrompter +from backend.managers.search_manager import SearchManager from backend.agent.coding_agent import CodingAgent - from backend.managers.debug_logger import get_logger -logger = get_logger(__name__) -import asyncio -import json +logger = get_logger(__name__) # ── Agent Mode helpers ──────────────────────────────────────────────────────── @@ -377,6 +376,32 @@ def render_normal_chat(): user sends a message, they are prepended to that message as a context block. """ logger.info("Chat mode") + chat_manager: ChatManager = st.session_state.chat_manager + + # Apply model/token overrides from the Settings panel before any API call. + if st.session_state.get("selected_model"): + chat_manager.model = st.session_state.selected_model + if "chat_max_tokens" in st.session_state: + chat_manager.max_tokens = st.session_state.chat_max_tokens + + # Consume a debug message forwarded from the editor's "Debug with AI" button. + pending_debug = st.session_state.pop("pending_debug_message", None) + if pending_debug: + if not chat_manager.get_history(): + system_prompt = SystemPrompter.generate_prompt(_build_file_context()) + chat_manager.add_message("system", system_prompt) + with st.spinner("Sending debug info to AI..."): + try: + ai_response = chat_manager.send_message(pending_debug) + except Exception as e: + ai_response = f"Error: {e}" + st.session_state.chat_history.append({"role": "user", "content": pending_debug}) + st.session_state.chat_history.append( + {"role": "assistant", "content": ai_response} + ) + st.rerun() + return + # Replay the conversation history as chat bubbles (skip system messages). for message in st.session_state.chat_history: if message["role"] == "system": @@ -430,7 +455,6 @@ def render_normal_chat(): return # ── Normal chat message ─────────────────────────────────────────────── - chat_manager = st.session_state.chat_manager search_results = st.session_state.get("search_results", []) # 5g — System-prompt logic: inject on first message, update on file change. @@ -473,8 +497,8 @@ def render_normal_chat(): if st.button("🗑️ Clear Chat"): _clear_chat_dialog() - # REVIEW: duplicate widget key — "agent_mode" toggle is already rendered inside render_agent_mode(); - # having two st.toggle calls with the same key on the same page will raise a DuplicateWidgetID error. + # Toggle to switch to Agent Mode (render_normal_chat and render_agent_mode are + # mutually exclusive, so the same key here causes no DuplicateWidgetID conflict). st.toggle("Agent Mode", key="agent_mode") # 5h — Settings expander: file context toggle, model, token limit, custom prompt. diff --git a/frontend/editor.py b/frontend/editor.py index de2af8b..68b2877 100644 --- a/frontend/editor.py +++ b/frontend/editor.py @@ -126,7 +126,7 @@ def run_active_file(): except SyntaxError as e: result = {"stdout": "", "stderr": str(e), "return_code": -1, "ast_error": True} st.session_state.exec_results[active_file] = result - debug_logger.log_error(f"Syntax error: {e}") + logger.error("Syntax error: %s", e) return result with st.spinner(f"Running {Path(active_file).name}..."): diff --git a/frontend/sidebar.py b/frontend/sidebar.py index 373c71d..e265d61 100644 --- a/frontend/sidebar.py +++ b/frontend/sidebar.py @@ -253,14 +253,29 @@ def build_arborist_tree(tree, parent_path=Path()): def render_filetree_arborist(tree): - """Render the interactive file tree and return the currently selected node dict.""" + """Render the interactive file tree and return the currently selected node dict. + + Passes the active file's relative path as ``selection`` so the tree always + highlights whichever file is currently open in the editor, even when the + user switches tabs instead of clicking the tree. + """ data = build_arborist_tree(tree) + # Compute the node-ID of the currently active file (posix relative path) + # so the tree highlights it regardless of how the tab was opened. + active_selection = None + active_file = st.session_state.get("active_file") + if active_file: + try: + active_selection = str(Path(active_file).relative_to(fm.base_path).as_posix()) + except ValueError: + pass + selected = tree_view( data=data, icons={"open": "📂", "closed": "📁"}, height=400, - selection=None, + selection=active_selection, select_internal_nodes=True, # allow clicking folder names, not just files open_by_default=True ) diff --git a/frontend/state.py b/frontend/state.py index 5eda2d6..787dd81 100644 --- a/frontend/state.py +++ b/frontend/state.py @@ -78,6 +78,10 @@ def init_state(): if "code_execution_output" not in st.session_state: st.session_state.code_execution_output = "" + # Per-file execution results: {file_path: {stdout, stderr, return_code, ast_error}} + if "exec_results" not in st.session_state: + st.session_state.exec_results = {} + # ── Chat state ──────────────────────────────────────────────────────────── # Flat list of {"role": ..., "content": ...} dicts rendered as chat bubbles.