mirror of
https://github.com/Omni-guides/Jackify.git
synced 2026-08-14 03:13:44 +02:00
Release v0.7.1 - Remote Manifest System, Stability Fixes
This commit is contained in:
@@ -339,8 +339,9 @@ class ManualDownloadDialog(QDialog):
|
||||
colour = _STATUS_COLOURS.get(item.status, '#808080')
|
||||
status_cell = QTableWidgetItem(_STATUS_LABELS.get(item.status, item.status))
|
||||
status_cell.setForeground(QColor(colour))
|
||||
if item.error_message:
|
||||
status_cell.setToolTip(item.error_message)
|
||||
tooltip_parts = [p for p in (item.download_reason, item.error_message) if p]
|
||||
if tooltip_parts:
|
||||
status_cell.setToolTip("\n".join(tooltip_parts))
|
||||
self._table.setItem(row, _COL_STATUS, status_cell)
|
||||
|
||||
def _update_row(self, row: int, item: DownloadItem) -> None:
|
||||
@@ -349,7 +350,8 @@ class ManualDownloadDialog(QDialog):
|
||||
if status_cell:
|
||||
status_cell.setText(_STATUS_LABELS.get(item.status, item.status))
|
||||
status_cell.setForeground(QColor(_STATUS_COLOURS.get(item.status, '#808080')))
|
||||
status_cell.setToolTip(item.error_message or "")
|
||||
tooltip_parts = [p for p in (item.download_reason, item.error_message) if p]
|
||||
status_cell.setToolTip("\n".join(tooltip_parts))
|
||||
|
||||
def _rebuild_row_map(self) -> None:
|
||||
self._row_map.clear()
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
"""Guided dialog for installing a Nexus-only tool via manual browser download."""
|
||||
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from PySide6.QtWidgets import (
|
||||
QDialog, QVBoxLayout, QHBoxLayout, QLabel, QPushButton,
|
||||
QLineEdit, QFileDialog, QFrame,
|
||||
)
|
||||
|
||||
from jackify.frontends.gui.services.message_service import open_url
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NexusManualInstallDialog(QDialog):
|
||||
"""
|
||||
Guides the user through manually downloading a Nexus-only tool and handing
|
||||
the archive to Jackify for extraction and installation.
|
||||
"""
|
||||
|
||||
def __init__(self, tool_id: str, display_name: str, nexus_url: str, parent=None):
|
||||
super().__init__(parent)
|
||||
self._tool_id = tool_id
|
||||
self._nexus_url = nexus_url
|
||||
self._archive_path: Optional[Path] = None
|
||||
|
||||
self.setWindowTitle(f"Install {display_name}")
|
||||
self.setModal(True)
|
||||
self.setMinimumWidth(480)
|
||||
self.setStyleSheet("QDialog { background: #181818; color: #fff; }")
|
||||
self._build_ui(display_name)
|
||||
self.adjustSize()
|
||||
|
||||
def _build_ui(self, display_name: str) -> None:
|
||||
main_layout = QVBoxLayout(self)
|
||||
main_layout.setSpacing(0)
|
||||
main_layout.setContentsMargins(20, 20, 20, 20)
|
||||
|
||||
card = QFrame(self)
|
||||
card.setObjectName("dialogCard")
|
||||
card.setFrameShape(QFrame.StyledPanel)
|
||||
card.setFrameShadow(QFrame.Raised)
|
||||
card.setStyleSheet(
|
||||
"QFrame#dialogCard { "
|
||||
" background: #2d2d2d; "
|
||||
" border-radius: 12px; "
|
||||
" border: 1px solid #555; "
|
||||
"}"
|
||||
)
|
||||
card_layout = QVBoxLayout(card)
|
||||
card_layout.setSpacing(16)
|
||||
card_layout.setContentsMargins(28, 28, 28, 28)
|
||||
|
||||
title_label = QLabel(f"Manual download required: {display_name}")
|
||||
title_label.setStyleSheet("color: #3fd0ea; font-size: 14px; font-weight: 600;")
|
||||
title_label.setWordWrap(True)
|
||||
card_layout.addWidget(title_label)
|
||||
|
||||
body_label = QLabel(
|
||||
f"{display_name} is only available on Nexus Mods. As you do not have Nexus "
|
||||
"Premium, please perform the following steps manually:\n\n"
|
||||
f"1. Click 'Open Nexus Page' below and click Manual Download on the Nexus page "
|
||||
f"to download {display_name}\n"
|
||||
"2. Once the download is complete, click 'Browse...' below and select the "
|
||||
"downloaded archive\n"
|
||||
"3. Click Install to complete the installation."
|
||||
)
|
||||
body_label.setWordWrap(True)
|
||||
card_layout.addWidget(body_label)
|
||||
|
||||
nexus_btn = QPushButton("Open Nexus Page")
|
||||
nexus_btn.clicked.connect(self._open_nexus)
|
||||
card_layout.addWidget(nexus_btn)
|
||||
|
||||
file_row = QHBoxLayout()
|
||||
self._file_edit = QLineEdit()
|
||||
self._file_edit.setPlaceholderText("No file selected...")
|
||||
self._file_edit.setReadOnly(True)
|
||||
self._file_edit.setStyleSheet(
|
||||
"QLineEdit { "
|
||||
" background: #1a1a1a; "
|
||||
" color: #fff; "
|
||||
" border: 1px solid #555; "
|
||||
" border-radius: 4px; "
|
||||
" padding: 8px; "
|
||||
"}"
|
||||
)
|
||||
file_row.addWidget(self._file_edit)
|
||||
|
||||
browse_btn = QPushButton("Browse...")
|
||||
browse_btn.setMinimumWidth(90)
|
||||
browse_btn.clicked.connect(self._browse)
|
||||
file_row.addWidget(browse_btn)
|
||||
card_layout.addLayout(file_row)
|
||||
|
||||
btn_row = QHBoxLayout()
|
||||
btn_row.addStretch()
|
||||
|
||||
cancel_btn = QPushButton("Cancel")
|
||||
cancel_btn.setMinimumWidth(100)
|
||||
cancel_btn.clicked.connect(self.reject)
|
||||
btn_row.addWidget(cancel_btn)
|
||||
|
||||
self._install_btn = QPushButton("Install")
|
||||
self._install_btn.setDefault(True)
|
||||
self._install_btn.setMinimumWidth(100)
|
||||
self._install_btn.setEnabled(False)
|
||||
self._install_btn.clicked.connect(self.accept)
|
||||
btn_row.addWidget(self._install_btn)
|
||||
|
||||
card_layout.addLayout(btn_row)
|
||||
main_layout.addWidget(card)
|
||||
|
||||
def _open_nexus(self) -> None:
|
||||
if self._nexus_url:
|
||||
open_url(self._nexus_url)
|
||||
|
||||
def _browse(self) -> None:
|
||||
path, _ = QFileDialog.getOpenFileName(
|
||||
self,
|
||||
"Select downloaded archive",
|
||||
str(Path.home() / "Downloads"),
|
||||
"Archives (*.zip *.tar.gz *.tar.xz *.7z);;All files (*)",
|
||||
)
|
||||
if path:
|
||||
self._archive_path = Path(path)
|
||||
self._file_edit.setText(path)
|
||||
self._install_btn.setEnabled(True)
|
||||
|
||||
@property
|
||||
def selected_archive(self) -> Optional[Path]:
|
||||
return self._archive_path
|
||||
|
||||
@property
|
||||
def tool_id(self) -> str:
|
||||
return self._tool_id
|
||||
@@ -20,6 +20,7 @@ from PySide6.QtWidgets import (
|
||||
|
||||
from jackify.backend.services.nxm_url import NxmUrl
|
||||
from jackify.frontends.gui.shared_theme import JACKIFY_COLOR_BLUE
|
||||
from jackify.frontends.gui.mixins.thread_lifecycle_mixin import ThreadLifecycleMixin
|
||||
import jackify.backend.services.nxm_session as nxm_session
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -117,7 +118,7 @@ class _DownloadThread(QThread):
|
||||
self.progress.emit(downloaded, total)
|
||||
|
||||
|
||||
class NxmDownloadDialog(QDialog):
|
||||
class NxmDownloadDialog(ThreadLifecycleMixin, QDialog):
|
||||
"""Modlist picker and download runner for incoming nxm:// links.
|
||||
|
||||
When auto_start_modlist is provided the picker is hidden and the download
|
||||
|
||||
@@ -72,9 +72,7 @@ class SettingsDialog(SettingsDialogTabsMixin, SettingsDialogProtonMixin, QDialog
|
||||
main_layout.addLayout(btn_layout)
|
||||
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Exception in SettingsDialog.__init__: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
logger.error(f"Exception in SettingsDialog.__init__: {e}", exc_info=True)
|
||||
|
||||
def _toggle_api_key_visibility(self, checked):
|
||||
eye_icon = QIcon.fromTheme("view-visible")
|
||||
@@ -402,7 +400,7 @@ class SettingsDialog(SettingsDialogTabsMixin, SettingsDialogProtonMixin, QDialog
|
||||
screen.refresh_paths()
|
||||
|
||||
except Exception as e:
|
||||
print(f"Warning: Could not refresh GUI paths: {e}")
|
||||
logger.warning(f"Could not refresh GUI paths: {e}")
|
||||
|
||||
def _bold_label(self, text):
|
||||
label = QLabel(text)
|
||||
|
||||
@@ -16,6 +16,8 @@ from PySide6.QtWidgets import (
|
||||
from PySide6.QtCore import Qt, QTimer
|
||||
from PySide6.QtGui import QPixmap, QIcon, QFont
|
||||
|
||||
from jackify.frontends.gui.services.message_service import open_url
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -38,6 +40,8 @@ class SuccessDialog(QDialog):
|
||||
time_taken: str,
|
||||
game_name: str = None,
|
||||
verification_results=None,
|
||||
disabled_problem_mods=None,
|
||||
readme_url: str = None,
|
||||
parent=None,
|
||||
):
|
||||
super().__init__(parent)
|
||||
@@ -46,6 +50,8 @@ class SuccessDialog(QDialog):
|
||||
self.time_taken = time_taken
|
||||
self.game_name = game_name
|
||||
self.verification_results = verification_results
|
||||
self.disabled_problem_mods = disabled_problem_mods or []
|
||||
self.readme_url = readme_url
|
||||
self.setWindowTitle("Complete" if (verification_results and verification_results.failures) else "Success!")
|
||||
self.setWindowModality(Qt.NonModal)
|
||||
self.setAttribute(Qt.WA_ShowWithoutActivating, True)
|
||||
@@ -180,6 +186,25 @@ class SuccessDialog(QDialog):
|
||||
if self.verification_results is not None:
|
||||
self._add_verification_section(card_layout)
|
||||
|
||||
# Problem mods that were auto-disabled
|
||||
if self.disabled_problem_mods:
|
||||
self._add_problem_mods_section(card_layout)
|
||||
|
||||
# Readme link (install workflow only)
|
||||
if self.readme_url:
|
||||
readme_label = QLabel(
|
||||
f'<a href="{self.readme_url}" style="color:#3fd0ea; text-decoration:none;">'
|
||||
"Open modlist readme"
|
||||
"</a>"
|
||||
)
|
||||
readme_label.setAlignment(Qt.AlignCenter)
|
||||
readme_label.setStyleSheet(
|
||||
"QLabel { color: #3fd0ea; font-size: 11px; margin-top: 4px; padding: 4px; background-color: transparent; }"
|
||||
)
|
||||
readme_label.setTextInteractionFlags(Qt.TextBrowserInteraction)
|
||||
readme_label.linkActivated.connect(open_url)
|
||||
card_layout.addWidget(readme_label)
|
||||
|
||||
# Subtle Ko-Fi support link
|
||||
kofi_label = QLabel('<a href="https://ko-fi.com/omni1" style="color:#3fd0ea; text-decoration:none;">Enjoying Jackify? Support development ♥</a>')
|
||||
kofi_label.setAlignment(Qt.AlignCenter)
|
||||
@@ -193,7 +218,7 @@ class SuccessDialog(QDialog):
|
||||
"}"
|
||||
)
|
||||
kofi_label.setTextInteractionFlags(Qt.TextBrowserInteraction)
|
||||
kofi_label.setOpenExternalLinks(True)
|
||||
kofi_label.linkActivated.connect(open_url)
|
||||
card_layout.addWidget(kofi_label)
|
||||
|
||||
layout.addStretch()
|
||||
@@ -390,6 +415,29 @@ class SuccessDialog(QDialog):
|
||||
except Exception as exc:
|
||||
logger.error("Could not open verification dialog: %s", exc)
|
||||
|
||||
def _add_problem_mods_section(self, card_layout):
|
||||
"""Add an auto-disabled problem mods section to the card layout."""
|
||||
from PySide6.QtWidgets import QFrame
|
||||
|
||||
sep = QFrame()
|
||||
sep.setFrameShape(QFrame.HLine)
|
||||
sep.setStyleSheet("color: #444;")
|
||||
card_layout.addWidget(sep)
|
||||
|
||||
header = QLabel("Compatibility Notice")
|
||||
header.setStyleSheet("font-size: 12px; font-weight: bold; color: #c8a050; margin-top: 4px;")
|
||||
card_layout.addWidget(header)
|
||||
|
||||
msg_text = (
|
||||
"Due to known compatibility issues with Proton, the following mods were "
|
||||
"automatically disabled:\n\n"
|
||||
+ "\n".join(f" - {name}" for name in self.disabled_problem_mods)
|
||||
)
|
||||
msg_label = QLabel(msg_text)
|
||||
msg_label.setWordWrap(True)
|
||||
msg_label.setStyleSheet("font-size: 11px; color: #bbb; margin-bottom: 4px;")
|
||||
card_layout.addWidget(msg_label)
|
||||
|
||||
def _update_countdown(self):
|
||||
if self._countdown > 0:
|
||||
self.return_btn.setText(f"{self._orig_return_text} ({self._countdown}s)")
|
||||
|
||||
Reference in New Issue
Block a user