from core.errors.ErrorStack import ErrorStack import sys from PyQt6.QtWidgets import ( QApplication, QDialog, QVBoxLayout, QHBoxLayout, QLabel, QFrame, QScrollArea, QPushButton, QPlainTextEdit ) from PyQt6.QtCore import Qt, QTimer from PyQt6.QtGui import QFont class ErrorStackPrompt(QDialog): IGNORE = 1 EXIT = 2 def __init__(self, error_data: ErrorStack, parent=None): super().__init__(parent) self.error_data = error_data self.setWindowTitle("System Error") self.setModal(True) self.user_choice = self.EXIT self.setMinimumWidth(600) self.setMinimumHeight(500) self.setMaximumWidth(800) self.setMaximumHeight(800) self.setStyleSheet(""" ErrorStackPrompt { background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #0f172a, stop:1 #1e293b); } """) main_layout = QVBoxLayout() main_layout.setContentsMargins(0, 0, 0, 0) main_layout.setSpacing(0) # --- Header --- header = QFrame() header.setStyleSheet(""" QFrame { background: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #3b82f6, stop:1 #1e40af); border: none; } """) header.setFixedHeight(100) header_layout = QHBoxLayout() header_layout.setContentsMargins(30, 20, 30, 20) icon_label = QLabel("⚠️") icon_label.setFont(QFont("Segoe UI", 48)) header_layout.addWidget(icon_label, 0, Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter) title_layout = QVBoxLayout() title_layout.setSpacing(5) title = QLabel("App Error") title.setFont(QFont("Segoe UI", 18, QFont.Weight.Bold)) title.setStyleSheet("color: white;") title_layout.addWidget(title) subtitle = QLabel(f"Error in {self.error_data.func_name or 'Unknown'}") subtitle.setFont(QFont("Segoe UI", 11)) subtitle.setStyleSheet("color: rgba(255, 255, 255, 0.8);") title_layout.addWidget(subtitle) header_layout.addLayout(title_layout, 1) header.setLayout(header_layout) main_layout.addWidget(header) # --- Content Area --- content = QFrame() content.setStyleSheet("QFrame { background: #1e293b; border: none; }") content_layout = QVBoxLayout() scroll_area = QScrollArea() scroll_area.setWidgetResizable(True) scroll_area.setStyleSheet(""" QScrollArea { background: #1e293b; border: none; } QScrollBar:vertical { background: #1e293b; width: 12px; } QScrollBar::handle:vertical { background: #475569; border-radius: 6px; } """) scroll_widget = QFrame() scroll_widget.setStyleSheet("background: #1e293b; border: none;") scroll_layout = QVBoxLayout() scroll_layout.setContentsMargins(40, 30, 40, 30) scroll_layout.setSpacing(20) # User Message Section msg_container = QVBoxLayout() msg_label = QLabel("Message for User:") msg_label.setFont(QFont("Segoe UI", 10, QFont.Weight.Bold)) msg_label.setStyleSheet("color: #94a3b8;") msg_container.addWidget(msg_label) user_msg = QLabel(str(self.error_data.message_for_user)) user_msg.setFont(QFont("Segoe UI", 12)) user_msg.setStyleSheet("color: #e2e8f0;") user_msg.setWordWrap(True) msg_container.addWidget(user_msg) btn_copy_msg = self._create_small_button("Copy Message", "📋") btn_copy_msg.clicked.connect(lambda: self._handle_copy(btn_copy_msg, str(self.error_data.message_for_user))) msg_container.addWidget(btn_copy_msg, 0, Qt.AlignmentFlag.AlignRight) scroll_layout.addLayout(msg_container) # Stacktrace Section (Small scroll box) trace_container = QVBoxLayout() trace_label = QLabel("Technical Details (Stacktrace):") trace_label.setFont(QFont("Segoe UI", 10, QFont.Weight.Bold)) trace_label.setStyleSheet("color: #94a3b8;") trace_container.addWidget(trace_label) self.trace_box = QPlainTextEdit() self.trace_box.setPlainText(str(self.error_data.full_stacktrace)) self.trace_box.setReadOnly(True) self.trace_box.setMaximumHeight(200) # contained in box self.trace_box.setStyleSheet(""" QPlainTextEdit { background: #0f172a; color: #cbd5e1; border: 1px solid #334155; border-radius: 4px; font-family: 'Consolas', 'Monospace'; font-size: 10pt; } """) trace_container.addWidget(self.trace_box) btn_copy_trace = self._create_small_button("Copy Stacktrace", "📋") btn_copy_trace.clicked.connect(lambda: self._handle_copy(btn_copy_trace, str(self.error_data.full_stacktrace))) trace_container.addWidget(btn_copy_trace, 0, Qt.AlignmentFlag.AlignRight) scroll_layout.addLayout(trace_container) scroll_layout.addStretch() scroll_widget.setLayout(scroll_layout) scroll_area.setWidget(scroll_widget) content_layout.addWidget(scroll_area, 1) content.setLayout(content_layout) main_layout.addWidget(content, 1) # --- Button Area --- button_area = QFrame() button_area.setStyleSheet("QFrame { background: #0f172a; border-top: 1px solid #334155; }") button_layout = QVBoxLayout() button_layout.setContentsMargins(40, 20, 40, 20) button_layout.setSpacing(12) btn_ignore = self._create_button("Ignore and Continue", "secondary", "⏩") btn_ignore.clicked.connect(self.on_ignore) button_layout.addWidget(btn_ignore) btn_exit = self._create_button("Exit Application", "danger", "❌") btn_exit.clicked.connect(self.on_exit) button_layout.addWidget(btn_exit) button_area.setLayout(button_layout) main_layout.addWidget(button_area) self.setLayout(main_layout) def _create_button(self, text, style_type, icon): btn = QPushButton(f" {icon} {text}") btn.setFont(QFont("Segoe UI", 11, QFont.Weight.Medium)) btn.setFixedHeight(45) btn.setCursor(Qt.CursorShape.PointingHandCursor) if style_type == "secondary": btn.setStyleSheet(""" QPushButton { background: #334155; color: #e2e8f0; border: 1px solid #475569; border-radius: 6px; font-weight: 600; } QPushButton:hover { background: #475569; } """) elif style_type == "danger": btn.setStyleSheet(""" QPushButton { background: #64748b; color: #e2e8f0; border: 1px solid #475569; border-radius: 6px; font-weight: 600; } QPushButton:hover { background: #ef4444; border: 1px solid #dc2626; } """) return btn def _create_small_button(self, text, icon): btn = QPushButton(f"{icon} {text}") btn.setFixedHeight(30) btn.setCursor(Qt.CursorShape.PointingHandCursor) btn.setStyleSheet(""" QPushButton { background: #334155; color: #cbd5e1; border-radius: 4px; font-size: 10pt; border: 1px solid #475569; padding: 0 10px; /* Adds a little breathing room on the sides */ } QPushButton:hover { background: #475569; } """) return btn def _handle_copy(self, button, text): # 1. Perform the copy QApplication.clipboard().setText(text) # 2. Save the original style so we can restore it original_style = button.styleSheet() # 3. Change to a "Success" green color button.setStyleSheet(""" QPushButton { background: #22c55e; color: white; border-radius: 4px; font-size: 10pt; border: 1px solid #16a34a; padding: 0 10px; } """) # 4. Use a single-shot timer to reset the style after 500ms QTimer.singleShot(500, lambda: button.setStyleSheet(original_style)) def on_ignore(self): self.user_choice = self.IGNORE self.accept() def on_exit(self): self.user_choice = self.EXIT self.reject() def show_window(self): """Executes the dialog and returns the user's choice.""" self.exec() return self.user_choice def stand_alone_show(error_stack: ErrorStack): app = QApplication.instance() or QApplication(sys.argv) dialog = ErrorStackPrompt(error_stack) dialog.exec() return dialog.user_choice