Sync from development - prepare for v0.1.5

This commit is contained in:
Omni
2025-09-26 12:45:21 +01:00
parent c9bd6f60e6
commit f46ed2c0fe
26 changed files with 20459 additions and 2522 deletions

View File

@@ -1,247 +0,0 @@
"""
Tuxborn Command
CLI command for the Tuxborn Automatic Installer.
Extracted from the original jackify-cli.py.
"""
import os
import sys
import logging
from pathlib import Path
from typing import Optional
# Import the backend services we'll need
from jackify.backend.models.modlist import ModlistContext
from jackify.shared.colors import COLOR_INFO, COLOR_ERROR, COLOR_RESET
logger = logging.getLogger(__name__)
class TuxbornCommand:
"""Handler for the tuxborn-auto CLI command."""
def __init__(self, backend_services, system_info):
"""Initialize with backend services.
Args:
backend_services: Dictionary of backend service instances
system_info: System information (steamdeck flag, etc.)
"""
self.backend_services = backend_services
self.system_info = system_info
def add_args(self, parser):
"""Add tuxborn-auto arguments to the main parser.
Args:
parser: The main ArgumentParser
"""
parser.add_argument(
"--tuxborn-auto",
action="store_true",
help="Run the Tuxborn Automatic Installer non-interactively (for GUI integration)"
)
parser.add_argument(
"--install-dir",
type=str,
help="Install directory for Tuxborn (required with --tuxborn-auto)"
)
parser.add_argument(
"--download-dir",
type=str,
help="Downloads directory for Tuxborn (required with --tuxborn-auto)"
)
parser.add_argument(
"--modlist-name",
type=str,
default="Tuxborn",
help="Modlist name (optional, defaults to 'Tuxborn')"
)
def execute(self, args) -> int:
"""Execute the tuxborn-auto command.
Args:
args: Parsed command-line arguments
Returns:
Exit code (0 for success, 1 for failure)
"""
logger.info("Starting Tuxborn Automatic Installer (GUI integration mode)")
try:
# Set up logging redirection (copied from original)
self._setup_tee_logging()
# Build context from args
context = self._build_context_from_args(args)
# Validate required fields
if not self._validate_context(context):
return 1
# Use legacy implementation for now - will migrate to backend services later
result = self._execute_legacy_tuxborn(context)
logger.info("Finished Tuxborn Automatic Installer")
return result
except Exception as e:
logger.error(f"Failed to run Tuxborn installer: {e}")
print(f"{COLOR_ERROR}Tuxborn installation failed: {e}{COLOR_RESET}")
return 1
finally:
# Restore stdout/stderr
self._restore_stdout_stderr()
def _build_context_from_args(self, args) -> dict:
"""Build context dictionary from command arguments.
Args:
args: Parsed command-line arguments
Returns:
Context dictionary
"""
install_dir = getattr(args, 'install_dir', None)
download_dir = getattr(args, 'download_dir', None)
modlist_name = getattr(args, 'modlist_name', 'Tuxborn')
machineid = 'Tuxborn/Tuxborn'
# Try to get API key from saved config first, then environment variable
from jackify.backend.services.api_key_service import APIKeyService
api_key_service = APIKeyService()
api_key = api_key_service.get_saved_api_key()
if not api_key:
api_key = os.environ.get('NEXUS_API_KEY')
resolution = getattr(args, 'resolution', None)
mo2_exe_path = getattr(args, 'mo2_exe_path', None)
skip_confirmation = True # Always true in GUI mode
context = {
'machineid': machineid,
'modlist_name': modlist_name,
'install_dir': install_dir,
'download_dir': download_dir,
'nexus_api_key': api_key,
'skip_confirmation': skip_confirmation,
'resolution': resolution,
'mo2_exe_path': mo2_exe_path,
}
# PATCH: Always set modlist_value and modlist_source for Tuxborn workflow
context['modlist_value'] = 'Tuxborn/Tuxborn'
context['modlist_source'] = 'identifier'
return context
def _validate_context(self, context: dict) -> bool:
"""Validate Tuxborn context.
Args:
context: Tuxborn context dictionary
Returns:
True if valid, False otherwise
"""
required_keys = ['modlist_name', 'install_dir', 'download_dir', 'nexus_api_key']
missing = [k for k in required_keys if not context.get(k)]
if missing:
print(f"{COLOR_ERROR}Missing required arguments for --tuxborn-auto.\\n"
f"--install-dir, --download-dir, and NEXUS_API_KEY (env, 32+ chars) are required.{COLOR_RESET}")
return False
return True
def _setup_tee_logging(self):
"""Set up TEE logging (copied from original implementation)."""
import shutil
# TEE logging setup & log rotation (copied from original)
class TeeStdout:
def __init__(self, *files):
self.files = files
def write(self, data):
for f in self.files:
f.write(data)
f.flush()
def flush(self):
for f in self.files:
f.flush()
log_dir = Path.home() / "Jackify" / "logs"
log_dir.mkdir(parents=True, exist_ok=True)
workflow_log_path = log_dir / "tuxborn_workflow.log"
# Log rotation: keep last 3 logs, 1KB each (for testing)
max_logs = 3
max_size = 1024 # 1KB for testing
if workflow_log_path.exists() and workflow_log_path.stat().st_size > max_size:
for i in range(max_logs, 0, -1):
prev = log_dir / f"tuxborn_workflow.log.{i-1}" if i > 1 else workflow_log_path
dest = log_dir / f"tuxborn_workflow.log.{i}"
if prev.exists():
if dest.exists():
dest.unlink()
prev.rename(dest)
self.workflow_log = open(workflow_log_path, 'a')
self.orig_stdout, self.orig_stderr = sys.stdout, sys.stderr
sys.stdout = TeeStdout(sys.stdout, self.workflow_log)
sys.stderr = TeeStdout(sys.stderr, self.workflow_log)
def _restore_stdout_stderr(self):
"""Restore original stdout/stderr."""
if hasattr(self, 'orig_stdout'):
sys.stdout = self.orig_stdout
sys.stderr = self.orig_stderr
if hasattr(self, 'workflow_log'):
self.workflow_log.close()
def _execute_legacy_tuxborn(self, context: dict) -> int:
"""Execute Tuxborn using legacy implementation.
Args:
context: Tuxborn context dictionary
Returns:
Exit code
"""
# Import backend services
from jackify.backend.core.modlist_operations import ModlistInstallCLI
from jackify.backend.handlers.menu_handler import MenuHandler
# Create legacy handler instances
menu_handler = MenuHandler()
modlist_cli = ModlistInstallCLI(
menu_handler=menu_handler,
steamdeck=self.system_info.get('is_steamdeck', False)
)
confirmed_context = modlist_cli.run_discovery_phase(context_override=context)
if confirmed_context:
menu_handler.logger.info("Tuxborn discovery confirmed by GUI. Proceeding to configuration/installation.")
modlist_cli.configuration_phase()
# Handle GUI integration prompts (copied from original)
print('[PROMPT:RESTART_STEAM]')
if os.environ.get('JACKIFY_GUI_MODE'):
input() # Wait for GUI to send confirmation, no CLI prompt
else:
answer = input('Restart Steam automatically now? (Y/n): ')
# ... handle answer as before ...
print('[PROMPT:MANUAL_STEPS]')
if os.environ.get('JACKIFY_GUI_MODE'):
input() # Wait for GUI to send confirmation, no CLI prompt
else:
input('Once you have completed ALL the steps above, press Enter to continue...')
return 0
else:
menu_handler.logger.info("Tuxborn discovery/confirmation cancelled or failed (GUI mode).")
print(f"{COLOR_INFO}Tuxborn installation cancelled or not confirmed.{COLOR_RESET}")
return 1

