Merge branch 'main' into feat/75-theming
CI / Lint (ruff) (pull_request) Successful in 7s
CI / Tests (py3.10 / ubuntu-latest) (pull_request) Successful in 11s
CI / Tests (py3.12 / ubuntu-latest) (pull_request) Successful in 10s
CI / Tests (py3.13 / ubuntu-latest) (pull_request) Successful in 10s
CI / Catalog signature (pull_request) Successful in 7s
CI / Tests (py3.12 / windows-latest) (pull_request) Has been cancelled

Union conflict at the end of tests/test_core.py -- both branches appended a
test block. Kept both, main's #72/#73 block first. Verified: 265 test
functions = 238 baseline + 15 (#77) + 12 (theming), no duplicates.
This commit is contained in:
2026-07-20 12:50:09 -04:00
3 changed files with 283 additions and 10 deletions
+26 -4
View File
@@ -2010,9 +2010,21 @@ class MainWindow(QMainWindow):
return
self.full_config = cfg
repaired = True
# extract_servers tolerates malformed entries rather than raising (#72),
# but keep it inside the guard: a load failure must leave the previously
# loaded profile intact instead of half-swapping the window's state.
try:
servers = core.extract_servers(self.full_config)
except Exception as exc: # pragma: no cover - defence in depth
QMessageBox.critical(
self,
"Could not read config",
f"{profile.path}\n\nThe server list couldn't be read: {exc}",
)
return
self._loaded_stat = core.config_fingerprint(profile.path)
self.current_profile = profile
self.servers = core.extract_servers(self.full_config)
self.servers = servers
self.dirty = False
self.restart_btn.hide()
self._undo_stack.clear()
@@ -2335,7 +2347,7 @@ class MainWindow(QMainWindow):
entry = self.servers[idx]
old_name = entry.name
entry.name = self.editor.current_name()
entry.data = self.editor.dump_data()
entry.set_data(self.editor.dump_data())
# The server stays in its section (enable state unchanged), so update
# its existing row in place rather than re-rendering.
# An edit invalidates any cached "Test all" result -- the server that
@@ -2429,7 +2441,7 @@ class MainWindow(QMainWindow):
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
)
if ans == QMessageBox.StandardButton.Yes:
self.servers[existing[name]].data = data
self.servers[existing[name]].set_data(data)
return False, True
name = core.resolve_name_collision(name, {s.name for s in self.servers})
self.servers.append(core.ServerEntry(name, data, True))
@@ -2549,6 +2561,11 @@ class MainWindow(QMainWindow):
except Exception as e:
QMessageBox.critical(self, "Reload failed", str(e))
return
# The reload above is the on-disk truth for everything the user
# didn't touch -- but it also wipes BCC-authored keys the user
# changed in this session (named sets), which apply_servers
# doesn't write. Carry them over before saving (#73).
contested = core.carry_owned_keys(self.full_config, fresh)
core.apply_servers(fresh, self.servers)
try:
backup = core.write_config(self.current_profile.path, fresh)
@@ -2561,8 +2578,13 @@ class MainWindow(QMainWindow):
self.dirty = False
self.save_btn.setEnabled(False)
bnote = f" · backup: {backup.name}" if backup else " · (new file)"
cnote = (
f" · kept your {', '.join(contested)} (the file on disk had a different copy)"
if contested
else ""
)
self.status.setText(
f"Merged & saved {self.current_profile.path}{bnote}"
f"Merged & saved {self.current_profile.path}{bnote}{cnote}"
f" · Restart {self.current_profile.label} to apply."
)
self._offer_restart_button()