outsource

This commit is contained in:
leart-ramushi 2026-05-26 18:15:59 +02:00
parent 6506d366fc
commit 2f0a61e854
3 changed files with 143 additions and 125 deletions

View File

@ -20,91 +20,22 @@ st.set_page_config(
layout="wide" layout="wide"
) )
st.markdown(""" <style> .stAppDeployButton, #MainMenu { display: none !important; } .main .block-container { padding-top: 1rem !important; } body.chat-open .main .block-container { margin-right: 400px; } iframe[height="1"] { display: none !important; } </style> """, unsafe_allow_html=True) st.markdown("""
<style>
# ---------------- CHAT PANEL INJECTION ---------------- .stMainBlockContainer { padding-top: 2rem !important; }
def inject_chat_panel(is_open: bool): .stAppDeployButton, #MainMenu { display: none !important; }
st.iframe(f""" .main .block-container { padding-top: 1rem !important; }
<script> body.chat-open .main .block-container { margin-right: 400px; }
const win = window.parent; iframe[height="1"] { display: none !important; }
const doc = win.document; [data-testid="stSidebar"] { z-index: 99 !important; }
</style>
const old = doc.getElementById('ai-chat-panel'); """, unsafe_allow_html=True)
if (old) old.remove();
doc.body.classList.remove('chat-open');
if ({'true' if is_open else 'false'}) {{
doc.body.classList.add('chat-open');
const panel = doc.createElement('div');
panel.id = 'ai-chat-panel';
panel.style.cssText = `
position: fixed;
top: 0;
right: 0;
width: 380px;
height: 100vh;
background: #ffffff;
border-left: 1px solid #e0e0e0;
z-index: 999999;
display: flex;
flex-direction: column;
box-shadow: -4px 0 24px rgba(0,0,0,0.08);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
`;
panel.innerHTML = `
<div style="padding:1rem 1.25rem; border-bottom:1px solid #e0e0e0; display:flex; align-items:center;">
<span style="color:#1a1a1a; font-weight:600; font-size:0.95rem;">💬 Ask AI</span>
</div>
<div id="ai-messages" style="flex:1; overflow-y:auto; padding:1rem; display:flex; flex-direction:column; gap:0.75rem;">
<div style="color:#888; font-size:0.82rem; text-align:center;">Ask anything about your code.</div>
</div>
<div style="padding:0.75rem 1rem; border-top:1px solid #e0e0e0; display:flex; gap:0.5rem;">
<input id="ai-input" type="text" placeholder="Ask about your code…"
style="flex:1; background:#f5f5f5; border:1px solid #ddd; border-radius:6px;
color:#1a1a1a; padding:0.5rem 0.75rem; font-size:0.85rem; outline:none;" />
<button onclick="sendMessage()"
style="background:#1a1a1a; color:#fff; border:none; border-radius:6px;
padding:0.5rem 0.9rem; font-size:0.85rem; font-weight:600; cursor:pointer;">
Send
</button>
</div>
`;
doc.body.appendChild(panel);
win.sendMessage = function() {{
const input = doc.getElementById('ai-input');
const msgs = doc.getElementById('ai-messages');
const text = input.value.trim();
if (!text) return;
const userMsg = doc.createElement('div');
userMsg.style.cssText = 'background:#1a1a1a; border-radius:8px 8px 2px 8px; padding:0.5rem 0.75rem; color:#fff; font-size:0.85rem; align-self:flex-end; max-width:85%;';
userMsg.textContent = text;
msgs.appendChild(userMsg);
input.value = '';
msgs.scrollTop = msgs.scrollHeight;
const aiMsg = doc.createElement('div');
aiMsg.style.cssText = 'background:#f0f0f0; border-radius:8px 8px 8px 2px; padding:0.5rem 0.75rem; color:#1a1a1a; font-size:0.85rem; align-self:flex-start; max-width:85%;';
aiMsg.textContent = '';
msgs.appendChild(aiMsg);
msgs.scrollTop = msgs.scrollHeight;
}};
doc.getElementById('ai-input').addEventListener('keydown', function(e) {{
if (e.key === 'Enter') win.sendMessage();
}});
}}
</script>
""", height=1)
# ---------------- MAIN ---------------- # ---------------- MAIN ----------------
def main(): def main():
file_manager = FileManager() file_manager = FileManager()
navigation = FileNavigation() navigation = FileNavigation()
chat = ChatInterface()
chat_manager = ChatManager() chat_manager = ChatManager()
execution_engine = ExecutionEngine() execution_engine = ExecutionEngine()
output_display = OutputDisplay() output_display = OutputDisplay()
@ -146,21 +77,19 @@ def main():
else: else:
st.session_state.editor_content = "" st.session_state.editor_content = ""
inject_chat_panel(st.session_state.chat_open) chat.inject_panel(st.session_state.chat_open)
# ---------------- MAIN UI ---------------- # ---------------- MAIN UI ----------------
if st.session_state.active_file: if st.session_state.active_file:
title_col, btn_col = st.columns([5, 1]) st.subheader(f"✏️ {Path(st.session_state.active_file).name}")
with title_col:
st.subheader(f"✏️ {Path(st.session_state.active_file).name}")
with btn_col:
run_clicked = st.button("▶ Run", type="primary", use_container_width=True)
editor = CodeEditor() editor = CodeEditor()
new_value = editor.display_code( new_value, run_clicked = editor.display_code(
st.session_state.editor_content, st.session_state.editor_content,
filename=st.session_state.active_file filename=st.session_state.active_file
) )
# ---------------- APPLY DETECTION ----------------
if new_value is not None: if new_value is not None:
if new_value != st.session_state.last_applied_content: if new_value != st.session_state.last_applied_content:
st.session_state.editor_content = new_value st.session_state.editor_content = new_value
@ -168,6 +97,7 @@ def main():
file_manager.save_file(st.session_state.active_file, new_value) file_manager.save_file(st.session_state.active_file, new_value)
st.success("Saved ✔") st.success("Saved ✔")
# ---------------- RUN ----------------
if run_clicked: if run_clicked:
with st.spinner("Running…"): with st.spinner("Running…"):
st.session_state.run_result = execution_engine.run_file( st.session_state.run_result = execution_engine.run_file(

View File

@ -1,45 +1,116 @@
import streamlit as st import streamlit as st
class ChatInterface: class ChatInterface:
""" """
Chat Interface für KI-Interaktion [1] Chat Interface for AI interaction, including the right-side slide-out panel.
""" """
def __init__(self): def __init__(self):
self.chat_messages = [] self.chat_messages = []
def display_chat(self, chat_manager): # ---------------- PANEL INJECTION ----------------
""" def inject_panel(self, is_open: bool):
Zeigt die Chat-Schnittstelle mit Konversationsverlauf [1] """Injects or removes the fixed right-side AI chat panel via st.iframe."""
""" st.iframe(f"""
st.title("🤖 KI-Assistent") <script>
const win = window.parent;
const doc = win.document;
const old = doc.getElementById('ai-chat-panel');
if (old) old.remove();
doc.body.classList.remove('chat-open');
if ({'true' if is_open else 'false'}) {{
doc.body.classList.add('chat-open');
const panel = doc.createElement('div');
panel.id = 'ai-chat-panel';
panel.style.cssText = `
position: fixed;
top: 0;
right: 0;
width: 380px;
height: 100vh;
background: #ffffff;
border-left: 1px solid #e0e0e0;
z-index: 999999;
display: flex;
flex-direction: column;
box-shadow: -4px 0 24px rgba(0,0,0,0.08);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
`;
panel.innerHTML = `
<div style="padding:1rem 1.25rem; border-bottom:1px solid #e0e0e0; display:flex; align-items:center;">
<span style="color:#1a1a1a; font-weight:600; font-size:0.95rem;">💬 Ask AI</span>
</div>
<div id="ai-messages" style="flex:1; overflow-y:auto; padding:1rem; display:flex; flex-direction:column; gap:0.75rem;">
<div style="color:#888; font-size:0.82rem; text-align:center;">Ask anything about your code.</div>
</div>
<div style="padding:0.75rem 1rem; border-top:1px solid #e0e0e0; display:flex; gap:0.5rem;">
<input id="ai-input" type="text" placeholder="Ask about your code…"
style="flex:1; background:#f5f5f5; border:1px solid #ddd; border-radius:6px;
color:#1a1a1a; padding:0.5rem 0.75rem; font-size:0.85rem; outline:none;" />
<button onclick="sendMessage()"
style="background:#1a1a1a; color:#fff; border:none; border-radius:6px;
padding:0.5rem 0.9rem; font-size:0.85rem; font-weight:600; cursor:pointer;">
Send
</button>
</div>
`;
doc.body.appendChild(panel);
win.sendMessage = function() {{
const input = doc.getElementById('ai-input');
const msgs = doc.getElementById('ai-messages');
const text = input.value.trim();
if (!text) return;
const userMsg = doc.createElement('div');
userMsg.style.cssText = 'background:#1a1a1a; border-radius:8px 8px 2px 8px; padding:0.5rem 0.75rem; color:#fff; font-size:0.85rem; align-self:flex-end; max-width:85%;';
userMsg.textContent = text;
msgs.appendChild(userMsg);
input.value = '';
msgs.scrollTop = msgs.scrollHeight;
const aiMsg = doc.createElement('div');
aiMsg.style.cssText = 'background:#f0f0f0; border-radius:8px 8px 8px 2px; padding:0.5rem 0.75rem; color:#1a1a1a; font-size:0.85rem; align-self:flex-start; max-width:85%;';
aiMsg.textContent = '';
msgs.appendChild(aiMsg);
msgs.scrollTop = msgs.scrollHeight;
}};
doc.getElementById('ai-input').addEventListener('keydown', function(e) {{
if (e.key === 'Enter') win.sendMessage();
}});
}}
</script>
""", height=1)
# ---------------- CHAT DISPLAY ----------------
def display_chat(self, chat_manager):
"""Renders the Streamlit chat interface (used when not in panel mode)."""
st.title("AI Assistant")
# Chat History anzeigen
for message in self.chat_messages: for message in self.chat_messages:
with st.chat_message(message["role"]): with st.chat_message(message["role"]):
st.markdown(message["content"]) st.markdown(message["content"])
# User Input if prompt := st.chat_input("Ask about your code..."):
if prompt := st.chat_input("Frage zum Code stellen..."):
# User Nachricht anzeigen
self.chat_messages.append({"role": "user", "content": prompt}) self.chat_messages.append({"role": "user", "content": prompt})
with st.chat_message("user"): with st.chat_message("user"):
st.markdown(prompt) st.markdown(prompt)
# AI Antwort generieren (via ChatManager)
with st.chat_message("assistant"): with st.chat_message("assistant"):
response = chat_manager.generate_response(prompt) response = chat_manager.generate_response(prompt)
st.markdown(response) st.markdown(response)
self.chat_messages.append({"role": "assistant", "content": response}) self.chat_messages.append({"role": "assistant", "content": response})
def add_message(self, role, content): # ---------------- HELPERS ----------------
""" def add_message(self, role: str, content: str):
Fügt eine Nachricht zur Konversation hinzu
"""
self.chat_messages.append({"role": role, "content": content}) self.chat_messages.append({"role": role, "content": content})
def clear_history(self): def clear_history(self):
"""
Löscht den Chat-Verlauf
"""
self.chat_messages.clear() self.chat_messages.clear()

View File

@ -1,29 +1,44 @@
from streamlit_ace import st_ace from streamlit_ace import st_ace
import streamlit as st
class CodeEditor: class CodeEditor:
def detect_language(self, filename): def detect_language(self, filename: str) -> str:
mapping = { mapping = {
".py": "python", ".py": "python",
".js": "javascript", ".js": "javascript",
".html": "html", ".html": "html",
".css": "css", ".css": "css",
".json": "json", ".json": "json",
".md": "markdown", ".md": "markdown",
} }
for ext, lang in mapping.items(): for ext, lang in mapping.items():
if filename.endswith(ext): if filename.endswith(ext):
return lang return lang
return "text" return "text"
def display_code(self, code_content, filename=""): def display_code(self, code_content: str, filename: str = "") -> tuple:
"""
Renders a toolbar row (Apply is built into st_ace, Run is next to it)
then the ACE editor below.
Returns (new_value, run_clicked) where new_value is None until Apply is hit.
"""
language = self.detect_language(filename) language = self.detect_language(filename)
return st_ace( # --- Toolbar: filename label + Run button aligned right ---
label_col, run_col = st.columns([6, 1])
with label_col:
st.caption(f" {filename.split('/')[-1] or filename}" if filename else "")
with run_col:
run_clicked = st.button(
"▶ Run",
key=f"run_{filename}",
type="primary",
use_container_width=True
)
new_value = st_ace(
value=code_content, value=code_content,
language=language, language=language,
theme="chrome", theme="chrome",
@ -33,5 +48,7 @@ class CodeEditor:
wrap=False, wrap=False,
auto_update=False, auto_update=False,
show_gutter=True, show_gutter=True,
show_print_margin=False show_print_margin=False,
) )
return new_value, run_clicked