Der code enthält fehler die eine korrekte ausführbarkeit verhindern. zudem könnte die nutzerfreundlichkeit und nutzerfahrung bei der verwendung sicher noch verbessert werden und das gui könnte ebenso noch ein paar bearbeitungen benötigen um es zu verbessern.korrigiere alle solche fehler und nenne mir den überarbeiteten und vollständigen code der gesamten anwendung.

zum beispiel gibt es diesen fehler:

(base) D:\_Projekte\_MyCodes\GitHub\AntSimulation_Gemini_o1>C:/Users/Admin/miniconda3/python.exe d:/_Projekte/_MyCodes/GitHub/AntSimulation_Gemini_o1/temp4.py
2024-11-18 05:48:34,132 - __main__ - INFO - New chat session created: 20241118_054834
Traceback (most recent call last):
  File "d:\_Projekte\_MyCodes\GitHub\AntSimulation_Gemini_o1\temp4.py", line 1176, in <module>
    ex = RobocoderApp()
  File "d:\_Projekte\_MyCodes\GitHub\AntSimulation_Gemini_o1\temp4.py", line 395, in __init__
    self.init_ui()
  File "d:\_Projekte\_MyCodes\GitHub\AntSimulation_Gemini_o1\temp4.py", line 656, in init_ui
    self.refresh_github_repos()
  File "d:\_Projekte\_MyCodes\GitHub\AntSimulation_Gemini_o1\temp4.py", line 872, in refresh_github_repos
    if self.github_token and self.github_username:
AttributeError: 'RobocoderApp' object has no attribute 'github_token'


Skript:

import sys
import os
import requests
import json
import datetime
import re
import logging
from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
    QPushButton, QTextEdit, QLineEdit, QLabel, QComboBox, QFileDialog,
    QMessageBox, QProgressBar, QSplitter, QInputDialog, QToolTip,
    QListWidget, QTabWidget, QDialog, QSizePolicy, QListWidgetItem,
    QAction, QMenu, QTreeView, QFrame, QFileSystemModel
)
from PyQt5.QtCore import (
    Qt, QThread, pyqtSignal, QSettings, QTimer,
    QRegularExpression, QRegularExpressionMatch
)
from PyQt5.QtGui import (
    QFont, QColor, QTextCursor, QTextCharFormat,
    QPalette, QIcon, QSyntaxHighlighter
)

# Configure root logger
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

