Sync from development - prepare for v0.1.7.1

This commit is contained in:
Omni
2025-11-11 20:04:32 +00:00
parent 9680814bbb
commit fe14e4ecfb
60 changed files with 622 additions and 344 deletions
+1 -1
View File
@@ -5,4 +5,4 @@ This package provides both CLI and GUI interfaces for managing
Wabbajack modlists natively on Linux systems.
"""
__version__ = "0.1.7"
__version__ = "0.1.7.1"
+2 -1
View File
@@ -100,7 +100,8 @@ class ConfigHandler:
libraryfolders_vdf_paths = [
os.path.expanduser("~/.steam/steam/config/libraryfolders.vdf"),
os.path.expanduser("~/.local/share/Steam/config/libraryfolders.vdf"),
os.path.expanduser("~/.steam/root/config/libraryfolders.vdf")
os.path.expanduser("~/.steam/root/config/libraryfolders.vdf"),
os.path.expanduser("~/.var/app/com.valvesoftware.Steam/.local/share/Steam/config/libraryfolders.vdf") # Flatpak
]
for vdf_path in libraryfolders_vdf_paths:
@@ -784,7 +784,8 @@ class FileSystemHandler:
possible_vdf_paths = [
Path.home() / ".steam/steam/config/libraryfolders.vdf",
Path.home() / ".local/share/Steam/config/libraryfolders.vdf",
Path.home() / ".steam/root/config/libraryfolders.vdf"
Path.home() / ".steam/root/config/libraryfolders.vdf",
Path.home() / ".var/app/com.valvesoftware.Steam/.local/share/Steam/config/libraryfolders.vdf" # Flatpak
]
libraryfolders_vdf_path: Optional[Path] = None
+4
View File
@@ -650,6 +650,10 @@ class ModlistMenuHandler:
print("Modlist Install and Configuration complete!")
print(f"• You should now be able to Launch '{context.get('name')}' through Steam")
print("• Congratulations and enjoy the game!")
print("")
print("NOTE: If you experience ENB issues, consider using GE-Proton 10-14 instead of")
print(" Valve's Proton 10 (known ENB compatibility issues in Valve's Proton 10).")
print("")
print("Detailed log available at: ~/Jackify/logs/Configure_New_Modlist_workflow.log")
# Only wait for input in CLI mode, not GUI mode
if not gui_mode:
+19 -8
View File
@@ -329,22 +329,18 @@ class ModlistHandler:
# On non-Steam Deck systems, /media mounts should use Z: drive, not D: drive
is_on_sdcard_path = str(self.modlist_dir).startswith("/run/media") or str(self.modlist_dir).startswith("/media")
# DEBUG: Log SD card detection logic
self.logger.debug(f"[SD_CARD_DEBUG] SD card detection for instance id={id(self)}:")
self.logger.debug(f"[SD_CARD_DEBUG] modlist_dir: {self.modlist_dir}")
self.logger.debug(f"[SD_CARD_DEBUG] is_on_sdcard_path: {is_on_sdcard_path}")
self.logger.debug(f"[SD_CARD_DEBUG] self.steamdeck: {self.steamdeck}")
# Log SD card detection for debugging
self.logger.debug(f"SD card detection: modlist_dir={self.modlist_dir}, is_sdcard_path={is_on_sdcard_path}, steamdeck={self.steamdeck}")
if is_on_sdcard_path and self.steamdeck:
self.modlist_sdcard = True
self.logger.info("Modlist appears to be on an SD card (Steam Deck).")
self.logger.debug(f"[SD_CARD_DEBUG] Set modlist_sdcard=True")
self.logger.debug(f"Set modlist_sdcard=True")
else:
self.modlist_sdcard = False
self.logger.debug(f"[SD_CARD_DEBUG] Set modlist_sdcard=False because: is_on_sdcard_path={is_on_sdcard_path} AND steamdeck={self.steamdeck}")
self.logger.debug(f"Set modlist_sdcard=False (is_on_sdcard_path={is_on_sdcard_path}, steamdeck={self.steamdeck})")
if is_on_sdcard_path and not self.steamdeck:
self.logger.info("Modlist on /media mount detected on non-Steam Deck system - using Z: drive mapping.")
self.logger.debug("[SD_CARD_DEBUG] This is the ROOT CAUSE - SD card path but steamdeck=False!")
# Find and set compatdata path now that we have appid
# Ensure PathHandler is available (should be initialized in __init__)
@@ -722,6 +718,8 @@ class ModlistHandler:
success = self.winetricks_handler.install_wine_components(wineprefix, self.game_var_full, specific_components=components)
if success:
self.logger.info("Wine component installation completed successfully")
if status_callback:
status_callback(f"{self._get_progress_timestamp()} Wine components verified and installed successfully")
else:
self.logger.error("Wine component installation failed")
print("Error: Failed to install necessary Wine components.")
@@ -752,6 +750,19 @@ class ModlistHandler:
self.logger.error("=" * 80)
# Continue but user should be aware of potential issues
# Step 4.6: Enable dotfiles visibility for Wine prefix
if status_callback:
status_callback(f"{self._get_progress_timestamp()} Enabling dotfiles visibility")
self.logger.info("Step 4.6: Enabling dotfiles visibility in Wine prefix...")
try:
if self.protontricks_handler.enable_dotfiles(self.appid):
self.logger.info("Dotfiles visibility enabled successfully")
else:
self.logger.warning("Failed to enable dotfiles visibility (non-critical, continuing)")
except Exception as e:
self.logger.warning(f"Error enabling dotfiles visibility: {e} (non-critical, continuing)")
self.logger.info("Step 4.6: Enabling dotfiles visibility... Done")
# Step 5: Ensure permissions of Modlist directory
if status_callback:
status_callback(f"{self._get_progress_timestamp()} Setting ownership and permissions for modlist directory")
+7 -6
View File
@@ -390,7 +390,7 @@ class PathHandler:
libraryfolders_vdf_paths = [
os.path.expanduser("~/.steam/steam/config/libraryfolders.vdf"),
os.path.expanduser("~/.local/share/Steam/config/libraryfolders.vdf"),
# Add other potential standard locations if necessary
os.path.expanduser("~/.var/app/com.valvesoftware.Steam/.local/share/Steam/config/libraryfolders.vdf"), # Flatpak
]
# Simple backup mechanism (optional but good practice)
@@ -622,7 +622,9 @@ class PathHandler:
m = re.search(r'"path"\s*"([^"]+)"', line)
if m:
lib_path = Path(m.group(1))
library_paths.add(lib_path)
# Resolve symlinks for consistency (mmcblk0p1 -> deck/UUID)
resolved_path = lib_path.resolve()
library_paths.add(resolved_path)
except Exception as e:
logger.error(f"[DEBUG] Failed to parse {vdf_path}: {e}")
logger.info(f"[DEBUG] All detected Steam libraries: {library_paths}")
@@ -871,10 +873,9 @@ class PathHandler:
else:
found_stock = None
for folder in STOCK_GAME_FOLDERS:
folder_pattern = f"/{folder.replace(' ', '')}".lower()
value_part_lower = value_part.replace(' ', '').lower()
if folder_pattern in value_part_lower:
idx = value_part_lower.index(folder_pattern)
folder_pattern = f"/{folder}"
if folder_pattern in value_part:
idx = value_part.index(folder_pattern)
rel_path = value_part[idx:].lstrip('/')
found_stock = folder
break
@@ -117,23 +117,21 @@ class ProtontricksHandler:
try:
# PyInstaller fix: Comprehensive environment cleaning for subprocess calls
env = self._get_clean_subprocess_env()
result = subprocess.run(
["flatpak", "list"],
capture_output=True,
["flatpak", "list"],
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL, # Suppress stderr to avoid error messages when flatpak not installed
text=True,
check=True,
env=env # Use comprehensively cleaned environment
)
if "com.github.Matoking.protontricks" in result.stdout:
if result.returncode == 0 and "com.github.Matoking.protontricks" in result.stdout:
logger.info("Flatpak Protontricks is installed")
self.which_protontricks = 'flatpak'
flatpak_installed = True
return True
except FileNotFoundError:
logger.warning("'flatpak' command not found. Cannot check for Flatpak Protontricks.")
except subprocess.CalledProcessError as e:
logger.warning(f"Error checking flatpak list: {e}")
except Exception as e:
logger.error(f"Unexpected error checking flatpak: {e}")
@@ -707,8 +705,15 @@ class ProtontricksHandler:
result = self.run_protontricks("--no-bwrap", appid, "-q", *components_to_install, env=env, timeout=600)
self.logger.debug(f"Protontricks output: {result.stdout if result else ''}")
if result and result.returncode == 0:
self.logger.info("Wine Component installation command completed successfully.")
return True
self.logger.info("Wine Component installation command completed.")
# Verify components were actually installed
if self._verify_components_installed(appid, components_to_install):
self.logger.info("Component verification successful - all components installed correctly.")
return True
else:
self.logger.error(f"Component verification failed (Attempt {attempt}/{max_attempts})")
# Continue to retry
else:
self.logger.error(f"Protontricks command failed (Attempt {attempt}/{max_attempts}). Return Code: {result.returncode if result else 'N/A'}")
self.logger.error(f"Stdout: {result.stdout.strip() if result else ''}")
@@ -718,14 +723,73 @@ class ProtontricksHandler:
self.logger.error(f"Failed to install Wine components after {max_attempts} attempts.")
return False
def _verify_components_installed(self, appid: str, components: List[str]) -> bool:
"""
Verify that Wine components were actually installed by querying protontricks.
Args:
appid: Steam AppID
components: List of components that should be installed
Returns:
bool: True if all critical components are verified, False otherwise
"""
try:
self.logger.info("Verifying installed components...")
# Run protontricks list-installed to get actual installed components
result = self.run_protontricks("--no-bwrap", appid, "list-installed", timeout=30)
if not result or result.returncode != 0:
self.logger.error("Failed to query installed components")
self.logger.debug(f"list-installed stderr: {result.stderr if result else 'N/A'}")
return False
installed_output = result.stdout.lower()
self.logger.debug(f"Installed components output: {installed_output}")
# Define critical components that MUST be installed
# These are the core components that determine success
critical_components = ["vcrun2022", "xact"]
# Check for critical components
missing_critical = []
for component in critical_components:
if component.lower() not in installed_output:
missing_critical.append(component)
if missing_critical:
self.logger.error(f"CRITICAL: Missing essential components: {missing_critical}")
self.logger.error("Installation reported success but components are NOT installed")
return False
# Check for requested components (warn but don't fail)
missing_requested = []
for component in components:
# Handle settings like fontsmooth=rgb (just check the base component name)
base_component = component.split('=')[0].lower()
if base_component not in installed_output and component.lower() not in installed_output:
missing_requested.append(component)
if missing_requested:
self.logger.warning(f"Some requested components may not be installed: {missing_requested}")
self.logger.warning("This may cause issues, but critical components are present")
self.logger.info(f"Verification passed - critical components confirmed: {critical_components}")
return True
except Exception as e:
self.logger.error(f"Error verifying components: {e}", exc_info=True)
return False
def _cleanup_wine_processes(self):
"""
Internal method to clean up wine processes during component installation
"""
try:
subprocess.run("pgrep -f 'win7|win10|ShowDotFiles|protontricks' | xargs -r kill -9",
subprocess.run("pgrep -f 'win7|win10|ShowDotFiles|protontricks' | xargs -r kill -9",
shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
subprocess.run("pkill -9 winetricks",
subprocess.run("pkill -9 winetricks",
shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
except Exception as e:
logger.error(f"Error cleaning up wine processes: {e}")
+41 -26
View File
@@ -200,40 +200,55 @@ class WineUtils:
@staticmethod
def _get_sd_card_mounts():
"""
Dynamically detect all current SD card mount points
Returns list of mount point paths
Detect SD card mount points using df.
Returns list of actual mount paths from /run/media (e.g., /run/media/deck/MicroSD).
"""
try:
import subprocess
result = subprocess.run(['df', '-h'], capture_output=True, text=True, timeout=5)
sd_mounts = []
for line in result.stdout.split('\n'):
# Look for common SD card mount patterns
if '/run/media' in line or ('/mnt' in line and 'sdcard' in line.lower()):
parts = line.split()
if len(parts) >= 6: # df output has 6+ columns
mount_point = parts[-1] # Last column is mount point
if mount_point.startswith(('/run/media', '/mnt')):
sd_mounts.append(mount_point)
return sd_mounts
except Exception:
# Fallback to common patterns if df fails
return ['/run/media/mmcblk0p1', '/run/media/deck']
import subprocess
import re
result = subprocess.run(['df', '-h'], capture_output=True, text=True, timeout=5)
sd_mounts = []
for line in result.stdout.split('\n'):
if '/run/media' in line:
parts = line.split()
if len(parts) >= 6:
mount_point = parts[-1] # Last column is the mount point
if mount_point.startswith('/run/media/'):
sd_mounts.append(mount_point)
# Sort by length (longest first) to match most specific paths first
sd_mounts.sort(key=len, reverse=True)
logger.debug(f"Detected SD card mounts from df: {sd_mounts}")
return sd_mounts
@staticmethod
def _strip_sdcard_path(path):
"""
Strip any detected SD card mount prefix from paths
Handles both /run/media/mmcblk0p1 and /run/media/deck/UUID patterns
Strip SD card mount prefix from path.
Handles both /run/media/mmcblk0p1 and /run/media/deck/UUID patterns.
Pattern: /run/media/deck/UUID/Games/... becomes /Games/...
Pattern: /run/media/mmcblk0p1/Games/... becomes /Games/...
"""
sd_mounts = WineUtils._get_sd_card_mounts()
import re
for mount in sd_mounts:
if path.startswith(mount):
# Strip the mount prefix and ensure proper leading slash
relative_path = path[len(mount):].lstrip('/')
return "/" + relative_path if relative_path else "/"
# Pattern 1: /run/media/deck/UUID/... strip everything up to and including UUID
# This matches the bash: "${path#*/run/media/deck/*/*}"
deck_pattern = r'^/run/media/deck/[^/]+(/.*)?$'
match = re.match(deck_pattern, path)
if match:
stripped = match.group(1) if match.group(1) else "/"
logger.debug(f"Stripped SD card path (deck pattern): {path} -> {stripped}")
return stripped
# Pattern 2: /run/media/mmcblk0p1/... strip /run/media/mmcblk0p1
# This matches the bash: "${path#*mmcblk0p1}"
if path.startswith('/run/media/mmcblk0p1/'):
stripped = path.replace('/run/media/mmcblk0p1', '', 1)
logger.debug(f"Stripped SD card path (mmcblk pattern): {path} -> {stripped}")
return stripped
# No SD card pattern matched
return path
@staticmethod
+116 -20
View File
@@ -349,10 +349,17 @@ class WinetricksHandler:
self.logger.debug(f"Winetricks output: {result.stdout}")
if result.returncode == 0:
self.logger.info("Wine Component installation command completed successfully.")
# Set Windows 10 mode after component installation (matches legacy script timing)
self._set_windows_10_mode(wineprefix, env.get('WINE', ''))
return True
self.logger.info("Wine Component installation command completed.")
# Verify components were actually installed
if self._verify_components_installed(wineprefix, components_to_install, env):
self.logger.info("Component verification successful - all components installed correctly.")
# Set Windows 10 mode after component installation (matches legacy script timing)
self._set_windows_10_mode(wineprefix, env.get('WINE', ''))
return True
else:
self.logger.error(f"Component verification failed (Attempt {attempt}/{max_attempts})")
# Continue to retry
else:
# Special handling for dotnet40 verification issue (mimics protontricks behavior)
if "dotnet40" in components_to_install and "ngen.exe not found" in result.stderr:
@@ -430,11 +437,36 @@ class WinetricksHandler:
if winetricks_failed:
self.logger.error(f"Winetricks failed after {max_attempts} attempts.")
# Check if protontricks is available for fallback
# Network diagnostics before fallback (non-fatal)
self.logger.warning("=" * 80)
self.logger.warning("NETWORK DIAGNOSTICS: Testing connectivity to component download sources...")
try:
protontricks_check = subprocess.run(['which', 'protontricks'],
capture_output=True, text=True, timeout=5)
if protontricks_check.returncode == 0:
# Check if curl is available
curl_check = subprocess.run(['which', 'curl'], capture_output=True, timeout=5)
if curl_check.returncode == 0:
# Test Microsoft download servers (used by winetricks for .NET, VC runtimes, DirectX)
test_result = subprocess.run(['curl', '-I', '--max-time', '10', 'https://download.microsoft.com'],
capture_output=True, text=True, timeout=15)
if test_result.returncode == 0:
self.logger.warning("Can reach download.microsoft.com")
else:
self.logger.error("Cannot reach download.microsoft.com - network/DNS issue likely")
self.logger.error(f" Curl exit code: {test_result.returncode}")
if test_result.stderr:
self.logger.error(f" Curl error: {test_result.stderr.strip()}")
else:
self.logger.warning("curl not available, skipping network diagnostic test")
except Exception as e:
self.logger.warning(f"Network diagnostic test skipped: {e}")
self.logger.warning("=" * 80)
# Check if protontricks is available for fallback using centralized handler
try:
from .protontricks_handler import ProtontricksHandler
protontricks_handler = ProtontricksHandler()
protontricks_available = protontricks_handler.is_available()
if protontricks_available:
self.logger.warning("=" * 80)
self.logger.warning("AUTOMATIC FALLBACK: Winetricks failed, attempting protontricks fallback...")
self.logger.warning(f"Last winetricks error: {last_error_details}")
@@ -721,13 +753,6 @@ class WinetricksHandler:
if success:
self.logger.info(f"Legacy .NET components {legacy_components} installed successfully with protontricks")
# Enable dotfiles and symlinks for the prefix
if protontricks_handler.enable_dotfiles(appid):
self.logger.info("Enabled dotfiles and symlinks support")
else:
self.logger.warning("Failed to enable dotfiles/symlinks (non-critical)")
return True
else:
self.logger.error(f"Legacy .NET components {legacy_components} installation failed with protontricks")
@@ -844,11 +869,18 @@ class WinetricksHandler:
)
if result.returncode == 0:
self.logger.info(f"Winetricks components installed successfully: {components}")
# Set Windows 10 mode after component installation (matches legacy script timing)
wine_binary = env.get('WINE', '')
self._set_windows_10_mode(env.get('WINEPREFIX', ''), wine_binary)
return True
self.logger.info(f"Winetricks components installation command completed.")
# Verify components were actually installed
if self._verify_components_installed(wineprefix, components, env):
self.logger.info("Component verification successful - all components installed correctly.")
# Set Windows 10 mode after component installation (matches legacy script timing)
wine_binary = env.get('WINE', '')
self._set_windows_10_mode(env.get('WINEPREFIX', ''), wine_binary)
return True
else:
self.logger.error(f"Component verification failed (attempt {attempt})")
# Continue to retry
else:
self.logger.error(f"Winetricks failed (attempt {attempt}): {result.stderr.strip()}")
@@ -992,6 +1024,70 @@ class WinetricksHandler:
self.logger.error(f"Error getting wine binary for prefix: {e}")
return ""
def _verify_components_installed(self, wineprefix: str, components: List[str], env: dict) -> bool:
"""
Verify that Wine components were actually installed by checking winetricks.log.
Args:
wineprefix: Wine prefix path
components: List of components that should be installed
env: Environment variables (includes WINE path)
Returns:
bool: True if all critical components are verified, False otherwise
"""
try:
self.logger.info("Verifying installed components...")
# Check winetricks.log file for installed components
winetricks_log = os.path.join(wineprefix, 'winetricks.log')
if not os.path.exists(winetricks_log):
self.logger.error(f"winetricks.log not found at {winetricks_log}")
return False
try:
with open(winetricks_log, 'r', encoding='utf-8', errors='ignore') as f:
log_content = f.read().lower()
except Exception as e:
self.logger.error(f"Failed to read winetricks.log: {e}")
return False
self.logger.debug(f"winetricks.log length: {len(log_content)} bytes")
# Define critical components that MUST be installed
critical_components = ["vcrun2022", "xact"]
# Check for critical components
missing_critical = []
for component in critical_components:
if component.lower() not in log_content:
missing_critical.append(component)
if missing_critical:
self.logger.error(f"CRITICAL: Missing essential components: {missing_critical}")
self.logger.error("Installation reported success but components are NOT in winetricks.log")
return False
# Check for requested components (warn but don't fail)
missing_requested = []
for component in components:
# Handle settings like fontsmooth=rgb (just check the base component name)
base_component = component.split('=')[0].lower()
if base_component not in log_content and component.lower() not in log_content:
missing_requested.append(component)
if missing_requested:
self.logger.warning(f"Some requested components may not be installed: {missing_requested}")
self.logger.warning("This may cause issues, but critical components are present")
self.logger.info(f"Verification passed - critical components confirmed: {critical_components}")
return True
except Exception as e:
self.logger.error(f"Error verifying components: {e}", exc_info=True)
return False
def _cleanup_wine_processes(self):
"""
Internal method to clean up wine processes during component installation
@@ -2904,10 +2904,21 @@ echo Prefix creation complete.
"""Find a Steam game installation path by AppID and common names"""
import os
from pathlib import Path
# Get Steam libraries from libraryfolders.vdf
steam_config_path = Path.home() / ".steam/steam/config/libraryfolders.vdf"
if not steam_config_path.exists():
# Get Steam libraries from libraryfolders.vdf - check multiple possible locations
possible_config_paths = [
Path.home() / ".steam/steam/config/libraryfolders.vdf",
Path.home() / ".local/share/Steam/config/libraryfolders.vdf",
Path.home() / ".var/app/com.valvesoftware.Steam/.local/share/Steam/config/libraryfolders.vdf" # Flatpak
]
steam_config_path = None
for path in possible_config_paths:
if path.exists():
steam_config_path = path
break
if not steam_config_path:
return None
steam_libraries = []
@@ -135,6 +135,9 @@ class NativeSteamOperationsService:
steam_locations = [
Path.home() / ".steam/steam",
Path.home() / ".local/share/Steam",
# Flatpak Steam - direct data directory
Path.home() / ".var/app/com.valvesoftware.Steam/.local/share/Steam",
# Flatpak Steam - symlinked home paths
Path.home() / ".var/app/com.valvesoftware.Steam/home/.steam/steam",
Path.home() / ".var/app/com.valvesoftware.Steam/home/.local/share/Steam"
]
@@ -161,6 +164,9 @@ class NativeSteamOperationsService:
standard_locations = [
Path.home() / ".steam/steam/steamapps/compatdata",
Path.home() / ".local/share/Steam/steamapps/compatdata",
# Flatpak Steam - direct data directory
Path.home() / ".var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/compatdata",
# Flatpak Steam - symlinked home paths
Path.home() / ".var/app/com.valvesoftware.Steam/home/.steam/steam/steamapps/compatdata",
Path.home() / ".var/app/com.valvesoftware.Steam/home/.local/share/Steam/steamapps/compatdata"
]
@@ -127,20 +127,18 @@ class ProtontricksDetectionService:
try:
env = handler._get_clean_subprocess_env()
result = subprocess.run(
["flatpak", "list"],
capture_output=True,
["flatpak", "list"],
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL, # Suppress stderr to avoid error messages
text=True,
check=True,
env=env
)
if "com.github.Matoking.protontricks" in result.stdout:
if result.returncode == 0 and "com.github.Matoking.protontricks" in result.stdout:
logger.info("Flatpak Protontricks is installed")
handler.which_protontricks = 'flatpak'
return True
except FileNotFoundError:
logger.warning("'flatpak' command not found. Cannot check for Flatpak Protontricks.")
except subprocess.CalledProcessError as e:
logger.warning(f"Error checking flatpak list: {e}")
except Exception as e:
logger.error(f"Unexpected error checking flatpak: {e}")
@@ -5,6 +5,7 @@ import signal
import psutil
import logging
import sys
import shutil
from typing import Callable, Optional
logger = logging.getLogger(__name__)
@@ -86,6 +87,25 @@ def is_steam_deck() -> bool:
logger.debug(f"Error detecting Steam Deck: {e}")
return False
def is_flatpak_steam() -> bool:
"""Detect if Steam is installed as a Flatpak."""
try:
# First check if flatpak command exists
if not shutil.which('flatpak'):
return False
# Verify the app is actually installed (not just directory exists)
result = subprocess.run(['flatpak', 'list', '--app'],
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL, # Suppress stderr to avoid error messages
text=True,
timeout=5)
if result.returncode == 0 and 'com.valvesoftware.Steam' in result.stdout:
return True
except Exception as e:
logger.debug(f"Error detecting Flatpak Steam: {e}")
return False
def get_steam_processes() -> list:
"""Return a list of psutil.Process objects for running Steam processes."""
steam_procs = []
@@ -122,16 +142,37 @@ def start_steam() -> bool:
"""Attempt to start Steam using the exact methods from existing working logic."""
env = _get_clean_subprocess_env()
try:
# Try systemd user service (Steam Deck)
# Try systemd user service (Steam Deck) - HIGHEST PRIORITY
if is_steam_deck():
subprocess.Popen(["systemctl", "--user", "restart", "app-steam@autostart.service"], env=env)
return True
# Check if Flatpak Steam (only if not Steam Deck)
if is_flatpak_steam():
logger.info("Flatpak Steam detected - using flatpak run command")
try:
# Redirect flatpak's stderr to suppress "app not installed" errors on systems without flatpak Steam
# Steam's own stdout/stderr will still go through (flatpak forwards them)
subprocess.Popen(["flatpak", "run", "com.valvesoftware.Steam", "-silent"],
env=env, stderr=subprocess.DEVNULL)
time.sleep(5)
check_result = subprocess.run(['pgrep', '-f', 'steam'], capture_output=True, timeout=10, env=env)
if check_result.returncode == 0:
logger.info("Flatpak Steam process detected after start.")
return True
else:
logger.warning("Flatpak Steam process not detected after start attempt.")
return False
except Exception as e:
logger.error(f"Error starting Flatpak Steam: {e}")
return False
# Use startup methods with only -silent flag (no -minimized or -no-browser)
# Don't redirect stdout/stderr or use start_new_session to allow Steam to connect to display/tray
start_methods = [
{"name": "Popen", "cmd": ["steam", "-silent"], "kwargs": {"stdout": subprocess.DEVNULL, "stderr": subprocess.DEVNULL, "stdin": subprocess.DEVNULL, "start_new_session": True, "env": env}},
{"name": "setsid", "cmd": ["setsid", "steam", "-silent"], "kwargs": {"stdout": subprocess.DEVNULL, "stderr": subprocess.DEVNULL, "stdin": subprocess.DEVNULL, "env": env}},
{"name": "nohup", "cmd": ["nohup", "steam", "-silent"], "kwargs": {"stdout": subprocess.DEVNULL, "stderr": subprocess.DEVNULL, "stdin": subprocess.DEVNULL, "start_new_session": True, "preexec_fn": os.setpgrp, "env": env}}
{"name": "Popen", "cmd": ["steam", "-silent"], "kwargs": {"env": env}},
{"name": "setsid", "cmd": ["setsid", "steam", "-silent"], "kwargs": {"env": env}},
{"name": "nohup", "cmd": ["nohup", "steam", "-silent"], "kwargs": {"preexec_fn": os.setpgrp, "env": env}}
]
for method in start_methods:
@@ -174,17 +215,26 @@ def robust_steam_restart(progress_callback: Optional[Callable[[str], None]] = No
progress_callback(msg)
report("Shutting down Steam...")
# Steam Deck: Use systemctl for shutdown (special handling)
# Steam Deck: Use systemctl for shutdown (special handling) - HIGHEST PRIORITY
if is_steam_deck():
try:
report("Steam Deck detected - using systemctl shutdown...")
subprocess.run(['systemctl', '--user', 'stop', 'app-steam@autostart.service'],
subprocess.run(['systemctl', '--user', 'stop', 'app-steam@autostart.service'],
timeout=15, check=False, capture_output=True, env=env)
time.sleep(2)
except Exception as e:
logger.debug(f"systemctl stop failed on Steam Deck: {e}")
# Flatpak Steam: Use flatpak kill command (only if not Steam Deck)
elif is_flatpak_steam():
try:
report("Flatpak Steam detected - stopping via flatpak...")
subprocess.run(['flatpak', 'kill', 'com.valvesoftware.Steam'],
timeout=15, check=False, capture_output=True, stderr=subprocess.DEVNULL, env=env)
time.sleep(2)
except Exception as e:
logger.debug(f"flatpak kill failed: {e}")
# All systems: Use pkill approach (proven 15/16 test success rate)
try:
# Skip unreliable steam -shutdown, go straight to pkill
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+225 -225
View File
@@ -7,7 +7,7 @@
"targets": {
".NETCoreApp,Version=v8.0": {},
".NETCoreApp,Version=v8.0/linux-x64": {
"jackify-engine/0.3.17": {
"jackify-engine/0.3.18": {
"dependencies": {
"Markdig": "0.40.0",
"Microsoft.Extensions.Configuration.Json": "9.0.1",
@@ -22,16 +22,16 @@
"SixLabors.ImageSharp": "3.1.6",
"System.CommandLine": "2.0.0-beta4.22272.1",
"System.CommandLine.NamingConventionBinder": "2.0.0-beta4.22272.1",
"Wabbajack.CLI.Builder": "0.3.17",
"Wabbajack.Downloaders.Bethesda": "0.3.17",
"Wabbajack.Downloaders.Dispatcher": "0.3.17",
"Wabbajack.Hashing.xxHash64": "0.3.17",
"Wabbajack.Networking.Discord": "0.3.17",
"Wabbajack.Networking.GitHub": "0.3.17",
"Wabbajack.Paths.IO": "0.3.17",
"Wabbajack.Server.Lib": "0.3.17",
"Wabbajack.Services.OSIntegrated": "0.3.17",
"Wabbajack.VFS": "0.3.17",
"Wabbajack.CLI.Builder": "0.3.18",
"Wabbajack.Downloaders.Bethesda": "0.3.18",
"Wabbajack.Downloaders.Dispatcher": "0.3.18",
"Wabbajack.Hashing.xxHash64": "0.3.18",
"Wabbajack.Networking.Discord": "0.3.18",
"Wabbajack.Networking.GitHub": "0.3.18",
"Wabbajack.Paths.IO": "0.3.18",
"Wabbajack.Server.Lib": "0.3.18",
"Wabbajack.Services.OSIntegrated": "0.3.18",
"Wabbajack.VFS": "0.3.18",
"MegaApiClient": "1.0.0.0",
"runtimepack.Microsoft.NETCore.App.Runtime.linux-x64": "8.0.19"
},
@@ -1781,7 +1781,7 @@
}
}
},
"Wabbajack.CLI.Builder/0.3.17": {
"Wabbajack.CLI.Builder/0.3.18": {
"dependencies": {
"Microsoft.Extensions.Configuration.Json": "9.0.1",
"Microsoft.Extensions.DependencyInjection": "9.0.1",
@@ -1791,109 +1791,109 @@
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"System.CommandLine": "2.0.0-beta4.22272.1",
"System.CommandLine.NamingConventionBinder": "2.0.0-beta4.22272.1",
"Wabbajack.Paths": "0.3.17"
"Wabbajack.Paths": "0.3.18"
},
"runtime": {
"Wabbajack.CLI.Builder.dll": {}
}
},
"Wabbajack.Common/0.3.17": {
"Wabbajack.Common/0.3.18": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"System.Reactive": "6.0.1",
"Wabbajack.DTOs": "0.3.17",
"Wabbajack.Networking.Http": "0.3.17",
"Wabbajack.Paths.IO": "0.3.17"
"Wabbajack.DTOs": "0.3.18",
"Wabbajack.Networking.Http": "0.3.18",
"Wabbajack.Paths.IO": "0.3.18"
},
"runtime": {
"Wabbajack.Common.dll": {}
}
},
"Wabbajack.Compiler/0.3.17": {
"Wabbajack.Compiler/0.3.18": {
"dependencies": {
"F23.StringSimilarity": "6.0.0",
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Newtonsoft.Json": "13.0.3",
"SixLabors.ImageSharp": "3.1.6",
"Wabbajack.Downloaders.Dispatcher": "0.3.17",
"Wabbajack.Installer": "0.3.17",
"Wabbajack.VFS": "0.3.17",
"Wabbajack.Downloaders.Dispatcher": "0.3.18",
"Wabbajack.Installer": "0.3.18",
"Wabbajack.VFS": "0.3.18",
"ini-parser-netstandard": "2.5.2"
},
"runtime": {
"Wabbajack.Compiler.dll": {}
}
},
"Wabbajack.Compression.BSA/0.3.17": {
"Wabbajack.Compression.BSA/0.3.18": {
"dependencies": {
"K4os.Compression.LZ4.Streams": "1.3.8",
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"SharpZipLib": "1.4.2",
"Wabbajack.Common": "0.3.17",
"Wabbajack.DTOs": "0.3.17"
"Wabbajack.Common": "0.3.18",
"Wabbajack.DTOs": "0.3.18"
},
"runtime": {
"Wabbajack.Compression.BSA.dll": {}
}
},
"Wabbajack.Compression.Zip/0.3.17": {
"Wabbajack.Compression.Zip/0.3.18": {
"dependencies": {
"Wabbajack.IO.Async": "0.3.17"
"Wabbajack.IO.Async": "0.3.18"
},
"runtime": {
"Wabbajack.Compression.Zip.dll": {}
}
},
"Wabbajack.Configuration/0.3.17": {
"Wabbajack.Configuration/0.3.18": {
"runtime": {
"Wabbajack.Configuration.dll": {}
}
},
"Wabbajack.Downloaders.Bethesda/0.3.17": {
"Wabbajack.Downloaders.Bethesda/0.3.18": {
"dependencies": {
"LibAES-CTR": "1.1.0",
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"SharpZipLib": "1.4.2",
"Wabbajack.Common": "0.3.17",
"Wabbajack.Downloaders.Interfaces": "0.3.17",
"Wabbajack.Networking.BethesdaNet": "0.3.17"
"Wabbajack.Common": "0.3.18",
"Wabbajack.Downloaders.Interfaces": "0.3.18",
"Wabbajack.Networking.BethesdaNet": "0.3.18"
},
"runtime": {
"Wabbajack.Downloaders.Bethesda.dll": {}
}
},
"Wabbajack.Downloaders.Dispatcher/0.3.17": {
"Wabbajack.Downloaders.Dispatcher/0.3.18": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Newtonsoft.Json": "13.0.3",
"SixLabors.ImageSharp": "3.1.6",
"Wabbajack.Downloaders.Bethesda": "0.3.17",
"Wabbajack.Downloaders.GameFile": "0.3.17",
"Wabbajack.Downloaders.GoogleDrive": "0.3.17",
"Wabbajack.Downloaders.Http": "0.3.17",
"Wabbajack.Downloaders.IPS4OAuth2Downloader": "0.3.17",
"Wabbajack.Downloaders.Interfaces": "0.3.17",
"Wabbajack.Downloaders.Manual": "0.3.17",
"Wabbajack.Downloaders.MediaFire": "0.3.17",
"Wabbajack.Downloaders.Mega": "0.3.17",
"Wabbajack.Downloaders.ModDB": "0.3.17",
"Wabbajack.Downloaders.Nexus": "0.3.17",
"Wabbajack.Downloaders.VerificationCache": "0.3.17",
"Wabbajack.Downloaders.WabbajackCDN": "0.3.17",
"Wabbajack.Networking.WabbajackClientApi": "0.3.17"
"Wabbajack.Downloaders.Bethesda": "0.3.18",
"Wabbajack.Downloaders.GameFile": "0.3.18",
"Wabbajack.Downloaders.GoogleDrive": "0.3.18",
"Wabbajack.Downloaders.Http": "0.3.18",
"Wabbajack.Downloaders.IPS4OAuth2Downloader": "0.3.18",
"Wabbajack.Downloaders.Interfaces": "0.3.18",
"Wabbajack.Downloaders.Manual": "0.3.18",
"Wabbajack.Downloaders.MediaFire": "0.3.18",
"Wabbajack.Downloaders.Mega": "0.3.18",
"Wabbajack.Downloaders.ModDB": "0.3.18",
"Wabbajack.Downloaders.Nexus": "0.3.18",
"Wabbajack.Downloaders.VerificationCache": "0.3.18",
"Wabbajack.Downloaders.WabbajackCDN": "0.3.18",
"Wabbajack.Networking.WabbajackClientApi": "0.3.18"
},
"runtime": {
"Wabbajack.Downloaders.Dispatcher.dll": {}
}
},
"Wabbajack.Downloaders.GameFile/0.3.17": {
"Wabbajack.Downloaders.GameFile/0.3.18": {
"dependencies": {
"GameFinder.StoreHandlers.EADesktop": "4.5.0",
"GameFinder.StoreHandlers.EGS": "4.5.0",
@@ -1903,360 +1903,360 @@
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"SixLabors.ImageSharp": "3.1.6",
"Wabbajack.Downloaders.Interfaces": "0.3.17",
"Wabbajack.VFS": "0.3.17"
"Wabbajack.Downloaders.Interfaces": "0.3.18",
"Wabbajack.VFS": "0.3.18"
},
"runtime": {
"Wabbajack.Downloaders.GameFile.dll": {}
}
},
"Wabbajack.Downloaders.GoogleDrive/0.3.17": {
"Wabbajack.Downloaders.GoogleDrive/0.3.18": {
"dependencies": {
"HtmlAgilityPack": "1.11.72",
"Microsoft.AspNetCore.Http.Extensions": "2.3.0",
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Wabbajack.Common": "0.3.17",
"Wabbajack.DTOs": "0.3.17",
"Wabbajack.Downloaders.Interfaces": "0.3.17",
"Wabbajack.Networking.Http": "0.3.17",
"Wabbajack.Networking.Http.Interfaces": "0.3.17"
"Wabbajack.Common": "0.3.18",
"Wabbajack.DTOs": "0.3.18",
"Wabbajack.Downloaders.Interfaces": "0.3.18",
"Wabbajack.Networking.Http": "0.3.18",
"Wabbajack.Networking.Http.Interfaces": "0.3.18"
},
"runtime": {
"Wabbajack.Downloaders.GoogleDrive.dll": {}
}
},
"Wabbajack.Downloaders.Http/0.3.17": {
"Wabbajack.Downloaders.Http/0.3.18": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Wabbajack.Common": "0.3.17",
"Wabbajack.DTOs": "0.3.17",
"Wabbajack.Downloaders.Interfaces": "0.3.17",
"Wabbajack.Networking.BethesdaNet": "0.3.17",
"Wabbajack.Networking.Http.Interfaces": "0.3.17",
"Wabbajack.Paths.IO": "0.3.17"
"Wabbajack.Common": "0.3.18",
"Wabbajack.DTOs": "0.3.18",
"Wabbajack.Downloaders.Interfaces": "0.3.18",
"Wabbajack.Networking.BethesdaNet": "0.3.18",
"Wabbajack.Networking.Http.Interfaces": "0.3.18",
"Wabbajack.Paths.IO": "0.3.18"
},
"runtime": {
"Wabbajack.Downloaders.Http.dll": {}
}
},
"Wabbajack.Downloaders.Interfaces/0.3.17": {
"Wabbajack.Downloaders.Interfaces/0.3.18": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Wabbajack.Compression.Zip": "0.3.17",
"Wabbajack.DTOs": "0.3.17",
"Wabbajack.Paths.IO": "0.3.17"
"Wabbajack.Compression.Zip": "0.3.18",
"Wabbajack.DTOs": "0.3.18",
"Wabbajack.Paths.IO": "0.3.18"
},
"runtime": {
"Wabbajack.Downloaders.Interfaces.dll": {}
}
},
"Wabbajack.Downloaders.IPS4OAuth2Downloader/0.3.17": {
"Wabbajack.Downloaders.IPS4OAuth2Downloader/0.3.18": {
"dependencies": {
"F23.StringSimilarity": "6.0.0",
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Wabbajack.Common": "0.3.17",
"Wabbajack.Downloaders.Interfaces": "0.3.17",
"Wabbajack.Networking.Http": "0.3.17",
"Wabbajack.Networking.Http.Interfaces": "0.3.17"
"Wabbajack.Common": "0.3.18",
"Wabbajack.Downloaders.Interfaces": "0.3.18",
"Wabbajack.Networking.Http": "0.3.18",
"Wabbajack.Networking.Http.Interfaces": "0.3.18"
},
"runtime": {
"Wabbajack.Downloaders.IPS4OAuth2Downloader.dll": {}
}
},
"Wabbajack.Downloaders.Manual/0.3.17": {
"Wabbajack.Downloaders.Manual/0.3.18": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Wabbajack.Common": "0.3.17",
"Wabbajack.Downloaders.Interfaces": "0.3.17"
"Wabbajack.Common": "0.3.18",
"Wabbajack.Downloaders.Interfaces": "0.3.18"
},
"runtime": {
"Wabbajack.Downloaders.Manual.dll": {}
}
},
"Wabbajack.Downloaders.MediaFire/0.3.17": {
"Wabbajack.Downloaders.MediaFire/0.3.18": {
"dependencies": {
"HtmlAgilityPack": "1.11.72",
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Wabbajack.Common": "0.3.17",
"Wabbajack.Downloaders.Interfaces": "0.3.17",
"Wabbajack.Networking.Http.Interfaces": "0.3.17"
"Wabbajack.Common": "0.3.18",
"Wabbajack.Downloaders.Interfaces": "0.3.18",
"Wabbajack.Networking.Http.Interfaces": "0.3.18"
},
"runtime": {
"Wabbajack.Downloaders.MediaFire.dll": {}
}
},
"Wabbajack.Downloaders.Mega/0.3.17": {
"Wabbajack.Downloaders.Mega/0.3.18": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Newtonsoft.Json": "13.0.3",
"Wabbajack.Common": "0.3.17",
"Wabbajack.Downloaders.Interfaces": "0.3.17",
"Wabbajack.Paths.IO": "0.3.17"
"Wabbajack.Common": "0.3.18",
"Wabbajack.Downloaders.Interfaces": "0.3.18",
"Wabbajack.Paths.IO": "0.3.18"
},
"runtime": {
"Wabbajack.Downloaders.Mega.dll": {}
}
},
"Wabbajack.Downloaders.ModDB/0.3.17": {
"Wabbajack.Downloaders.ModDB/0.3.18": {
"dependencies": {
"HtmlAgilityPack": "1.11.72",
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Newtonsoft.Json": "13.0.3",
"Wabbajack.Common": "0.3.17",
"Wabbajack.Downloaders.Interfaces": "0.3.17",
"Wabbajack.Networking.Http": "0.3.17",
"Wabbajack.Networking.Http.Interfaces": "0.3.17"
"Wabbajack.Common": "0.3.18",
"Wabbajack.Downloaders.Interfaces": "0.3.18",
"Wabbajack.Networking.Http": "0.3.18",
"Wabbajack.Networking.Http.Interfaces": "0.3.18"
},
"runtime": {
"Wabbajack.Downloaders.ModDB.dll": {}
}
},
"Wabbajack.Downloaders.Nexus/0.3.17": {
"Wabbajack.Downloaders.Nexus/0.3.18": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Wabbajack.DTOs": "0.3.17",
"Wabbajack.Downloaders.Interfaces": "0.3.17",
"Wabbajack.Hashing.xxHash64": "0.3.17",
"Wabbajack.Networking.Http": "0.3.17",
"Wabbajack.Networking.Http.Interfaces": "0.3.17",
"Wabbajack.Networking.NexusApi": "0.3.17",
"Wabbajack.Paths": "0.3.17"
"Wabbajack.DTOs": "0.3.18",
"Wabbajack.Downloaders.Interfaces": "0.3.18",
"Wabbajack.Hashing.xxHash64": "0.3.18",
"Wabbajack.Networking.Http": "0.3.18",
"Wabbajack.Networking.Http.Interfaces": "0.3.18",
"Wabbajack.Networking.NexusApi": "0.3.18",
"Wabbajack.Paths": "0.3.18"
},
"runtime": {
"Wabbajack.Downloaders.Nexus.dll": {}
}
},
"Wabbajack.Downloaders.VerificationCache/0.3.17": {
"Wabbajack.Downloaders.VerificationCache/0.3.18": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Stub.System.Data.SQLite.Core.NetStandard": "1.0.119",
"Wabbajack.DTOs": "0.3.17",
"Wabbajack.Paths.IO": "0.3.17"
"Wabbajack.DTOs": "0.3.18",
"Wabbajack.Paths.IO": "0.3.18"
},
"runtime": {
"Wabbajack.Downloaders.VerificationCache.dll": {}
}
},
"Wabbajack.Downloaders.WabbajackCDN/0.3.17": {
"Wabbajack.Downloaders.WabbajackCDN/0.3.18": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Microsoft.Toolkit.HighPerformance": "7.1.2",
"Wabbajack.Common": "0.3.17",
"Wabbajack.Downloaders.Interfaces": "0.3.17",
"Wabbajack.Networking.Http": "0.3.17",
"Wabbajack.RateLimiter": "0.3.17"
"Wabbajack.Common": "0.3.18",
"Wabbajack.Downloaders.Interfaces": "0.3.18",
"Wabbajack.Networking.Http": "0.3.18",
"Wabbajack.RateLimiter": "0.3.18"
},
"runtime": {
"Wabbajack.Downloaders.WabbajackCDN.dll": {}
}
},
"Wabbajack.DTOs/0.3.17": {
"Wabbajack.DTOs/0.3.18": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Wabbajack.Hashing.xxHash64": "0.3.17",
"Wabbajack.Paths": "0.3.17"
"Wabbajack.Hashing.xxHash64": "0.3.18",
"Wabbajack.Paths": "0.3.18"
},
"runtime": {
"Wabbajack.DTOs.dll": {}
}
},
"Wabbajack.FileExtractor/0.3.17": {
"Wabbajack.FileExtractor/0.3.18": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"OMODFramework": "3.0.1",
"Wabbajack.Common": "0.3.17",
"Wabbajack.Compression.BSA": "0.3.17",
"Wabbajack.Hashing.PHash": "0.3.17",
"Wabbajack.Paths": "0.3.17"
"Wabbajack.Common": "0.3.18",
"Wabbajack.Compression.BSA": "0.3.18",
"Wabbajack.Hashing.PHash": "0.3.18",
"Wabbajack.Paths": "0.3.18"
},
"runtime": {
"Wabbajack.FileExtractor.dll": {}
}
},
"Wabbajack.Hashing.PHash/0.3.17": {
"Wabbajack.Hashing.PHash/0.3.18": {
"dependencies": {
"BCnEncoder.Net.ImageSharp": "1.1.1",
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Shipwreck.Phash": "0.5.0",
"SixLabors.ImageSharp": "3.1.6",
"Wabbajack.Common": "0.3.17",
"Wabbajack.DTOs": "0.3.17",
"Wabbajack.Paths": "0.3.17",
"Wabbajack.Paths.IO": "0.3.17"
"Wabbajack.Common": "0.3.18",
"Wabbajack.DTOs": "0.3.18",
"Wabbajack.Paths": "0.3.18",
"Wabbajack.Paths.IO": "0.3.18"
},
"runtime": {
"Wabbajack.Hashing.PHash.dll": {}
}
},
"Wabbajack.Hashing.xxHash64/0.3.17": {
"Wabbajack.Hashing.xxHash64/0.3.18": {
"dependencies": {
"Wabbajack.Paths": "0.3.17",
"Wabbajack.RateLimiter": "0.3.17"
"Wabbajack.Paths": "0.3.18",
"Wabbajack.RateLimiter": "0.3.18"
},
"runtime": {
"Wabbajack.Hashing.xxHash64.dll": {}
}
},
"Wabbajack.Installer/0.3.17": {
"Wabbajack.Installer/0.3.18": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Newtonsoft.Json": "13.0.3",
"Octopus.Octodiff": "2.0.548",
"SixLabors.ImageSharp": "3.1.6",
"Wabbajack.DTOs": "0.3.17",
"Wabbajack.Downloaders.Dispatcher": "0.3.17",
"Wabbajack.Downloaders.GameFile": "0.3.17",
"Wabbajack.FileExtractor": "0.3.17",
"Wabbajack.Networking.WabbajackClientApi": "0.3.17",
"Wabbajack.Paths": "0.3.17",
"Wabbajack.Paths.IO": "0.3.17",
"Wabbajack.VFS": "0.3.17",
"Wabbajack.DTOs": "0.3.18",
"Wabbajack.Downloaders.Dispatcher": "0.3.18",
"Wabbajack.Downloaders.GameFile": "0.3.18",
"Wabbajack.FileExtractor": "0.3.18",
"Wabbajack.Networking.WabbajackClientApi": "0.3.18",
"Wabbajack.Paths": "0.3.18",
"Wabbajack.Paths.IO": "0.3.18",
"Wabbajack.VFS": "0.3.18",
"ini-parser-netstandard": "2.5.2"
},
"runtime": {
"Wabbajack.Installer.dll": {}
}
},
"Wabbajack.IO.Async/0.3.17": {
"Wabbajack.IO.Async/0.3.18": {
"runtime": {
"Wabbajack.IO.Async.dll": {}
}
},
"Wabbajack.Networking.BethesdaNet/0.3.17": {
"Wabbajack.Networking.BethesdaNet/0.3.18": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Wabbajack.DTOs": "0.3.17",
"Wabbajack.Networking.Http": "0.3.17",
"Wabbajack.Networking.Http.Interfaces": "0.3.17"
"Wabbajack.DTOs": "0.3.18",
"Wabbajack.Networking.Http": "0.3.18",
"Wabbajack.Networking.Http.Interfaces": "0.3.18"
},
"runtime": {
"Wabbajack.Networking.BethesdaNet.dll": {}
}
},
"Wabbajack.Networking.Discord/0.3.17": {
"Wabbajack.Networking.Discord/0.3.18": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Wabbajack.Networking.Http.Interfaces": "0.3.17"
"Wabbajack.Networking.Http.Interfaces": "0.3.18"
},
"runtime": {
"Wabbajack.Networking.Discord.dll": {}
}
},
"Wabbajack.Networking.GitHub/0.3.17": {
"Wabbajack.Networking.GitHub/0.3.18": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Octokit": "14.0.0",
"Wabbajack.DTOs": "0.3.17",
"Wabbajack.Networking.Http.Interfaces": "0.3.17"
"Wabbajack.DTOs": "0.3.18",
"Wabbajack.Networking.Http.Interfaces": "0.3.18"
},
"runtime": {
"Wabbajack.Networking.GitHub.dll": {}
}
},
"Wabbajack.Networking.Http/0.3.17": {
"Wabbajack.Networking.Http/0.3.18": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Http": "9.0.1",
"Microsoft.Extensions.Logging": "9.0.1",
"Wabbajack.Configuration": "0.3.17",
"Wabbajack.Downloaders.Interfaces": "0.3.17",
"Wabbajack.Hashing.xxHash64": "0.3.17",
"Wabbajack.Networking.Http.Interfaces": "0.3.17",
"Wabbajack.Paths": "0.3.17",
"Wabbajack.Paths.IO": "0.3.17"
"Wabbajack.Configuration": "0.3.18",
"Wabbajack.Downloaders.Interfaces": "0.3.18",
"Wabbajack.Hashing.xxHash64": "0.3.18",
"Wabbajack.Networking.Http.Interfaces": "0.3.18",
"Wabbajack.Paths": "0.3.18",
"Wabbajack.Paths.IO": "0.3.18"
},
"runtime": {
"Wabbajack.Networking.Http.dll": {}
}
},
"Wabbajack.Networking.Http.Interfaces/0.3.17": {
"Wabbajack.Networking.Http.Interfaces/0.3.18": {
"dependencies": {
"Wabbajack.Hashing.xxHash64": "0.3.17"
"Wabbajack.Hashing.xxHash64": "0.3.18"
},
"runtime": {
"Wabbajack.Networking.Http.Interfaces.dll": {}
}
},
"Wabbajack.Networking.NexusApi/0.3.17": {
"Wabbajack.Networking.NexusApi/0.3.18": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Wabbajack.DTOs": "0.3.17",
"Wabbajack.Networking.Http": "0.3.17",
"Wabbajack.Networking.Http.Interfaces": "0.3.17",
"Wabbajack.Networking.WabbajackClientApi": "0.3.17"
"Wabbajack.DTOs": "0.3.18",
"Wabbajack.Networking.Http": "0.3.18",
"Wabbajack.Networking.Http.Interfaces": "0.3.18",
"Wabbajack.Networking.WabbajackClientApi": "0.3.18"
},
"runtime": {
"Wabbajack.Networking.NexusApi.dll": {}
}
},
"Wabbajack.Networking.WabbajackClientApi/0.3.17": {
"Wabbajack.Networking.WabbajackClientApi/0.3.18": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Octokit": "14.0.0",
"Wabbajack.Common": "0.3.17",
"Wabbajack.DTOs": "0.3.17",
"Wabbajack.Paths.IO": "0.3.17",
"Wabbajack.VFS.Interfaces": "0.3.17",
"Wabbajack.Common": "0.3.18",
"Wabbajack.DTOs": "0.3.18",
"Wabbajack.Paths.IO": "0.3.18",
"Wabbajack.VFS.Interfaces": "0.3.18",
"YamlDotNet": "16.3.0"
},
"runtime": {
"Wabbajack.Networking.WabbajackClientApi.dll": {}
}
},
"Wabbajack.Paths/0.3.17": {
"Wabbajack.Paths/0.3.18": {
"runtime": {
"Wabbajack.Paths.dll": {}
}
},
"Wabbajack.Paths.IO/0.3.17": {
"Wabbajack.Paths.IO/0.3.18": {
"dependencies": {
"Wabbajack.Paths": "0.3.17",
"Wabbajack.Paths": "0.3.18",
"shortid": "4.0.0"
},
"runtime": {
"Wabbajack.Paths.IO.dll": {}
}
},
"Wabbajack.RateLimiter/0.3.17": {
"Wabbajack.RateLimiter/0.3.18": {
"runtime": {
"Wabbajack.RateLimiter.dll": {}
}
},
"Wabbajack.Server.Lib/0.3.17": {
"Wabbajack.Server.Lib/0.3.18": {
"dependencies": {
"FluentFTP": "52.0.0",
"Microsoft.Extensions.DependencyInjection": "9.0.1",
@@ -2264,58 +2264,58 @@
"Nettle": "3.0.0",
"Newtonsoft.Json": "13.0.3",
"SixLabors.ImageSharp": "3.1.6",
"Wabbajack.Common": "0.3.17",
"Wabbajack.Networking.Http.Interfaces": "0.3.17",
"Wabbajack.Services.OSIntegrated": "0.3.17"
"Wabbajack.Common": "0.3.18",
"Wabbajack.Networking.Http.Interfaces": "0.3.18",
"Wabbajack.Services.OSIntegrated": "0.3.18"
},
"runtime": {
"Wabbajack.Server.Lib.dll": {}
}
},
"Wabbajack.Services.OSIntegrated/0.3.17": {
"Wabbajack.Services.OSIntegrated/0.3.18": {
"dependencies": {
"DeviceId": "6.8.0",
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Newtonsoft.Json": "13.0.3",
"SixLabors.ImageSharp": "3.1.6",
"Wabbajack.Compiler": "0.3.17",
"Wabbajack.Downloaders.Dispatcher": "0.3.17",
"Wabbajack.Installer": "0.3.17",
"Wabbajack.Networking.BethesdaNet": "0.3.17",
"Wabbajack.Networking.Discord": "0.3.17",
"Wabbajack.VFS": "0.3.17"
"Wabbajack.Compiler": "0.3.18",
"Wabbajack.Downloaders.Dispatcher": "0.3.18",
"Wabbajack.Installer": "0.3.18",
"Wabbajack.Networking.BethesdaNet": "0.3.18",
"Wabbajack.Networking.Discord": "0.3.18",
"Wabbajack.VFS": "0.3.18"
},
"runtime": {
"Wabbajack.Services.OSIntegrated.dll": {}
}
},
"Wabbajack.VFS/0.3.17": {
"Wabbajack.VFS/0.3.18": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"SixLabors.ImageSharp": "3.1.6",
"System.Data.SQLite.Core": "1.0.119",
"Wabbajack.Common": "0.3.17",
"Wabbajack.FileExtractor": "0.3.17",
"Wabbajack.Hashing.PHash": "0.3.17",
"Wabbajack.Hashing.xxHash64": "0.3.17",
"Wabbajack.Paths": "0.3.17",
"Wabbajack.Paths.IO": "0.3.17",
"Wabbajack.VFS.Interfaces": "0.3.17"
"Wabbajack.Common": "0.3.18",
"Wabbajack.FileExtractor": "0.3.18",
"Wabbajack.Hashing.PHash": "0.3.18",
"Wabbajack.Hashing.xxHash64": "0.3.18",
"Wabbajack.Paths": "0.3.18",
"Wabbajack.Paths.IO": "0.3.18",
"Wabbajack.VFS.Interfaces": "0.3.18"
},
"runtime": {
"Wabbajack.VFS.dll": {}
}
},
"Wabbajack.VFS.Interfaces/0.3.17": {
"Wabbajack.VFS.Interfaces/0.3.18": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Wabbajack.DTOs": "0.3.17",
"Wabbajack.Hashing.xxHash64": "0.3.17",
"Wabbajack.Paths": "0.3.17"
"Wabbajack.DTOs": "0.3.18",
"Wabbajack.Hashing.xxHash64": "0.3.18",
"Wabbajack.Paths": "0.3.18"
},
"runtime": {
"Wabbajack.VFS.Interfaces.dll": {}
@@ -2332,7 +2332,7 @@
}
},
"libraries": {
"jackify-engine/0.3.17": {
"jackify-engine/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
@@ -3021,202 +3021,202 @@
"path": "yamldotnet/16.3.0",
"hashPath": "yamldotnet.16.3.0.nupkg.sha512"
},
"Wabbajack.CLI.Builder/0.3.17": {
"Wabbajack.CLI.Builder/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Common/0.3.17": {
"Wabbajack.Common/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Compiler/0.3.17": {
"Wabbajack.Compiler/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Compression.BSA/0.3.17": {
"Wabbajack.Compression.BSA/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Compression.Zip/0.3.17": {
"Wabbajack.Compression.Zip/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Configuration/0.3.17": {
"Wabbajack.Configuration/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.Bethesda/0.3.17": {
"Wabbajack.Downloaders.Bethesda/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.Dispatcher/0.3.17": {
"Wabbajack.Downloaders.Dispatcher/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.GameFile/0.3.17": {
"Wabbajack.Downloaders.GameFile/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.GoogleDrive/0.3.17": {
"Wabbajack.Downloaders.GoogleDrive/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.Http/0.3.17": {
"Wabbajack.Downloaders.Http/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.Interfaces/0.3.17": {
"Wabbajack.Downloaders.Interfaces/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.IPS4OAuth2Downloader/0.3.17": {
"Wabbajack.Downloaders.IPS4OAuth2Downloader/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.Manual/0.3.17": {
"Wabbajack.Downloaders.Manual/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.MediaFire/0.3.17": {
"Wabbajack.Downloaders.MediaFire/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.Mega/0.3.17": {
"Wabbajack.Downloaders.Mega/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.ModDB/0.3.17": {
"Wabbajack.Downloaders.ModDB/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.Nexus/0.3.17": {
"Wabbajack.Downloaders.Nexus/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.VerificationCache/0.3.17": {
"Wabbajack.Downloaders.VerificationCache/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Downloaders.WabbajackCDN/0.3.17": {
"Wabbajack.Downloaders.WabbajackCDN/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.DTOs/0.3.17": {
"Wabbajack.DTOs/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.FileExtractor/0.3.17": {
"Wabbajack.FileExtractor/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Hashing.PHash/0.3.17": {
"Wabbajack.Hashing.PHash/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Hashing.xxHash64/0.3.17": {
"Wabbajack.Hashing.xxHash64/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Installer/0.3.17": {
"Wabbajack.Installer/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.IO.Async/0.3.17": {
"Wabbajack.IO.Async/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Networking.BethesdaNet/0.3.17": {
"Wabbajack.Networking.BethesdaNet/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Networking.Discord/0.3.17": {
"Wabbajack.Networking.Discord/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Networking.GitHub/0.3.17": {
"Wabbajack.Networking.GitHub/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Networking.Http/0.3.17": {
"Wabbajack.Networking.Http/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Networking.Http.Interfaces/0.3.17": {
"Wabbajack.Networking.Http.Interfaces/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Networking.NexusApi/0.3.17": {
"Wabbajack.Networking.NexusApi/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Networking.WabbajackClientApi/0.3.17": {
"Wabbajack.Networking.WabbajackClientApi/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Paths/0.3.17": {
"Wabbajack.Paths/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Paths.IO/0.3.17": {
"Wabbajack.Paths.IO/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.RateLimiter/0.3.17": {
"Wabbajack.RateLimiter/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Server.Lib/0.3.17": {
"Wabbajack.Server.Lib/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.Services.OSIntegrated/0.3.17": {
"Wabbajack.Services.OSIntegrated/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.VFS/0.3.17": {
"Wabbajack.VFS/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Wabbajack.VFS.Interfaces/0.3.17": {
"Wabbajack.VFS.Interfaces/0.3.18": {
"type": "project",
"serviceable": false,
"sha512": ""
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 335 B

@@ -183,7 +183,7 @@ class NextStepsDialog(QDialog):
def _build_completion_text(self) -> str:
"""
Build the completion text matching the CLI version from menu_handler.py.
Returns:
Formatted completion text string
"""
@@ -195,6 +195,9 @@ Modlist Install and Configuration complete!:
You should now be able to Launch '{self.modlist_name}' through Steam.
Congratulations and enjoy the game!
NOTE: If you experience ENB issues, consider using GE-Proton 10-14 instead of
Valve's Proton 10 (known ENB compatibility issues in Valve's Proton 10).
Detailed log available at: ~/Jackify/logs/Configure_New_Modlist_workflow.log"""
return completion_text
+15 -23
View File
@@ -40,12 +40,12 @@ class SuccessDialog(QDialog):
self.setWindowTitle("Success!")
self.setWindowModality(Qt.NonModal)
self.setAttribute(Qt.WA_ShowWithoutActivating, True)
self.setFixedSize(500, 420)
self.setFixedSize(500, 500)
self.setWindowFlag(Qt.WindowDoesNotAcceptFocus, True)
self.setStyleSheet("QDialog { background: #181818; color: #fff; border-radius: 12px; }" )
layout = QVBoxLayout(self)
layout.setSpacing(0)
layout.setContentsMargins(0, 0, 0, 0)
layout.setContentsMargins(30, 30, 30, 30)
# --- Card background for content ---
card = QFrame(self)
@@ -53,6 +53,7 @@ class SuccessDialog(QDialog):
card.setFrameShape(QFrame.StyledPanel)
card.setFrameShadow(QFrame.Raised)
card.setFixedWidth(440)
card.setMinimumHeight(380)
card_layout = QVBoxLayout(card)
card_layout.setSpacing(12)
card_layout.setContentsMargins(28, 28, 28, 28)
@@ -65,23 +66,6 @@ class SuccessDialog(QDialog):
)
card.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
# Trophy icon (smaller, more subtle)
trophy_label = QLabel()
trophy_label.setAlignment(Qt.AlignCenter)
trophy_icon_path = Path(__file__).parent.parent.parent.parent.parent / "Files" / "trophy.png"
if trophy_icon_path.exists():
pixmap = QPixmap(str(trophy_icon_path)).scaled(36, 36, Qt.KeepAspectRatio, Qt.SmoothTransformation)
trophy_label.setPixmap(pixmap)
else:
trophy_label.setText("")
trophy_label.setStyleSheet(
"QLabel { "
" font-size: 28px; "
" margin-bottom: 4px; "
"}"
)
card_layout.addWidget(trophy_label)
# Success title (less saturated green)
title_label = QLabel("Success!")
title_label.setAlignment(Qt.AlignCenter)
@@ -137,11 +121,12 @@ class SuccessDialog(QDialog):
next_steps_label = QLabel(next_steps_text)
next_steps_label.setAlignment(Qt.AlignCenter)
next_steps_label.setWordWrap(True)
next_steps_label.setMinimumHeight(100)
next_steps_label.setStyleSheet(
"QLabel { "
" font-size: 13px; "
" color: #b0b0b0; "
" line-height: 1.2; "
" line-height: 1.4; "
" padding: 6px; "
" background-color: transparent; "
" border-radius: 6px; "
@@ -232,15 +217,22 @@ class SuccessDialog(QDialog):
def _build_next_steps(self) -> str:
"""
Build the next steps guidance based on workflow type.
Returns:
Formatted next steps string
"""
game_display = self.game_name or self.modlist_name
base_message = ""
if self.workflow_type == "tuxborn":
return f"You can now launch Tuxborn from Steam and enjoy your modded {game_display} experience!"
base_message = f"You can now launch Tuxborn from Steam and enjoy your modded {game_display} experience!"
else:
return f"You can now launch {self.modlist_name} from Steam and enjoy your modded {game_display} experience!"
base_message = f"You can now launch {self.modlist_name} from Steam and enjoy your modded {game_display} experience!"
# Add GE-Proton recommendation
proton_note = "\n\nNOTE: If you experience ENB issues, consider using GE-Proton 10-14 instead of Valve's Proton 10 (known ENB compatibility issues)."
return base_message + proton_note
def _update_countdown(self):
if self._countdown > 0: