diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5a1efc0..d026bb5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,16 +1,31 @@
# Jackify Changelog
+## v0.2.0.7 - Critical Auth Fix
+**Release Date:** 2025-12-28
+
+### Critical Bug Fixes
+- **OAuth Token Loss**: Fixed version comparison bug that was deleting OAuth tokens every time settings were saved (affects users on v0.2.0.4+)
+- Fixed internal import paths for improved stability
+
+---
+
## v0.2.0.6 - Premium Detection and Engine Update
**Release Date:** 2025-12-28
+**IMPORTANT:** If you are on v0.2.0.5, automatic updates will not work. You must manually download and install v0.2.0.6.
+
### Engine Updates
- **jackify-engine 0.4.4**: Latest engine version with improvements
### Critical Bug Fixes
+- **Auto-Update System**: Fixed broken update dialog import that prevented automatic updates
- **Premium Detection**: Fixed false Premium errors caused by overly-broad detection pattern triggering on jackify-engine 0.4.3's userinfo JSON output
- **Custom Data Directory**: Fixed AppImage always creating ~/Jackify on startup, even when user configured a custom jackify_data_dir
- **Proton Auto-Selection**: Fixed auto-selection writing invalid "auto" string to config on detection failure
+### Quality Improvements
+- Added pre-build import validator to prevent broken imports from reaching production
+
---
## v0.2.0.5 - Emergency OAuth Fix
diff --git a/jackify/__init__.py b/jackify/__init__.py
index 20fe8c0..c8e98d5 100644
--- a/jackify/__init__.py
+++ b/jackify/__init__.py
@@ -5,4 +5,4 @@ This package provides both CLI and GUI interfaces for managing
Wabbajack modlists natively on Linux systems.
"""
-__version__ = "0.2.0.6"
+__version__ = "0.2.0.7"
diff --git a/jackify/backend/handlers/config_handler.py b/jackify/backend/handlers/config_handler.py
index 376642a..f2764f3 100644
--- a/jackify/backend/handlers/config_handler.py
+++ b/jackify/backend/handlers/config_handler.py
@@ -157,7 +157,8 @@ class ConfigHandler:
# Migration: v0.0.x -> v0.2.0
# Encryption changed from cryptography (Fernet) to pycryptodome (AES-GCM)
# Old encrypted API keys cannot be decrypted, must be re-entered
- if current_version < "0.2.0":
+ from packaging import version
+ if version.parse(current_version) < version.parse("0.2.0"):
# Clear old encrypted credentials
if self.settings.get("nexus_api_key"):
logger.warning("Clearing saved API key due to encryption format change")
diff --git a/jackify/backend/handlers/shortcut_handler.py b/jackify/backend/handlers/shortcut_handler.py
index f3f3819..4ebfe51 100644
--- a/jackify/backend/handlers/shortcut_handler.py
+++ b/jackify/backend/handlers/shortcut_handler.py
@@ -205,8 +205,8 @@ class ShortcutHandler:
time.sleep(1) # Give some time for the install to complete
# Now import it
- import steam_vdf
-
+ import vdf as steam_vdf
+
with open(shortcuts_file, 'rb') as f:
shortcuts_data = steam_vdf.load(f)
diff --git a/jackify/frontends/gui/main.py b/jackify/frontends/gui/main.py
index 9bb53d4..8de945b 100644
--- a/jackify/frontends/gui/main.py
+++ b/jackify/frontends/gui/main.py
@@ -1562,7 +1562,7 @@ class JackifyMainWindow(QMainWindow):
# Show update dialog after a short delay to ensure GUI is fully loaded
def show_update_dialog():
- from ..dialogs.update_dialog import UpdateDialog
+ from .dialogs.update_dialog import UpdateDialog
dialog = UpdateDialog(update_info, self.update_service, self)
dialog.exec()
diff --git a/jackify/frontends/gui/screens/install_modlist.py b/jackify/frontends/gui/screens/install_modlist.py
index 46eb6a9..1bc858a 100644
--- a/jackify/frontends/gui/screens/install_modlist.py
+++ b/jackify/frontends/gui/screens/install_modlist.py
@@ -4358,7 +4358,7 @@ class InstallModlistScreen(QWidget):
def _show_somnium_post_install_guidance(self):
"""Show guidance popup for Somnium post-installation steps"""
- from ..widgets.message_service import MessageService
+ from ..services.message_service import MessageService
guidance_text = f"""Somnium Post-Installation Required
Due to Somnium's non-standard folder structure, you need to manually update the binary paths in ModOrganizer:
diff --git a/jackify/frontends/gui/screens/install_ttw.py b/jackify/frontends/gui/screens/install_ttw.py
index 82da3ef..0a3b12a 100644
--- a/jackify/frontends/gui/screens/install_ttw.py
+++ b/jackify/frontends/gui/screens/install_ttw.py
@@ -3494,7 +3494,7 @@ class InstallTTWScreen(QWidget):
def _show_somnium_post_install_guidance(self):
"""Show guidance popup for Somnium post-installation steps"""
- from ..widgets.message_service import MessageService
+ from ..services.message_service import MessageService
guidance_text = f"""Somnium Post-Installation Required
Due to Somnium's non-standard folder structure, you need to manually update the binary paths in ModOrganizer:
diff --git a/jackify/frontends/gui/screens/modlist_gallery.py b/jackify/frontends/gui/screens/modlist_gallery.py
index 57e5533..7901446 100644
--- a/jackify/frontends/gui/screens/modlist_gallery.py
+++ b/jackify/frontends/gui/screens/modlist_gallery.py
@@ -1509,17 +1509,18 @@ class ModlistGalleryDialog(QDialog):
try:
# Remove all cards from layout
- # CRITICAL FIX: Properly remove widgets to prevent overlapping and orphaned windows
- # We need to explicitly remove widgets from the layout before taking items
- # to ensure they're fully cleaned up, but we don't setParent(None) because
- # widgets are immediately re-added to the grid (Qt will reparent them).
- while self.grid_layout.count():
- item = self.grid_layout.takeAt(0)
+ # CRITICAL FIX: Properly remove all widgets to prevent overlapping
+ # Iterate backwards to avoid index shifting issues
+ for i in range(self.grid_layout.count() - 1, -1, -1):
+ item = self.grid_layout.takeAt(i)
widget = item.widget() if item else None
if widget:
- # Explicitly remove widget from layout to prevent overlapping
- self.grid_layout.removeWidget(widget)
+ # Hide widget during removal to prevent visual artifacts
+ widget.hide()
del item
+
+ # Force layout update to ensure all widgets are removed
+ self.grid_layout.update()
# Calculate number of columns based on available width
# Get the scroll area width (accounting for filter panel ~280px + margins)
@@ -1558,16 +1559,20 @@ class ModlistGalleryDialog(QDialog):
card = self.all_cards.get(modlist.machineURL)
if card:
- # Ensure widget is not already in the layout (prevent overlapping)
- # If it is, remove it first (shouldn't happen after takeAt, but safety check)
- if card.parent() == self.grid_widget:
- # Widget is already a child of grid_widget, check if it's in layout
- for i in range(self.grid_layout.count()):
- item = self.grid_layout.itemAt(i)
- if item and item.widget() == card:
- # Already in layout, remove it first
- self.grid_layout.removeWidget(card)
- break
+ # Safety check: ensure widget is not already in the layout
+ # (shouldn't happen after proper removal above, but defensive programming)
+ already_in_layout = False
+ for i in range(self.grid_layout.count()):
+ item = self.grid_layout.itemAt(i)
+ if item and item.widget() == card:
+ # Widget is already in layout - this shouldn't happen, but handle it
+ already_in_layout = True
+ self.grid_layout.removeWidget(card)
+ break
+
+ # Ensure widget is visible and add to grid
+ if not already_in_layout or card.isHidden():
+ card.show()
self.grid_layout.addWidget(card, row, col)
# Set column stretch - don't stretch card columns, but add a spacer column