"""Verification results dialog shown after install/configure workflows.""" import logging from PySide6.QtCore import Qt from PySide6.QtGui import QClipboard, QGuiApplication from PySide6.QtWidgets import ( QDialog, QHBoxLayout, QLabel, QPushButton, QTextEdit, QVBoxLayout, ) logger = logging.getLogger(__name__) _COLOR_FAIL = "#e05050" _COLOR_WARN = "#f0c040" _COLOR_OK = "#3fd0ea" _COLOR_DIM = "#888888" def _html_row(prefix: str, color: str, msg: str) -> str: safe = msg.replace("&", "&").replace("<", "<").replace(">", ">") return ( f'' f'{prefix} {safe}' f'
' ) class VerificationResultsDialog(QDialog): """Shows the output of verify_install.py after a workflow completes.""" def __init__(self, results, parent=None): super().__init__(parent) self.setWindowTitle("Installation Verification") self.setWindowModality(Qt.NonModal) self.setAttribute(Qt.WA_DeleteOnClose, True) self.setMinimumWidth(600) self._results = results n_pass = len(results.passes) n_warn = len(results.warnings) n_fail = len(results.failures) layout = QVBoxLayout(self) layout.setContentsMargins(20, 16, 20, 16) layout.setSpacing(10) # --- Headline --- if n_fail: headline = f"[FAIL] {n_fail} failure{'s' if n_fail != 1 else ''}, {n_warn} warning{'s' if n_warn != 1 else ''}, {n_pass} passed" h_color = _COLOR_FAIL elif n_warn: headline = f"[WARN] {n_warn} warning{'s' if n_warn != 1 else ''}, {n_pass} passed" h_color = _COLOR_WARN else: headline = f"[OK] All {n_pass} checks passed" h_color = _COLOR_OK headline_label = QLabel(headline) headline_label.setStyleSheet( f"font-size: 14px; font-weight: bold; color: {h_color};" ) layout.addWidget(headline_label) # --- Issues view (always visible) --- self._issues_view = QTextEdit() self._issues_view.setReadOnly(True) self._issues_view.setStyleSheet( "QTextEdit { background: #1e1e1e; border: 1px solid #444; " "border-radius: 4px; font-size: 12px; padding: 6px; }" ) self._issues_view.setHtml(self._build_issues_html(results)) self._issues_view.setMinimumHeight(80) self._issues_view.setMaximumHeight(220) layout.addWidget(self._issues_view) # --- Full report view (hidden by default) --- self._full_view = QTextEdit() self._full_view.setReadOnly(True) self._full_view.setStyleSheet( "QTextEdit { background: #1a1a1a; border: 1px solid #333; " "border-radius: 4px; font-size: 11px; padding: 6px; }" ) self._full_view.setHtml(self._build_full_html(results)) self._full_view.setMinimumHeight(160) self._full_view.setMaximumHeight(300) self._full_view.setVisible(False) layout.addWidget(self._full_view) # --- Button row --- btn_row = QHBoxLayout() self._toggle_btn = QPushButton("Show all checks") self._toggle_btn.setFixedWidth(130) self._toggle_btn.setStyleSheet( "QPushButton { font-size: 11px; color: #ccc; background: #3a3a3a; " "border: 1px solid #555; border-radius: 4px; padding: 4px 8px; }" "QPushButton:hover { background: #4a4a4a; color: #fff; }" ) self._toggle_btn.setCursor(Qt.PointingHandCursor) self._toggle_btn.clicked.connect(self._toggle_full_report) btn_row.addWidget(self._toggle_btn) btn_row.addStretch() copy_btn = QPushButton("Copy Report") copy_btn.setFixedWidth(110) copy_btn.setToolTip("Copy plain-text report to clipboard") copy_btn.clicked.connect(self._copy_report) btn_row.addWidget(copy_btn) close_btn = QPushButton("Close") close_btn.setFixedWidth(80) close_btn.clicked.connect(self.accept) btn_row.addWidget(close_btn) layout.addLayout(btn_row) self.adjustSize() # ------------------------------------------------------------------ def _build_issues_html(self, results) -> str: lines = [] for msg in results.failures: lines.append(_html_row("[FAIL]", _COLOR_FAIL, msg)) for msg in results.warnings: lines.append(_html_row("[WARN]", _COLOR_WARN, msg)) if not results.failures and not results.warnings: lines.append(_html_row("[OK]", _COLOR_OK, "No issues found. Your modlist is correctly configured.")) return "".join(lines) def _build_full_html(self, results) -> str: lines = [] if results.failures: lines.append(f'FAILURES
') for msg in results.failures: lines.append(_html_row("[FAIL]", _COLOR_FAIL, msg)) lines.append("
") if results.warnings: lines.append(f'WARNINGS
') for msg in results.warnings: lines.append(_html_row("[WARN]", _COLOR_WARN, msg)) lines.append("
") if results.passes: lines.append(f'PASSED
') for msg in results.passes: lines.append(_html_row("[OK] ", _COLOR_OK, msg)) components = getattr(results, "installed_components", []) if components: lines.append("
") lines.append(f'INSTALLED COMPONENTS ({len(components)})
') for c in components: method = c.get("method", "unknown") method_color = _COLOR_OK if method == "native" else _COLOR_DIM safe_name = c["name"].replace("&", "&").replace("<", "<") lines.append( f'' f'{safe_name}' f' ({method})' f'
' ) return "".join(lines) def _build_plain_text(self) -> str: r = self._results n_pass = len(r.passes) n_warn = len(r.warnings) n_fail = len(r.failures) lines = ["Jackify - Installation Verification", "=" * 40] if n_fail: lines.append(f"RESULT: FAILED ({n_fail} failures, {n_warn} warnings, {n_pass} passed)") elif n_warn: lines.append(f"RESULT: WARNING ({n_warn} warnings, {n_pass} passed)") else: lines.append(f"RESULT: OK (all {n_pass} checks passed)") lines.append("") if r.failures: lines.append("FAILURES:") for msg in r.failures: lines.append(f" [FAIL] {msg}") lines.append("") if r.warnings: lines.append("WARNINGS:") for msg in r.warnings: lines.append(f" [WARN] {msg}") lines.append("") if r.passes: lines.append("PASSED:") for msg in r.passes: lines.append(f" [OK] {msg}") components = getattr(r, "installed_components", []) if components: lines.append("") lines.append("INSTALLED COMPONENTS:") for c in components: lines.append(f" {c['name']} ({c.get('method', 'unknown')})") return "\n".join(lines) def _toggle_full_report(self): visible = self._full_view.isVisible() self._full_view.setVisible(not visible) self._toggle_btn.setText("Hide all checks" if not visible else "Show all checks") self.adjustSize() def _copy_report(self): QGuiApplication.clipboard().setText(self._build_plain_text())