class ConsoleWidget(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.initUI()
        self.is_highlighted = False
        self.blink_timer = QTimer(self)
        self.blink_timer.timeout.connect(self.blink)
        self.blink_count = 0

    def initUI(self):
        layout = QVBoxLayout(self)
        
        self.toggle_button = QPushButton("Console")
        self.toggle_button.clicked.connect(self.toggle_console)
        layout.addWidget(self.toggle_button)

        self.console_output = QTextEdit(self)
        self.console_output.setReadOnly(True)
        self.console_output.setVisible(False)
        self.console_output.setStyleSheet('''
            QTextEdit {
                background-color: #2b2b2b;
                color: #ffffff;
                font-family: Consolas, Monospace;
                font-size: 12px;
            }
        ''')
        layout.addWidget(self.console_output)

        self.setLayout(layout)
        self.setMaximumHeight(30)  # Height of toggle button

    def toggle_console(self):
        if self.console_output.isVisible():
            self.console_output.setVisible(False)
            self.setMaximumHeight(30)
        else:
            self.console_output.setVisible(True)
            self.setMaximumHeight(200)
        self.is_highlighted = False
        self.update_style()

    def log(self, message, level='INFO'):
        color = {
            'DEBUG': '#808080',
            'INFO': '#ffffff',
            'WARNING': '#ffa500',
            'ERROR': '#ff0000',
            'CRITICAL': '#ff00ff'
        }.get(level, '#ffffff')

        self.console_output.append(f'<font color="{color}">[{level}] {message}</font>')
        self.console_output.verticalScrollBar().setValue(self.console_output.verticalScrollBar().maximum())
        
        self.highlight()

    def highlight(self):
        if self.is_highlighted:
            self.blink()
        else:
            self.is_highlighted = True
            self.update_style()

    def update_style(self):
        if self.is_highlighted:
            self.toggle_button.setStyleSheet("""
                QPushButton {
                    background-color: #FFA500;
                    color: white;
                    font-weight: bold;
                }
            """)
        else:
            self.toggle_button.setStyleSheet("")

    def blink(self):
        if self.blink_count < 6:  # Blink 3 times (on-off-on-off-on-off)
            self.is_highlighted = not self.is_highlighted
            self.update_style()
            self.blink_count += 1
            if not self.blink_timer.isActive():
                self.blink_timer.start(500)  # Blink every 500ms
        else:
            self.blink_timer.stop()
            self.blink_count = 0
            self.is_highlighted = True
            self.update_style()

class ConsoleLogger(logging.Handler):
    def __init__(self, console_widget):
        super().__init__()
        self.console_widget = console_widget

    def emit(self, record):
        msg = self.format(record)
        self.console_widget.log(msg, record.levelname)

def setup_logger(console_widget):
    console_handler = ConsoleLogger(console_widget)
    console_handler.setLevel(logging.DEBUG)
    
    logger.addHandler(console_handler)
    logger.setLevel(logging.DEBUG)


class SettingsDialog(QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle("Settings")
        self.setModal(True)
        self.settings = QSettings("RobocoderApp", "Settings")
        
        layout = QVBoxLayout()
        
        # Robocoder API Key
        api_key_layout = QHBoxLayout()
        api_key_layout.addWidget(QLabel("Robocoder API Key:"))
        self.api_key_input = QLineEdit()
        self.api_key_input.setText(self.settings.value("api_key", ""))
        api_key_layout.addWidget(self.api_key_input)
        layout.addLayout(api_key_layout)
        
        # GitHub Access Token
        github_token_layout = QHBoxLayout()
        github_token_layout.addWidget(QLabel("GitHub Access Token:"))
        self.github_token_input = QLineEdit()
        self.github_token_input.setText(self.settings.value("github_token", ""))
        github_token_layout.addWidget(self.github_token_input)
        layout.addLayout(github_token_layout)
        
        # GitHub Username
        github_username_layout = QHBoxLayout()
        github_username_layout.addWidget(QLabel("GitHub Username:"))
        self.github_username_input = QLineEdit()
        self.github_username_input.setText(self.settings.value("github_username", ""))
        github_username_layout.addWidget(self.github_username_input)
        layout.addLayout(github_username_layout)
        
        # Auto-save Interval
        auto_save_layout = QHBoxLayout()
        auto_save_layout.addWidget(QLabel("Auto-save Interval (minutes):"))
        self.auto_save_input = QLineEdit()
        self.auto_save_input.setText(str(self.settings.value("auto_save_interval", 1)))
        auto_save_layout.addWidget(self.auto_save_input)
        layout.addLayout(auto_save_layout)
        
        # Proxy Settings
        proxy_layout = QHBoxLayout()
        proxy_layout.addWidget(QLabel("Proxy URL:"))
        self.proxy_input = QLineEdit()
        self.proxy_input.setText(self.settings.value("proxy_url", ""))
        proxy_layout.addWidget(self.proxy_input)
        layout.addLayout(proxy_layout)
        
        # Save Button
        save_button = QPushButton("Save")
        save_button.clicked.connect(self.save_settings)
        layout.addWidget(save_button)
        
        self.setLayout(layout)
    
    def save_settings(self):
        self.settings.setValue("api_key", self.api_key_input.text())
        self.settings.setValue("github_token", self.github_token_input.text())
        self.settings.setValue("github_username", self.github_username_input.text())
        self.settings.setValue("auto_save_interval", int(self.auto_save_input.text()))
        self.settings.setValue("proxy_url", self.proxy_input.text())
        self.accept()

class CodeHighlighter(QSyntaxHighlighter):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.highlighting_rules = []

        keyword_format = QTextCharFormat()
        keyword_format.setForeground(Qt.darkBlue)
        keyword_format.setFontWeight(QFont.Bold)
        keywords = [
            "def", "class", "import", "from", "for", "while", "if", "else",
            "elif", "try", "except", "finally", "return", "break", "continue",
            "with", "as", "pass", "lambda", "yield", "raise", "assert", "del"
        ]
        keyword_pattern = r'\b(' + '|'.join(keywords) + r')\b'
        self.highlighting_rules.append((QRegularExpression(keyword_pattern), keyword_format))

        # Strings
        string_format = QTextCharFormat()
        string_format.setForeground(Qt.darkGreen)
        self.highlighting_rules.append((QRegularExpression(r'".*?"'), string_format))
        self.highlighting_rules.append((QRegularExpression(r"'.*?'"), string_format))

        # Comments
        comment_format = QTextCharFormat()
        comment_format.setForeground(Qt.gray)
        self.highlighting_rules.append((QRegularExpression(r'#.*'), comment_format))

        # Function names
        function_format = QTextCharFormat()
        function_format.setForeground(Qt.blue)
        function_format.setFontItalic(True)
        self.highlighting_rules.append((QRegularExpression(r'\b[A-Za-z0-9_]+(?=\()'), function_format))

    def highlightBlock(self, text):
        for pattern, fmt in self.highlighting_rules:
            match_iter = pattern.globalMatch(text)
            while match_iter.hasNext():
                match: QRegularExpressionMatch = match_iter.next()
                start = match.capturedStart()
                length = match.capturedLength()
                self.setFormat(start, length, fmt)
        self.setCurrentBlockState(0)

class RobocoderAPI:
    def __init__(self, api_key):
        self.API_BASE_URL = "https://api.robocoders.ai"
        self.API_KEY = api_key
        self.session_id = None

    def create_session(self):
        url = f"{self.API_BASE_URL}/create-session"
        headers = {"Authorization": f"Bearer {self.API_KEY}"}
        response = requests.get(url, headers=headers, timeout=10)
        if response.status_code == 200:
            self.session_id = response.json().get("sid")
            if not self.session_id:
                raise Exception("Session ID not found in response.")
            return self.session_id
        else:
            raise Exception(f"Failed to create session: {response.text}")

    def chat_with_agent(self, prompt, agent, context=None):
        if not self.session_id:
            raise Exception("No active session. Please create a session first.")

        url = f"{self.API_BASE_URL}/chat"
        headers = {
            "Authorization": f"Bearer {self.API_KEY}",
            "Content-Type": "application/json"
        }
        data = {
            "sid": self.session_id,
            "prompt": prompt,
            "agent": agent
        }
        if context:
            data["context"] = context
            
        try:
            print("Request Data:", json.dumps(data, indent=4))  # Protokollierung der Anfrage
            with requests.post(url, headers=headers, json=data, stream=True) as response:
                response.raise_for_status()
                for line in response.iter_lines():
                    if line:
                        decoded_line = line.decode('utf-8')
                        try:
                            json_response = json.loads(decoded_line)
                            if "message" in json_response:
                                yield json_response["message"]
                            else:
                                print("Unexpected response format:", json_response)
                                yield f"Unexpected response format: {json_response}"
                        except json.JSONDecodeError:
                            print("Failed to decode JSON:", decoded_line)
                            yield f"Failed to decode response: {decoded_line}"
        except requests.exceptions.Timeout:
            error_message = "The request timed out. Please try again."
            print(error_message)
            yield error_message
        except requests.exceptions.RequestException as e:
            error_message = f"An error occurred while making the request: {str(e)}"
            print(f"Request failed: {e}")  # Protokollierung von Fehlern
            yield error_message
        

    def get_session_code(self, github_username):
        if not self.session_id:
            raise Exception("No active session. Please create a session first.")

        url = f"{self.API_BASE_URL}/get-session-code"
        headers = {
            "Authorization": f"Bearer {self.API_KEY}",
            "Content-Type": "application/json"
        }
        data = {
            "sid": self.session_id,
            "github_username": github_username
        }
        try:
            response = requests.post(url, headers=headers, json=data, timeout=10)
            response.raise_for_status()
            return response.json()
        except requests.exceptions.RequestException as e:
            raise Exception(f"Failed to get session code: {str(e)}")

class ChatWorker(QThread):
    finished = pyqtSignal(str)
    error = pyqtSignal(str)
    progress = pyqtSignal(int)
    partial_response = pyqtSignal(str)

    def __init__(self, api, prompt, agent, context=None):
        super().__init__()
        self.api = api
        self.prompt = prompt
        self.agent = agent
        self.context = context

    def run(self):
        try:
            self.progress.emit(10)
            full_response = ""
            for chunk in self.api.chat_with_agent(self.prompt, self.agent, self.context):
                if chunk.startswith("An error occurred") or chunk.startswith("The request timed out"):
                    self.error.emit(chunk)
                    self.progress.emit(100)
                    return
                full_response += chunk
                self.partial_response.emit(chunk)
                self.progress.emit(50)  # You might want to adjust this based on the actual progress
            self.progress.emit(90)
            self.finished.emit(full_response)
            self.progress.emit(100)
        except Exception as e:
            self.error.emit(str(e))
            self.progress.emit(100)

class APIKeyDialog(QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle("API Key")
        self.setWindowIcon(QIcon())  # Optionally set an icon
        layout = QVBoxLayout()

        self.api_key_input = QLineEdit()
        self.api_key_input.setEchoMode(QLineEdit.Password)
        layout.addWidget(QLabel("Enter your Robocoder API Key:"))
        layout.addWidget(self.api_key_input)

        buttons = QHBoxLayout()
        ok_button = QPushButton("OK")
        ok_button.clicked.connect(self.accept)
        cancel_button = QPushButton("Cancel")
        cancel_button.clicked.connect(self.reject)
        buttons.addStretch()
        buttons.addWidget(ok_button)
        buttons.addWidget(cancel_button)
        layout.addLayout(buttons)

        self.setLayout(layout)
        self.setFixedSize(400, 150)

    def get_api_key(self):
        return self.api_key_input.text().strip()

class RobocoderApp(QMainWindow):
    def __init__(self):
        super().__init__()
        self.logger = logging.getLogger(__name__)
        
        # Initialize attributes
        self.api = None
        self.api_key = self.load_api_key()
        self.chat_archives = {}
        self.current_chat_id = None
        self.settings_button = None
        self.auto_save_interval = int(self.load_setting("auto_save_interval", 1))
        
        self.init_ui()
        
        # Initialize ConsoleWidget
        self.console_widget = ConsoleWidget(self)
        self.statusBar().addPermanentWidget(self.console_widget)
        
        # Set up logging
        self.setup_logger()
        
        self.logger.info("RobocoderApp initialized successfully.")

        self.check_api_key()

        # Set up proxy
        self.setup_proxy()

        # Initialize auto-save timer
        self.auto_save_timer = QTimer(self)
        self.auto_save_timer.timeout.connect(self.auto_save)
        self.auto_save_timer.start(self.auto_save_interval * 60000)

        # Initialize first chat
        self.new_chat()

    def load_setting(self, key, default_value):
        settings = QSettings("Robocoder", "RobocoderApp")
        return settings.value(key, default_value)

    def load_api_key(self):
        settings = QSettings("Robocoder", "RobocoderApp")
        return settings.value("api_key", "")

    def save_api_key(self, api_key):
        settings = QSettings("Robocoder", "RobocoderApp")
        settings.setValue("api_key", api_key)

    def setup_logger(self):
        console_handler = ConsoleLogger(self.console_widget)
        console_handler.setLevel(logging.INFO)
        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        console_handler.setFormatter(formatter)
        self.logger.addHandler(console_handler)

    def check_api_key(self):
        if not self.api_key:
            self.logger.warning("API Key not found. Please set up your API key in the settings.")
            self.highlight_settings_button()
        else:
            try:
                self.api = RobocoderAPI(self.api_key)
                if self.api.validate_key():
                    self.logger.info("API Key is valid.")
                    self.save_api_key(self.api_key)  # Save the valid key
                else:
                    self.logger.error("Invalid API Key. Please check your API key in the settings.")
                    self.highlight_settings_button()
                    self.api_key = None  # Clear the invalid key
                    self.save_api_key("")  # Clear the saved key
            except Exception as e:
                self.logger.error(f"Error validating API Key: {str(e)}")
                self.highlight_settings_button()
                self.api_key = None  # Clear the key on error
                self.save_api_key("")  # Clear the saved key

    def highlight_settings_button(self):
        if hasattr(self, 'settings_button'):
            self.settings_button.setStyleSheet("""
                QPushButton {
                    background-color: #FFA500;
                    color: white;
                    font-weight: bold;
                }
                QPushButton::after {
                    content: " !";
                    color: red;
                }
            """)

    def setup_proxy(self):
        if self.proxy_url:
            os.environ['HTTP_PROXY'] = self.proxy_url
            os.environ['HTTPS_PROXY'] = self.proxy_url
            logger.info(f"Proxy set to {self.proxy_url}")
        else:
            logger.info("No proxy set.")

    def open_settings(self):
        logger.info("Opening settings dialog.")
        dialog = SettingsDialog(self)
        if dialog.exec_() == QDialog.Accepted:
            self.api_key = self.settings.value("api_key", "")
            self.github_token = self.settings.value("github_token", "")
            self.github_username = self.settings.value("github_username", "")
            self.auto_save_interval = int(self.settings.value("auto_save_interval", 1))
            self.proxy_url = self.settings.value("proxy_url", "")
            if self.api_key:
                self.api = RobocoderAPI(self.api_key)
                self.setup_proxy()
                self.auto_save_timer.setInterval(self.auto_save_interval * 60000)
                logger.info("Settings updated successfully.")
            else:
                logger.warning("API Key cannot be empty.")
                QMessageBox.warning(self, "Invalid Input", "API Key cannot be empty.")
                self.open_settings()

    def init_ui(self):
        self.setWindowTitle('Robocoder Application')
        self.setGeometry(100, 100, 1400, 900)
        self.setWindowIcon(QIcon())  # Optionally set an application icon

        # Main widget and layout
        main_widget = QWidget(self)
        main_layout = QVBoxLayout(main_widget)

        # Create menu bar
        menubar = self.menuBar()
        file_menu = menubar.addMenu('File')

        # Create settings button
        self.settings_button = QPushButton("Settings")
        self.settings_button.clicked.connect(self.open_settings)
        menubar.setCornerWidget(self.settings_button, Qt.TopRightCorner)

        # Create status bar
        self.status_bar = self.statusBar()
        
        # Add Settings action
        settings_action = QAction('Settings', self)
        settings_action.triggered.connect(self.open_settings)
        file_menu.addAction(settings_action)

        # Add New Chat action
        new_chat_action = QAction('New Chat', self)
        new_chat_action.triggered.connect(self.new_chat)
        file_menu.addAction(new_chat_action)

        # Add Export Chat action
        export_chat_action = QAction('Export Chat', self)
        export_chat_action.triggered.connect(self.export_chat)
        file_menu.addAction(export_chat_action)

        # Main content layout
        content_layout = QHBoxLayout()

        # Left panel for chat archives and file tree
        left_panel = QWidget()
        left_layout = QVBoxLayout()
        left_layout.setContentsMargins(10, 10, 10, 10)
        left_layout.setSpacing(10)

        # Chat archives
        self.chat_list = QListWidget()
        self.chat_list.setToolTip("Select a chat archive to load")
        self.chat_list.itemClicked.connect(self.load_chat)
        left_layout.addWidget(QLabel('Chat Archives:'))
        left_layout.addWidget(self.chat_list)

        # File tree
        self.file_tree = QTreeView()
        self.file_tree.setToolTip("View and select files in the current chat session")
        self.file_tree.clicked.connect(self.open_file_from_tree)
        left_layout.addWidget(QLabel('Files:'))
        left_layout.addWidget(self.file_tree)

        left_panel.setLayout(left_layout)
        left_panel.setFixedWidth(250)

        # Middle panel for chat
        middle_panel = QWidget()
        middle_layout = QVBoxLayout()
        middle_layout.setContentsMargins(10, 10, 10, 10)
        middle_layout.setSpacing(10)

        self.chat_history = QTextEdit()
        self.chat_history.setReadOnly(True)
        chat_font = QFont("Segoe UI", 11)
        self.chat_history.setFont(chat_font)
        middle_layout.addWidget(self.chat_history)

        agent_layout = QHBoxLayout()
        agent_label = QLabel('Select Agent:')
        self.agent_combo = QComboBox()
        self.agent_combo.addItems(["GeneralCodingAgent", "RepoAgent", "FrontEndAgent"])
        self.agent_combo.setToolTip("Select the AI agent you want to interact with")
        agent_layout.addWidget(agent_label)
        agent_layout.addWidget(self.agent_combo)
        agent_layout.addStretch()
        middle_layout.addLayout(agent_layout)

        prompt_layout = QHBoxLayout()
        self.prompt_input = QTextEdit()  # Changed from QLineEdit to QTextEdit
        self.prompt_input.setPlaceholderText("Type your message here...")
        self.prompt_input.setToolTip("Enter your message or question for the AI")
        self.prompt_input.setMaximumHeight(100)
        self.send_button = QPushButton('Send')
        self.send_button.setToolTip("Send your message to the AI")
        self.send_button.clicked.connect(self.send_message)
        prompt_layout.addWidget(self.prompt_input)
        prompt_layout.addWidget(self.send_button)
        middle_layout.addLayout(prompt_layout)

        self.progress_bar = QProgressBar()
        self.progress_bar.setVisible(False)
        middle_layout.addWidget(self.progress_bar)

        # GitHub Repository Shortcuts
        github_layout = QHBoxLayout()
        github_layout.addWidget(QLabel('GitHub Repositories:'))
        self.repo_combo = QComboBox()
        self.repo_combo.setToolTip("Select a GitHub repository")
        github_layout.addWidget(self.repo_combo)
        self.refresh_repos_button = QPushButton('Refresh')
        self.refresh_repos_button.setToolTip("Refresh the list of GitHub repositories")
        self.refresh_repos_button.clicked.connect(self.refresh_github_repos)
        github_layout.addWidget(self.refresh_repos_button)
        middle_layout.addLayout(github_layout)

        middle_panel.setLayout(middle_layout)

        # Right panel for file content
        right_panel = QWidget()
        right_layout = QVBoxLayout()
        right_layout.setContentsMargins(10, 10, 10, 10)
        right_layout.setSpacing(10)

        self.file_content = QTextEdit()
        self.file_content.setReadOnly(False)
        self.highlighter = CodeHighlighter(self.file_content.document())
        right_layout.addWidget(self.file_content)

        # Save file button
        self.save_file_button = QPushButton('Save File')
        self.save_file_button.setToolTip("Save changes to the current file")
        self.save_file_button.clicked.connect(self.save_file)
        right_layout.addWidget(self.save_file_button)

        right_panel.setLayout(right_layout)

        # Add panels to content layout
        content_layout.addWidget(left_panel)
        content_layout.addWidget(middle_panel, 2)
        content_layout.addWidget(right_panel, 1)

        main_layout.addLayout(content_layout)

        # Console widget
        self.console_widget = ConsoleWidget(self)
        main_layout.addWidget(self.console_widget)

        # Set the main widget
        self.setCentralWidget(main_widget)

        # Initialize session
        self.new_chat()

        # Set up auto-save timer
        self.auto_save_timer = QTimer(self)
        self.auto_save_timer.timeout.connect(self.auto_save)
        self.auto_save_timer.start(self.auto_save_interval * 60000)  # Auto-save based on interval

        # Refresh GitHub repositories
        self.refresh_github_repos()

        self.logger.info("Application UI initialized successfully.")

        # Set application-wide style
        self.setStyleSheet("""
            QMainWindow, QWidget {
                background-color: #2b2b2b;
                color: #e0e0e0;
            }
            QLabel {
                font-size: 14px;
                font-weight: bold;
            }
            QListWidget, QTextEdit, QLineEdit, QTreeView {
                background-color: #363636;
                border: 1px solid #4a4a4a;
                border-radius: 5px;
                padding: 5px;
                color: #e0e0e0;
                font-size: 13px;
            }
            QPushButton {
                background-color: #4a4a4a;
                color: #e0e0e0;
                border: none;
                padding: 8px 15px;
                border-radius: 5px;
                font-size: 13px;
                font-weight: bold;
            }
            QPushButton:hover {
                background-color: #5a5a5a;
            }
            QComboBox {
                background-color: #363636;
                border: 1px solid #4a4a4a;
                border-radius: 5px;
                padding: 5px;
                color: #e0e0e0;
                font-size: 13px;
            }
            QTabWidget::pane {
                border: 1px solid #4a4a4a;
                border-radius: 5px;
            }
            QTabBar::tab {
                background-color: #363636;
                color: #e0e0e0;
                padding: 8px 15px;
                border-top-left-radius: 5px;
                border-top-right-radius: 5px;
            }
            QTabBar::tab:selected {
                background-color: #4a4a4a;
            }
        """)

        # Create menu bar
        menubar = self.menuBar()
        file_menu = menubar.addMenu('File')
        
        # Add Settings action
        settings_action = QAction('Settings', self)
        settings_action.triggered.connect(self.open_settings)
        file_menu.addAction(settings_action)

        # Add New Chat action
        new_chat_action = QAction('New Chat', self)
        new_chat_action.triggered.connect(self.new_chat)
        file_menu.addAction(new_chat_action)

        # Add Export Chat action
        export_chat_action = QAction('Export Chat', self)
        export_chat_action.triggered.connect(self.export_chat)
        file_menu.addAction(export_chat_action)

        # Main layout
        main_layout = QHBoxLayout()

        # Left panel for chat archives and file tree
        left_panel = QWidget()
        left_layout = QVBoxLayout()
        left_layout.setContentsMargins(10, 10, 10, 10)
        left_layout.setSpacing(10)

        # Chat archives
        self.chat_list = QListWidget()
        self.chat_list.setToolTip("Select a chat archive to load")
        self.chat_list.itemClicked.connect(self.load_chat)
        left_layout.addWidget(QLabel('Chat Archives:'))
        left_layout.addWidget(self.chat_list)

        # File tree
        self.file_tree = QTreeView()
        self.file_tree.setToolTip("View and select files in the current chat session")
        self.file_tree.clicked.connect(self.open_file_from_tree)
        left_layout.addWidget(QLabel('Files:'))
        left_layout.addWidget(self.file_tree)

        left_panel.setLayout(left_layout)
        left_panel.setFixedWidth(250)

        # Middle panel for chat
        middle_panel = QWidget()
        middle_layout = QVBoxLayout()
        middle_layout.setContentsMargins(10, 10, 10, 10)
        middle_layout.setSpacing(10)

        self.chat_history = QTextEdit()
        self.chat_history.setReadOnly(True)
        chat_font = QFont("Segoe UI", 11)
        self.chat_history.setFont(chat_font)
        middle_layout.addWidget(self.chat_history)

        agent_layout = QHBoxLayout()
        agent_label = QLabel('Select Agent:')
        self.agent_combo = QComboBox()
        self.agent_combo.addItems(["GeneralCodingAgent", "RepoAgent", "FrontEndAgent"])
        self.agent_combo.setToolTip("Select the AI agent you want to interact with")
        agent_layout.addWidget(agent_label)
        agent_layout.addWidget(self.agent_combo)
        agent_layout.addStretch()
        middle_layout.addLayout(agent_layout)

        prompt_layout = QHBoxLayout()
        self.prompt_input = QLineEdit()
        self.prompt_input.setPlaceholderText("Type your message here...")
        self.prompt_input.setToolTip("Enter your message or question for the AI")
        self.prompt_input.returnPressed.connect(self.send_message)
        self.send_button = QPushButton('Send')
        self.send_button.setToolTip("Send your message to the AI")
        self.send_button.clicked.connect(self.send_message)
        prompt_layout.addWidget(self.prompt_input)
        prompt_layout.addWidget(self.send_button)
        middle_layout.addLayout(prompt_layout)

        self.progress_bar = QProgressBar()
        self.progress_bar.setVisible(False)
        middle_layout.addWidget(self.progress_bar)

        # GitHub Repository Shortcuts
        github_layout = QHBoxLayout()
        github_layout.addWidget(QLabel('GitHub Repositories:'))
        self.repo_combo = QComboBox()
        self.repo_combo.setToolTip("Select a GitHub repository")
        github_layout.addWidget(self.repo_combo)
        self.refresh_repos_button = QPushButton('Refresh')
        self.refresh_repos_button.setToolTip("Refresh the list of GitHub repositories")
        self.refresh_repos_button.clicked.connect(self.refresh_github_repos)
        github_layout.addWidget(self.refresh_repos_button)
        middle_layout.addLayout(github_layout)

        middle_panel.setLayout(middle_layout)

        # Right panel for file content
        right_panel = QWidget()
        right_layout = QVBoxLayout()
        right_layout.setContentsMargins(10, 10, 10, 10)
        right_layout.setSpacing(10)

        self.file_content = QTextEdit()
        self.file_content.setReadOnly(False)
        self.highlighter = CodeHighlighter(self.file_content.document())
        right_layout.addWidget(self.file_content)

        # Save file button
        self.save_file_button = QPushButton('Save File')
        self.save_file_button.setToolTip("Save changes to the current file")
        self.save_file_button.clicked.connect(self.save_file)
        right_layout.addWidget(self.save_file_button)

        right_panel.setLayout(right_layout)

        # Add panels to main layout
        main_layout.addWidget(left_panel)
        main_layout.addWidget(middle_panel, 2)
        main_layout.addWidget(right_panel, 1)

        # Central widget
        central_widget = QWidget()
        central_widget.setLayout(main_layout)
        self.setCentralWidget(central_widget)

        # Status bar
        self.statusBar().showMessage("Ready")

        # Initialize session
        self.new_chat()

        # Set up auto-save timer
        self.auto_save_timer = QTimer(self)
        self.auto_save_timer.timeout.connect(self.auto_save)
        self.auto_save_timer.start(60000)  # Auto-save every minute

        # Refresh GitHub repositories
        self.refresh_github_repos()

    def create_chat_directory(self):
        chat_dir = self.get_chat_directory()
        os.makedirs(chat_dir, exist_ok=True)

    def update_file_tree(self):
        chat_dir = self.get_chat_directory()
        model = QFileSystemModel()
        model.setRootPath(chat_dir)
        self.file_tree.setModel(model)
        self.file_tree.setRootIndex(model.index(chat_dir))
        self.file_tree.setColumnWidth(0, 200)

    def open_file_from_tree(self, index):
        file_path = self.file_tree.model().filePath(index)
        if os.path.isfile(file_path):
            self.open_file(file_path)

    def refresh_github_repos(self):
        if self.github_token and self.github_username:
            try:
                headers = {'Authorization': f'token {self.github_token}'}
                response = requests.get(f'https://api.github.com/users/{self.github_username}/repos', headers=headers)
                repos = response.json()
                self.repo_combo.clear()
                for repo in repos:
                    self.repo_combo.addItem(repo['name'], repo['clone_url'])
            except Exception as e:
                QMessageBox.warning(self, "Error", f"Failed to fetch GitHub repositories: {str(e)}")
        else:
            QMessageBox.warning(self, "Missing Information", "Please set your GitHub token and username in the settings.")

    def new_chat(self):
        try:
            if self.api:
                self.api.create_session()
            chat_id = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
            self.current_chat_id = chat_id
            self.chat_archives[chat_id] = []
            self.chat_list.addItem(chat_id)
            self.chat_list.setCurrentRow(self.chat_list.count() - 1)
            self.chat_history.clear()
            self.add_system_message("New session created successfully.")
            self.update_file_tree()
            logger.info(f"New chat session created: {chat_id}")
        except Exception as e:
            error_msg = f"Error creating session: {str(e)}"
            self.add_system_message(error_msg, error=True)
            logger.error(error_msg)

    def load_chat(self, item: QListWidgetItem):
        chat_id = item.text()
        self.current_chat_id = chat_id
        self.chat_history.clear()
        self.load_chat_archive()
        self.update_file_list()
        self.add_system_message(f"Loaded chat session: {chat_id}")

    def load_chat_archive(self):
        chat_dir = self.get_chat_directory()
        archive_file = os.path.join(chat_dir, "chat_archive.json")
        if os.path.exists(archive_file):
            with open(archive_file, 'r', encoding='utf-8') as f:
                try:
                    self.chat_archives[self.current_chat_id] = json.load(f)
                except json.JSONDecodeError:
                    self.chat_archives[self.current_chat_id] = []
        else:
            self.chat_archives[self.current_chat_id] = []

        for message in self.chat_archives[self.current_chat_id]:
            if message['type'] == 'user':
                self.add_user_message(message['content'], save=False)
            elif message['type'] == 'ai':
                self.add_ai_message(message['content'], save=False)
            elif message['type'] == 'system':
                self.add_system_message(message['content'], message.get('error', False), save=False)

    def send_message(self):
        prompt = self.prompt_input.text().strip()
        agent = self.agent_combo.currentText()
        
        if not prompt:
            QMessageBox.warning(self, "Input Error", "Please enter a message to send.")
            return

        self.add_user_message(prompt)
        self.prompt_input.clear()

        self.progress_bar.setVisible(True)
        self.progress_bar.setValue(0)

        context = self.get_file_context()

        self.worker = ChatWorker(self.api, prompt, agent, context)
        self.worker.finished.connect(self.handle_response)
        self.worker.error.connect(self.handle_error)
        self.worker.progress.connect(self.update_progress)
        self.worker.partial_response.connect(self.handle_partial_response)
        self.worker.start()

    def handle_partial_response(self, chunk):
        self.add_ai_message(chunk, save=False)

    def get_file_context(self):
        context = {}
        chat_dir = self.get_chat_directory()
        for i in range(self.file_list.count()):
            file_name = self.file_list.item(i).text()
            file_path = os.path.join(chat_dir, file_name)
            try:
                with open(file_path, 'r', encoding='utf-8') as f:
                    # Lesen Sie nur die ersten 1000 Zeilen, um den Speicherverbrauch zu begrenzen
                    context[file_name] = ''.join(f.readlines()[:1000])
                if len(context[file_name]) == 1000:
                    context[file_name] += "\n... (file truncated)"
            except Exception as e:
                context[file_name] = f"Error reading file {file_name}: {str(e)}"
        return context

    def handle_api_error(self, error_message):
        QMessageBox.critical(self, "API Error", f"An error occurred while communicating with the API: {error_message}")
        self.add_system_message(f"API Error: {error_message}", error=True)

    def handle_network_error(self, error_message):
        QMessageBox.warning(self, "Network Error", f"A network error occurred: {error_message}")
        self.add_system_message(f"Network Error: {error_message}", error=True)

    def update_progress(self, value):
        self.progress_bar.setValue(value)

    def handle_response(self, response):
        self.progress_bar.setVisible(False)
        # Die vollständige Antwort wurde bereits in Teilen angezeigt,
        # hier speichern wir sie nur noch in den Chat-Archiven
        self.chat_archives[self.current_chat_id].append({'type': 'ai', 'content': response})
        self.save_chat_archive()

    def handle_error(self, error_message):
        self.progress_bar.setVisible(False)
        if error_message.startswith("An error occurred while making the request"):
            self.handle_network_error(error_message)
        elif error_message.startswith("The request timed out"):
            self.handle_api_error(error_message)
        else:
            self.add_system_message(f"Error: {error_message}", error=True)

    def add_user_message(self, message, save=True):
        formatted_message = self.format_message(message)
        self.chat_history.append(f'''
            <div style="text-align: right; margin: 10px 0;">
                <div style="display: inline-block; max-width: 70%; background-color: #3a6351; color: #e0e0e0; padding: 10px; border-radius: 15px 0 15px 15px; position: relative; font-size: 14px;">
                    <div style="font-weight: bold; margin-bottom: 5px; font-size: 12px; color: #a0a0a0;">You</div>
                    {formatted_message}
                    <div style="position: absolute; right: -10px; top: 0; width: 0; height: 0; border-left: 10px solid #3a6351; border-top: 10px solid transparent;"></div>
                </div>
            </div>
        ''')
        if save:
            self.chat_archives[self.current_chat_id].append({'type': 'user', 'content': message})

    def add_ai_message(self, message, save=True):
        formatted_message = self.format_message(message)
        self.chat_history.append(f'''
            <div style="text-align: left; margin: 10px 0;">
                <div style="display: inline-block; max-width: 70%; background-color: #4a4a4a; color: #e0e0e0; padding: 10px; border-radius: 0 15px 15px 15px; position: relative; font-size: 14px;">
                    <div style="font-weight: bold; margin-bottom: 5px; font-size: 12px; color: #a0a0a0;">AI</div>
                    {formatted_message}
                    <div style="position: absolute; left: -10px; top: 0; width: 0; height: 0; border-right: 10px solid #4a4a4a; border-top: 10px solid transparent;"></div>
                </div>
            </div>
        ''')
        if save:
            self.chat_archives[self.current_chat_id].append({'type': 'ai', 'content': message})

    def format_message(self, message):
        # Format code blocks
        message = re.sub(r'```([\s\S]*?)```', r'<pre style="background-color: #2b2b2b; padding: 10px; border-radius: 5px; font-family: monospace; font-size: 12px;"><code>\1</code></pre>', message)
        
        # Format inline code
        message = re.sub(r'`([^`\n]+)`', r'<code style="background-color: #2b2b2b; padding: 2px 4px; border-radius: 3px; font-family: monospace; font-size: 12px;">\1</code>', message)
        
        # Format links
        url_pattern = r'(https?://\S+)'
        message = re.sub(url_pattern, r'<a href="\1" style="color: #4a9eff; text-decoration: none;">\1</a>', message)
        
        # Convert newlines to <br> tags
        message = message.replace('\n', '<br>')
        
        return message

    def add_system_message(self, message, error=False, save=True):
        color = "#ff6b6b" if error else "#4ecdc4"
        self.chat_history.append(f'''
            <div style="text-align: center; margin: 10px 0;">
                <span style="background-color: {color}; color: #2b2b2b; padding: 5px 10px; border-radius: 15px; font-size: 12px; font-weight: bold;">
                    System: {message}
                </span>
            </div>
        ''')
        if save:
            self.chat_archives[self.current_chat_id].append({'type': 'system', 'content': message, 'error': error})

    def open_file(self, item: QListWidgetItem):
        file_name = item.text()
        file_path = os.path.join(self.get_chat_directory(), file_name)
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                content = f.read()
            self.file_content.setText(content)
            self.tabs.setCurrentIndex(1)  # Switch to file tab
            self.add_system_message(f"File opened: {file_name}")
        except Exception as e:
            self.add_system_message(f"Error opening file {file_name}: {str(e)}", error=True)

    def save_file(self):
        file_name = None
        # Check if currently editing an existing file
        current_items = self.file_list.selectedItems()
        if current_items:
            file_name = current_items[0].text()
            file_path = os.path.join(self.get_chat_directory(), file_name)
        else:
            # Prompt to save as new file
            file_name, _ = QFileDialog.getSaveFileName(self, "Save File", self.get_chat_directory(), "All Files (*)")
            if not file_name:
                return
            file_path = file_name
            # Extract just the file name
            file_name = os.path.basename(file_path)

        try:
            with open(file_path, 'w', encoding='utf-8') as f:
                f.write(self.file_content.toPlainText())
            self.update_file_list()
            self.add_system_message(f"File saved: {file_name}")
        except Exception as e:
            self.add_system_message(f"Error saving file {file_name}: {str(e)}", error=True)

    def update_file_list(self):
        self.file_list.clear()
        chat_dir = self.get_chat_directory()
        if os.path.exists(chat_dir):
            try:
                files = os.listdir(chat_dir)
                for file in files:
                    if os.path.isfile(os.path.join(chat_dir, file)) and not file.endswith(".json"):
                        self.file_list.addItem(file)
            except Exception as e:
                self.add_system_message(f"Error listing files: {str(e)}", error=True)

    def get_chat_directory(self):
        base_dir = os.path.join(os.path.expanduser("~"), "RobocoderChats")
        chat_dir = os.path.join(base_dir, self.current_chat_id)
        os.makedirs(chat_dir, exist_ok=True)
        return chat_dir

    def auto_save(self):
        self.save_chat_archive()
        self.status_bar.setText(f"Auto-saved at {datetime.datetime.now().strftime('%H:%M:%S')}")
        QTimer.singleShot(3000, lambda: self.status_bar.clear())

    def save_chat_archive(self):
        chat_dir = self.get_chat_directory()
        archive_file = os.path.join(chat_dir, "chat_archive.json")
        try:
            with open(archive_file, 'w', encoding='utf-8') as f:
                json.dump(self.chat_archives[self.current_chat_id], f, ensure_ascii=False, indent=4)
            logger.info("Chat archive saved successfully")
        except Exception as e:
            error_msg = f"Error auto-saving chat: {str(e)}"
            self.add_system_message(error_msg, error=True)
            logger.error(error_msg)

    def export_chat(self):
        if not self.current_chat_id:
            logger.warning("No active chat to export.")
            QMessageBox.warning(self, "Error", "No active chat to export.")
            return

        file_name, _ = QFileDialog.getSaveFileName(self, "Export Chat", "", "JSON Files (*.json);;Text Files (*.txt)")
        if file_name:
            try:
                if file_name.endswith('.json'):
                    with open(file_name, 'w', encoding='utf-8') as f:
                        json.dump(self.chat_archives[self.current_chat_id], f, ensure_ascii=False, indent=4)
                elif file_name.endswith('.txt'):
                    with open(file_name, 'w', encoding='utf-8') as f:
                        for message in self.chat_archives[self.current_chat_id]:
                            f.write(f"{message['type'].upper()}: {message['content']}\n\n")
                logger.info(f"Chat exported successfully to {file_name}")
                QMessageBox.information(self, "Success", f"Chat exported successfully to {file_name}")
            except Exception as e:
                error_msg = f"Failed to export chat: {str(e)}"
                logger.error(error_msg)
                QMessageBox.warning(self, "Error", error_msg)

    def closeEvent(self, event):
        self.save_chat_archive()
        logger.info("Application closed")
        event.accept()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    
    # Optional: Set application style for better visuals
    app.setStyle("Fusion")
    
    # Optional: Improve the palette
    palette = QPalette()
    palette.setColor(QPalette.Window, QColor(240, 240, 240))
    palette.setColor(QPalette.WindowText, Qt.black)
    palette.setColor(QPalette.Base, QColor(255, 255, 255))
    palette.setColor(QPalette.AlternateBase, QColor(240, 240, 240))
    palette.setColor(QPalette.ToolTipBase, Qt.black)
    palette.setColor(QPalette.ToolTipText, Qt.white)
    palette.setColor(QPalette.Text, Qt.black)
    palette.setColor(QPalette.Button, QColor(200, 200, 200))
    palette.setColor(QPalette.ButtonText, Qt.black)
    palette.setColor(QPalette.BrightText, Qt.red)

    app.setPalette(palette)
    
    ex = RobocoderApp()
    ex.show()
    sys.exit(app.exec_())