View File

@@ -21,11 +21,9 @@ from jackify import __version__ as jackify_version
# Import our command handlers
from .commands.configure_modlist import ConfigureModlistCommand
from .commands.install_modlist import InstallModlistCommand
from .commands.tuxborn import TuxbornCommand
# Import our menu handlers
from .menus.main_menu import MainMenuHandler
from .menus.tuxborn_menu import TuxbornMenuHandler
from .menus.wabbajack_menu import WabbajackMenuHandler
from .menus.hoolamike_menu import HoolamikeMenuHandler
from .menus.additional_menu import AdditionalMenuHandler
@@ -280,7 +278,6 @@ class JackifyCLI:
commands = {
'configure_modlist': ConfigureModlistCommand(self.backend_services),
'install_modlist': InstallModlistCommand(self.backend_services, self.system_info),
'tuxborn': TuxbornCommand(self.backend_services, self.system_info)
}
return commands
@@ -292,7 +289,6 @@ class JackifyCLI:
"""
menus = {
'main': MainMenuHandler(dev_mode=getattr(self, 'dev_mode', False)),
'tuxborn': TuxbornMenuHandler(),
'wabbajack': WabbajackMenuHandler(),
'hoolamike': HoolamikeMenuHandler(),
'additional': AdditionalMenuHandler()
@@ -371,10 +367,6 @@ class JackifyCLI:
self._debug_print('Entering restart_steam workflow')
return self._handle_restart_steam()
# Handle Tuxborn auto mode
if getattr(self.args, 'tuxborn_auto', False):
self._debug_print('Entering Tuxborn workflow')
return self.commands['tuxborn'].execute(self.args)
# Handle install-modlist top-level functionality
if getattr(self.args, 'install_modlist', False):
@@ -404,7 +396,6 @@ class JackifyCLI:
parser.add_argument('--update', action='store_true', help='Check for and install updates')
# Add command-specific arguments
self.commands['tuxborn'].add_args(parser)
self.commands['install_modlist'].add_top_level_args(parser)
# Add subcommands
@@ -459,8 +450,6 @@ class JackifyCLI:
return 0
elif choice == "wabbajack":
self.menus['wabbajack'].show_wabbajack_tasks_menu(self)
elif choice == "tuxborn":
self.menus['tuxborn'].show_tuxborn_installer_menu(self)
# HIDDEN FOR FIRST RELEASE - UNCOMMENT WHEN READY
# elif choice == "hoolamike":
# self.menus['hoolamike'].show_hoolamike_menu(self)

View File

@@ -4,7 +4,6 @@ Extracted from the legacy monolithic CLI system
"""
from .main_menu import MainMenuHandler
from .tuxborn_menu import TuxbornMenuHandler
from .wabbajack_menu import WabbajackMenuHandler
from .hoolamike_menu import HoolamikeMenuHandler
from .additional_menu import AdditionalMenuHandler
@@ -12,7 +11,6 @@ from .recovery_menu import RecoveryMenuHandler
__all__ = [
'MainMenuHandler',
'TuxbornMenuHandler',
'WabbajackMenuHandler',
'HoolamikeMenuHandler',
'AdditionalMenuHandler',

View File

@@ -1,194 +0,0 @@
"""
Tuxborn Menu Handler for Jackify CLI Frontend
Extracted from src.modules.menu_handler.MenuHandler.show_tuxborn_installer_menu()
"""
from pathlib import Path
from typing import Optional
from jackify.shared.colors import (
COLOR_SELECTION, COLOR_RESET, COLOR_INFO, COLOR_PROMPT, COLOR_WARNING
)
from jackify.shared.ui_utils import print_jackify_banner
from jackify.backend.handlers.config_handler import ConfigHandler
class TuxbornMenuHandler:
"""
Handles the Tuxborn Automatic Installer workflow
Extracted from legacy MenuHandler class
"""
def __init__(self):
self.logger = None # Will be set by CLI when needed
def show_tuxborn_installer_menu(self, cli_instance):
"""
Implements the Tuxborn Automatic Installer workflow.
Prompts for install path, downloads path, and Nexus API key, then runs the one-shot install from start to finish
Args:
cli_instance: Reference to main CLI instance for access to handlers
"""
# Import backend service
from jackify.backend.core.modlist_operations import ModlistInstallCLI
print_jackify_banner()
print(f"{COLOR_SELECTION}Tuxborn Automatic Installer{COLOR_RESET}")
print(f"{COLOR_SELECTION}{'-'*32}{COLOR_RESET}")
print(f"{COLOR_INFO}This will install the Tuxborn modlist using the custom Jackify Install Engine in one automated flow.{COLOR_RESET}")
print(f"{COLOR_INFO}You will be prompted for the install location, downloads directory, and your Nexus API key.{COLOR_RESET}\n")
tuxborn_machineid = "Tuxborn/Tuxborn"
tuxborn_modlist_name = "Tuxborn"
# Prompt for install directory
print("----------------------------")
config_handler = ConfigHandler()
base_install_dir = Path(config_handler.get_modlist_install_base_dir())
default_install_dir = base_install_dir / "Skyrim" / "Tuxborn"
print(f"{COLOR_PROMPT}Please enter the directory you wish to use for Tuxborn installation.{COLOR_RESET}")
print(f"(Default: {default_install_dir})")
install_dir_result = self._get_directory_path_legacy(
cli_instance,
prompt_message=f"{COLOR_PROMPT}Install directory (Enter for default, 'q' to cancel): {COLOR_RESET}",
default_path=default_install_dir,
create_if_missing=True,
no_header=True
)
if not install_dir_result:
print(f"{COLOR_INFO}Cancelled by user.{COLOR_RESET}")
input("Press Enter to return to the main menu...")
return
if isinstance(install_dir_result, tuple):
install_dir, _ = install_dir_result # We'll use the path, creation handled by engine or later
else:
install_dir = install_dir_result
# Prompt for download directory
print("----------------------------")
base_download_dir = Path(config_handler.get_modlist_downloads_base_dir())
default_download_dir = base_download_dir / "Tuxborn"
print(f"{COLOR_PROMPT}Please enter the directory you wish to use for Tuxborn downloads.{COLOR_RESET}")
print(f"(Default: {default_download_dir})")
download_dir_result = self._get_directory_path_legacy(
cli_instance,
prompt_message=f"{COLOR_PROMPT}Download directory (Enter for default, 'q' to cancel): {COLOR_RESET}",
default_path=default_download_dir,
create_if_missing=True,
no_header=True
)
if not download_dir_result:
print(f"{COLOR_INFO}Cancelled by user.{COLOR_RESET}")
input("Press Enter to return to the main menu...")
return
if isinstance(download_dir_result, tuple):
download_dir, _ = download_dir_result # We'll use the path, creation handled by engine or later
else:
download_dir = download_dir_result
# Prompt for Nexus API key
print("----------------------------")
from jackify.backend.services.api_key_service import APIKeyService
api_key_service = APIKeyService()
saved_key = api_key_service.get_saved_api_key()
api_key = None
if saved_key:
print(f"{COLOR_INFO}A Nexus API Key is already saved.{COLOR_RESET}")
use_saved = input(f"{COLOR_PROMPT}Use the saved API key? [Y/n]: {COLOR_RESET}").strip().lower()
if use_saved in ('', 'y', 'yes'):
api_key = saved_key
else:
new_key = input(f"{COLOR_PROMPT}Enter a new Nexus API Key (or press Enter to keep the saved one): {COLOR_RESET}").strip()
if new_key:
api_key = new_key
replace = input(f"{COLOR_PROMPT}Replace the saved key with this one? [y/N]: {COLOR_RESET}").strip().lower()
if replace == 'y':
if api_key_service.save_api_key(api_key):
print(f"{COLOR_INFO}API key saved successfully.{COLOR_RESET}")
else:
print(f"{COLOR_WARNING}Failed to save API key. Using for this session only.{COLOR_RESET}")
else:
print(f"{COLOR_INFO}Using new key for this session only. Saved key unchanged.{COLOR_RESET}")
else:
api_key = saved_key
else:
print(f"{COLOR_PROMPT}A Nexus Mods API key is required for downloading mods.{COLOR_RESET}")
print(f"{COLOR_INFO}You can get your personal key at: {COLOR_SELECTION}https://www.nexusmods.com/users/myaccount?tab=api{COLOR_RESET}")
print(f"{COLOR_WARNING}Your API Key is NOT saved locally. It is used only for this session unless you choose to save it.{COLOR_RESET}")
api_key = input(f"{COLOR_PROMPT}Enter Nexus API Key (or 'q' to cancel): {COLOR_RESET}").strip()
if not api_key or api_key.lower() == 'q':
print(f"{COLOR_INFO}Cancelled by user.{COLOR_RESET}")
input("Press Enter to return to the main menu...")
return
save = input(f"{COLOR_PROMPT}Would you like to save this API key for future use? [y/N]: {COLOR_RESET}").strip().lower()
if save == 'y':
if api_key_service.save_api_key(api_key):
print(f"{COLOR_INFO}API key saved successfully.{COLOR_RESET}")
else:
print(f"{COLOR_WARNING}Failed to save API key. Using for this session only.{COLOR_RESET}")
else:
print(f"{COLOR_INFO}Using API key for this session only. It will not be saved.{COLOR_RESET}")
# Context for ModlistInstallCLI
context = {
'machineid': tuxborn_machineid,
'modlist_name': tuxborn_modlist_name, # Will be used for shortcut name
'install_dir': install_dir_result, # Pass tuple (path, create_flag) or path
'download_dir': download_dir_result, # Pass tuple (path, create_flag) or path
'nexus_api_key': api_key,
'resolution': None
}
modlist_cli = ModlistInstallCLI(self, getattr(cli_instance, 'steamdeck', False))
# run_discovery_phase will use context_override, display summary, and ask for confirmation.
# If user confirms, it returns the context, otherwise None.
confirmed_context = modlist_cli.run_discovery_phase(context_override=context)
if confirmed_context:
if self.logger:
self.logger.info("Tuxborn discovery confirmed by user. Proceeding to configuration/installation.")
# The modlist_cli instance now holds the confirmed context.
# configuration_phase will use modlist_cli.context
modlist_cli.configuration_phase()
# After configuration_phase, messages about success or next steps are handled within it or by _configure_new_modlist
else:
if self.logger:
self.logger.info("Tuxborn discovery/confirmation cancelled or failed.")
print(f"{COLOR_INFO}Tuxborn installation cancelled or not confirmed.{COLOR_RESET}")
input(f"{COLOR_PROMPT}Press Enter to return to the main menu...{COLOR_RESET}")
return
def _get_directory_path_legacy(self, cli_instance, prompt_message: str, default_path: Optional[Path],
create_if_missing: bool = True, no_header: bool = False) -> Optional[Path]:
"""
LEGACY BRIDGE: Delegate to legacy menu handler until full backend migration
Args:
cli_instance: Reference to main CLI instance
prompt_message: The prompt to show user
default_path: Default path if user presses Enter
create_if_missing: Whether to create directory if it doesn't exist
no_header: Whether to skip header display
Returns:
Path object or None if cancelled
"""
# LEGACY BRIDGE: Use the original menu handler's method
if hasattr(cli_instance, 'menu') and hasattr(cli_instance.menu, 'get_directory_path'):
return cli_instance.menu.get_directory_path(
prompt_message=prompt_message,
default_path=default_path,
create_if_missing=create_if_missing,
no_header=no_header
)
else:
# Fallback: simple input for now (will be replaced in future phases)
response = input(prompt_message).strip()
if response.lower() == 'q':
return None
elif response == '':
return default_path
else:
return Path(response)

View File

@@ -131,7 +131,6 @@ DISCLAIMER_TEXT = (
MENU_ITEMS = [
("Modlist Tasks", "modlist_tasks"),
("Tuxborn Automatic Installer", "tuxborn_installer"),
("Hoolamike Tasks", "hoolamike_tasks"),
("Additional Tasks", "additional_tasks"),
("Exit Jackify", "exit_jackify"),
@@ -162,6 +161,8 @@ class SettingsDialog(QDialog):
try:
super().__init__(parent)
from jackify.backend.handlers.config_handler import ConfigHandler
import logging
self.logger = logging.getLogger(__name__)
self.config_handler = ConfigHandler()
self._original_debug_mode = self.config_handler.get('debug_mode', False)
self.setWindowTitle("Settings")
@@ -627,7 +628,18 @@ class SettingsDialog(QDialog):
self.config_handler.set("proton_path", resolved_path)
self.config_handler.set("proton_version", resolved_version)
self.config_handler.save_config()
# Force immediate save and verify
save_result = self.config_handler.save_config()
if not save_result:
self.logger.error("Failed to save Proton configuration")
else:
self.logger.info(f"Saved Proton config: path={resolved_path}, version={resolved_version}")
# Verify the save worked by reading it back
saved_path = self.config_handler.get("proton_path")
if saved_path != resolved_path:
self.logger.error(f"Config save verification failed: expected {resolved_path}, got {saved_path}")
else:
self.logger.debug("Config save verified successfully")
# Refresh cached paths in GUI screens if Jackify directory changed
self._refresh_gui_paths()
@@ -664,7 +676,6 @@ class SettingsDialog(QDialog):
getattr(main_window, 'install_modlist_screen', None),
getattr(main_window, 'configure_new_modlist_screen', None),
getattr(main_window, 'configure_existing_modlist_screen', None),
getattr(main_window, 'tuxborn_screen', None),
]
for screen in screens_to_refresh:
@@ -773,7 +784,7 @@ class JackifyMainWindow(QMainWindow):
# Create screens using refactored codebase
from jackify.frontends.gui.screens import (
MainMenu, TuxbornInstallerScreen, ModlistTasksScreen,
MainMenu, ModlistTasksScreen,
InstallModlistScreen, ConfigureNewModlistScreen, ConfigureExistingModlistScreen
)
@@ -785,31 +796,26 @@ class JackifyMainWindow(QMainWindow):
main_menu_index=0,
dev_mode=dev_mode
)
self.tuxborn_screen = TuxbornInstallerScreen(
stacked_widget=self.stacked_widget,
self.install_modlist_screen = InstallModlistScreen(
stacked_widget=self.stacked_widget,
main_menu_index=0
)
self.install_modlist_screen = InstallModlistScreen(
stacked_widget=self.stacked_widget,
main_menu_index=3
)
self.configure_new_modlist_screen = ConfigureNewModlistScreen(
stacked_widget=self.stacked_widget,
main_menu_index=3
stacked_widget=self.stacked_widget,
main_menu_index=0
)
self.configure_existing_modlist_screen = ConfigureExistingModlistScreen(
stacked_widget=self.stacked_widget,
main_menu_index=3
stacked_widget=self.stacked_widget,
main_menu_index=0
)
# Add screens to stacked widget
self.stacked_widget.addWidget(self.main_menu) # Index 0: Main Menu
self.stacked_widget.addWidget(self.tuxborn_screen) # Index 1: Tuxborn Installer
self.stacked_widget.addWidget(self.feature_placeholder) # Index 2: Placeholder
self.stacked_widget.addWidget(self.modlist_tasks_screen) # Index 3: Modlist Tasks
self.stacked_widget.addWidget(self.install_modlist_screen) # Index 4: Install Modlist
self.stacked_widget.addWidget(self.configure_new_modlist_screen) # Index 5: Configure New
self.stacked_widget.addWidget(self.configure_existing_modlist_screen) # Index 6: Configure Existing
self.stacked_widget.addWidget(self.feature_placeholder) # Index 1: Placeholder
self.stacked_widget.addWidget(self.modlist_tasks_screen) # Index 2: Modlist Tasks
self.stacked_widget.addWidget(self.install_modlist_screen) # Index 3: Install Modlist
self.stacked_widget.addWidget(self.configure_new_modlist_screen) # Index 4: Configure New
self.stacked_widget.addWidget(self.configure_existing_modlist_screen) # Index 5: Configure Existing
# Add debug tracking for screen changes
self.stacked_widget.currentChanged.connect(self._debug_screen_change)
@@ -887,12 +893,11 @@ class JackifyMainWindow(QMainWindow):
screen_names = {
0: "Main Menu",
1: "Tuxborn Installer",
2: "Feature Placeholder",
3: "Modlist Tasks Menu",
4: "Install Modlist Screen",
5: "Configure New Modlist",
6: "Configure Existing Modlist"
1: "Feature Placeholder",
2: "Modlist Tasks Menu",
3: "Install Modlist Screen",
4: "Configure New Modlist",
5: "Configure Existing Modlist"
}
screen_name = screen_names.get(index, f"Unknown Screen (Index {index})")
widget = self.stacked_widget.widget(index)
@@ -1002,7 +1007,7 @@ class JackifyMainWindow(QMainWindow):
# Clean up screen processes
screens = [
self.modlist_tasks_screen, self.tuxborn_screen, self.install_modlist_screen,
self.modlist_tasks_screen, self.install_modlist_screen,
self.configure_new_modlist_screen, self.configure_existing_modlist_screen
]
for screen in screens:
@@ -1072,7 +1077,18 @@ def main():
# Command-line --debug always takes precedence
if '--debug' in sys.argv or '-d' in sys.argv:
debug_mode = True
# Temporarily save CLI debug flag to config so engine can see it
config_handler.set('debug_mode', True)
print("[DEBUG] CLI --debug flag detected, saved debug_mode=True to config")
import logging
# Initialize file logging on root logger so all modules inherit it
from jackify.shared.logging import LoggingHandler
logging_handler = LoggingHandler()
# Rotate log file before setting up new logger
logging_handler.rotate_log_for_logger('jackify_gui', 'jackify-gui.log')
root_logger = logging_handler.setup_logger('', 'jackify-gui.log', is_general=True) # Empty name = root logger
if debug_mode:
logging.getLogger().setLevel(logging.DEBUG)
print("[Jackify] Debug mode enabled (from config or CLI)")

View File

@@ -5,7 +5,6 @@ Contains all the GUI screen components for Jackify.
"""
from .main_menu import MainMenu
from .tuxborn_installer import TuxbornInstallerScreen
from .modlist_tasks import ModlistTasksScreen
from .install_modlist import InstallModlistScreen
from .configure_new_modlist import ConfigureNewModlistScreen
@@ -13,7 +12,6 @@ from .configure_existing_modlist import ConfigureExistingModlistScreen
__all__ = [
'MainMenu',
'TuxbornInstallerScreen',
'ModlistTasksScreen',
'InstallModlistScreen',
'ConfigureNewModlistScreen',

View File

@@ -119,7 +119,7 @@ class ConfigureExistingModlistScreen(QWidget):
self.shortcut_combo.addItem("Please Select...")
self.shortcut_map = []
for shortcut in self.mo2_shortcuts:
display = f"{shortcut.get('AppName', 'Unknown')} ({shortcut.get('StartDir', '')})"
display = f"{shortcut.get('AppName', shortcut.get('appname', 'Unknown'))} ({shortcut.get('StartDir', shortcut.get('startdir', ''))})"
self.shortcut_combo.addItem(display)
self.shortcut_map.append(shortcut)
@@ -427,8 +427,8 @@ class ConfigureExistingModlistScreen(QWidget):
self._enable_controls_after_operation()
return
shortcut = self.shortcut_map[idx]
modlist_name = shortcut.get('AppName', '')
install_dir = shortcut.get('StartDir', '')
modlist_name = shortcut.get('AppName', shortcut.get('appname', ''))
install_dir = shortcut.get('StartDir', shortcut.get('startdir', ''))
if not modlist_name or not install_dir:
MessageService.critical(self, "Invalid Shortcut", "The selected shortcut is missing required information.", safety_level="medium")
self._enable_controls_after_operation()
@@ -710,7 +710,7 @@ class ConfigureExistingModlistScreen(QWidget):
self.shortcut_map.clear()
for shortcut in self.mo2_shortcuts:
display = f"{shortcut.get('AppName', 'Unknown')} ({shortcut.get('StartDir', '')})"
display = f"{shortcut.get('AppName', shortcut.get('appname', 'Unknown'))} ({shortcut.get('StartDir', shortcut.get('startdir', ''))})"
self.shortcut_combo.addItem(display)
self.shortcut_map.append(shortcut)

View File

@@ -481,7 +481,7 @@ class ConfigureNewModlistScreen(QWidget):
def go_back(self):
if self.stacked_widget:
self.stacked_widget.setCurrentIndex(3) # Return to Modlist Tasks menu
self.stacked_widget.setCurrentIndex(self.main_menu_index)
def update_top_panel(self):
try:

View File

@@ -1057,7 +1057,7 @@ class InstallModlistScreen(QWidget):
def go_back(self):
if self.stacked_widget:
self.stacked_widget.setCurrentIndex(3) # Return to Modlist Tasks menu
self.stacked_widget.setCurrentIndex(self.main_menu_index)
def update_top_panel(self):
try:
@@ -1746,7 +1746,7 @@ class InstallModlistScreen(QWidget):
# Save resolution for later use in configuration
resolution = self.resolution_combo.currentText()
self._current_resolution = resolution.split()[0] if resolution != "Leave unchanged" else "2560x1600"
self._current_resolution = resolution.split()[0] if resolution != "Leave unchanged" else None
# Use automated prefix creation instead of manual steps
debug_print("DEBUG: Starting automated prefix creation workflow")
@@ -2321,7 +2321,7 @@ class InstallModlistScreen(QWidget):
'mo2_exe_path': self._get_mo2_path(install_dir, modlist_name),
'modlist_value': None,
'modlist_source': None,
'resolution': getattr(self, '_current_resolution', '2560x1600'),
'resolution': getattr(self, '_current_resolution', None),
'skip_confirmation': True,
'manual_steps_completed': True, # Mark as completed since automated prefix is done
'appid': new_appid, # Use the NEW AppID from automated prefix creation
@@ -2360,7 +2360,7 @@ class InstallModlistScreen(QWidget):
nexus_api_key='', # Not needed for configuration
modlist_value=self.context.get('modlist_value'),
modlist_source=self.context.get('modlist_source', 'identifier'),
resolution=self.context.get('resolution', '2560x1600'),
resolution=self.context.get('resolution'),
skip_confirmation=True,
engine_installed=True # Skip path manipulation for engine workflows
)
@@ -2419,7 +2419,7 @@ class InstallModlistScreen(QWidget):
'mo2_exe_path': self._get_mo2_path(install_dir, modlist_name),
'modlist_value': None,
'modlist_source': None,
'resolution': getattr(self, '_current_resolution', '2560x1600'),
'resolution': getattr(self, '_current_resolution', None),
'skip_confirmation': True,
'manual_steps_completed': True, # Mark as completed
'appid': new_appid # Use the NEW AppID from Steam

View File

@@ -120,7 +120,7 @@ class MainMenu(QWidget):
msg.setIcon(QMessageBox.Information)
msg.exec()
elif action_id == "modlist_tasks" and self.stacked_widget:
self.stacked_widget.setCurrentIndex(3)
self.stacked_widget.setCurrentIndex(2)
elif action_id == "return_main_menu":
# This is the main menu, so do nothing
pass

View File

@@ -198,11 +198,11 @@ class ModlistTasksScreen(QWidget):
if action_id == "return_main_menu":
self.stacked_widget.setCurrentIndex(0)
elif action_id == "install_modlist":
self.stacked_widget.setCurrentIndex(4)
self.stacked_widget.setCurrentIndex(3)
elif action_id == "configure_new_modlist":
self.stacked_widget.setCurrentIndex(5)
self.stacked_widget.setCurrentIndex(4)
elif action_id == "configure_existing_modlist":
self.stacked_widget.setCurrentIndex(6)
self.stacked_widget.setCurrentIndex(5)
def go_back(self):
"""Return to main menu"""

File diff suppressed because it is too large Load Diff