improvments

This commit is contained in:
Livio Meuli 2026-05-25 19:02:20 +02:00
parent 3891007fb2
commit efdd9351b3
5 changed files with 54 additions and 11 deletions

View File

@ -39,7 +39,7 @@ def main():
""" """
<style> <style>
.block-container { padding-top: 1rem; } .block-container { padding-top: 1rem; }
[data-testid="stSidebarContent"] { padding-top: 0rem; margin-top: -2rem; } [data-testid="stSidebarContent"] { padding-top: 1rem; }
</style> </style>
""", """,
unsafe_allow_html=True, unsafe_allow_html=True,

View File

@ -1,18 +1,17 @@
"""Chat view — renders both the normal chat interface and the Coding Agent mode.""" """Chat view — renders both the normal chat interface and the Coding Agent mode."""
import asyncio import asyncio
import json
from pathlib import Path from pathlib import Path
import streamlit as st import streamlit as st
from backend.managers.chat_manager import ChatManager from backend.managers.chat_manager import ChatManager
from backend.managers.system_prompter import SystemPrompter from backend.managers.system_prompter import SystemPrompter
from backend.managers.search_manager import SearchManager
from backend.agent.coding_agent import CodingAgent from backend.agent.coding_agent import CodingAgent
from backend.managers.debug_logger import get_logger from backend.managers.debug_logger import get_logger
logger = get_logger(__name__)
import asyncio logger = get_logger(__name__)
import json
# ── Agent Mode helpers ──────────────────────────────────────────────────────── # ── 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. user sends a message, they are prepended to that message as a context block.
""" """
logger.info("Chat mode") 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). # Replay the conversation history as chat bubbles (skip system messages).
for message in st.session_state.chat_history: for message in st.session_state.chat_history:
if message["role"] == "system": if message["role"] == "system":
@ -430,7 +455,6 @@ def render_normal_chat():
return return
# ── Normal chat message ─────────────────────────────────────────────── # ── Normal chat message ───────────────────────────────────────────────
chat_manager = st.session_state.chat_manager
search_results = st.session_state.get("search_results", []) search_results = st.session_state.get("search_results", [])
# 5g — System-prompt logic: inject on first message, update on file change. # 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"): if st.button("🗑️ Clear Chat"):
_clear_chat_dialog() _clear_chat_dialog()
# REVIEW: duplicate widget key — "agent_mode" toggle is already rendered inside render_agent_mode(); # Toggle to switch to Agent Mode (render_normal_chat and render_agent_mode are
# having two st.toggle calls with the same key on the same page will raise a DuplicateWidgetID error. # mutually exclusive, so the same key here causes no DuplicateWidgetID conflict).
st.toggle("Agent Mode", key="agent_mode") st.toggle("Agent Mode", key="agent_mode")
# 5h — Settings expander: file context toggle, model, token limit, custom prompt. # 5h — Settings expander: file context toggle, model, token limit, custom prompt.

View File

@ -126,7 +126,7 @@ def run_active_file():
except SyntaxError as e: except SyntaxError as e:
result = {"stdout": "", "stderr": str(e), "return_code": -1, "ast_error": True} result = {"stdout": "", "stderr": str(e), "return_code": -1, "ast_error": True}
st.session_state.exec_results[active_file] = result st.session_state.exec_results[active_file] = result
debug_logger.log_error(f"Syntax error: {e}") logger.error("Syntax error: %s", e)
return result return result
with st.spinner(f"Running {Path(active_file).name}..."): with st.spinner(f"Running {Path(active_file).name}..."):

View File

@ -253,14 +253,29 @@ def build_arborist_tree(tree, parent_path=Path()):
def render_filetree_arborist(tree): 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) 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( selected = tree_view(
data=data, data=data,
icons={"open": "📂", "closed": "📁"}, icons={"open": "📂", "closed": "📁"},
height=400, height=400,
selection=None, selection=active_selection,
select_internal_nodes=True, # allow clicking folder names, not just files select_internal_nodes=True, # allow clicking folder names, not just files
open_by_default=True open_by_default=True
) )

View File

@ -78,6 +78,10 @@ def init_state():
if "code_execution_output" not in st.session_state: if "code_execution_output" not in st.session_state:
st.session_state.code_execution_output = "" 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 ──────────────────────────────────────────────────────────── # ── Chat state ────────────────────────────────────────────────────────────
# Flat list of {"role": ..., "content": ...} dicts rendered as chat bubbles. # Flat list of {"role": ..., "content": ...} dicts rendered as chat bubbles.