v1.8.96: persist sidebar width across restarts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -58,6 +58,7 @@ class ServerStore:
|
||||
self._last_backup_hash: str = ""
|
||||
self._terminal_font_size: int = 11
|
||||
self._window_geometry: str = ""
|
||||
self._sidebar_width: int = 250
|
||||
self._servers_file: str = DEFAULT_SERVERS_FILE
|
||||
# Update settings
|
||||
self._update_mode: str = "auto-download" # "notify-only" | "auto-download" | "full-auto"
|
||||
@@ -83,6 +84,7 @@ class ServerStore:
|
||||
self._check_interval = settings.get("check_interval", 60)
|
||||
self._terminal_font_size = settings.get("terminal_font_size", 11)
|
||||
self._window_geometry = settings.get("window_geometry", "")
|
||||
self._sidebar_width = settings.get("sidebar_width", 250)
|
||||
self._update_mode = settings.get("update_mode", "auto-download")
|
||||
self._last_update_check = settings.get("last_update_check", 0)
|
||||
self._skip_version = settings.get("skip_version", "")
|
||||
@@ -100,6 +102,7 @@ class ServerStore:
|
||||
"check_interval": self._check_interval,
|
||||
"terminal_font_size": self._terminal_font_size,
|
||||
"window_geometry": self._window_geometry,
|
||||
"sidebar_width": self._sidebar_width,
|
||||
"update_mode": self._update_mode,
|
||||
"last_update_check": self._last_update_check,
|
||||
"skip_version": self._skip_version,
|
||||
@@ -394,6 +397,89 @@ class ServerStore:
|
||||
self._save()
|
||||
self._notify()
|
||||
|
||||
# ── Groups CRUD ─────────────────────────────────
|
||||
|
||||
def get_groups(self) -> list[dict]:
|
||||
"""Return all groups sorted by order."""
|
||||
groups = list(self._data.get("groups", []))
|
||||
groups.sort(key=lambda g: g.get("order", 0))
|
||||
return groups
|
||||
|
||||
def get_group(self, group_id: str) -> Optional[dict]:
|
||||
for g in self._data.get("groups", []):
|
||||
if g["id"] == group_id:
|
||||
return dict(g)
|
||||
return None
|
||||
|
||||
def add_group(self, name: str, color: str = "#6b7280") -> dict:
|
||||
"""Create a new group, return the created dict."""
|
||||
import uuid
|
||||
groups = self._data.setdefault("groups", [])
|
||||
max_order = max((g.get("order", 0) for g in groups), default=-1)
|
||||
group = {
|
||||
"id": uuid.uuid4().hex[:8],
|
||||
"name": name,
|
||||
"color": color,
|
||||
"collapsed": False,
|
||||
"order": max_order + 1,
|
||||
}
|
||||
groups.append(group)
|
||||
self._save()
|
||||
self._notify()
|
||||
return group
|
||||
|
||||
def update_group(self, group_id: str, **kwargs):
|
||||
"""Update group fields (name, color, collapsed, order)."""
|
||||
for g in self._data.get("groups", []):
|
||||
if g["id"] == group_id:
|
||||
for k, v in kwargs.items():
|
||||
if k in ("name", "color", "collapsed", "order"):
|
||||
g[k] = v
|
||||
self._save()
|
||||
self._notify()
|
||||
return
|
||||
raise ValueError(f"Group '{group_id}' not found")
|
||||
|
||||
def remove_group(self, group_id: str):
|
||||
"""Delete group. Servers in it become ungrouped."""
|
||||
self._data["groups"] = [g for g in self._data.get("groups", []) if g["id"] != group_id]
|
||||
for s in self._data.get("servers", []):
|
||||
if s.get("group") == group_id:
|
||||
s.pop("group", None)
|
||||
self._save()
|
||||
self._notify()
|
||||
|
||||
def reorder_groups(self, ordered_ids: list[str]):
|
||||
"""Set group order based on list of IDs."""
|
||||
id_to_order = {gid: i for i, gid in enumerate(ordered_ids)}
|
||||
for g in self._data.get("groups", []):
|
||||
if g["id"] in id_to_order:
|
||||
g["order"] = id_to_order[g["id"]]
|
||||
self._save()
|
||||
self._notify()
|
||||
|
||||
def set_server_group(self, alias: str, group_id: Optional[str]):
|
||||
"""Move a server to a group (or None to ungroup)."""
|
||||
for s in self._data.get("servers", []):
|
||||
if s["alias"] == alias:
|
||||
if group_id:
|
||||
s["group"] = group_id
|
||||
else:
|
||||
s.pop("group", None)
|
||||
self._save()
|
||||
self._notify()
|
||||
return
|
||||
raise ValueError(f"Server '{alias}' not found")
|
||||
|
||||
def get_servers_in_group(self, group_id: Optional[str]) -> list[dict]:
|
||||
"""Return servers in a group. None = ungrouped."""
|
||||
all_servers = self._data.get("servers", [])
|
||||
group_ids = {g["id"] for g in self._data.get("groups", [])}
|
||||
if group_id is None:
|
||||
return [s for s in all_servers
|
||||
if not s.get("group") or s.get("group") not in group_ids]
|
||||
return [s for s in all_servers if s.get("group") == group_id]
|
||||
|
||||
def get_ssh_key_path(self) -> str:
|
||||
path = self._data.get("ssh_key", {}).get("path", "~/.ssh/id_ed25519")
|
||||
return os.path.expanduser(path)
|
||||
|
||||
11
gui/app.py
11
gui/app.py
@@ -125,7 +125,7 @@ class App(ctk.CTk):
|
||||
|
||||
# Sidebar
|
||||
self.sidebar = Sidebar(self._paned, self.store, on_select=self._on_server_select, session_pool=self.session_pool)
|
||||
self._paned.add(self.sidebar, minsize=180, width=250)
|
||||
self._paned.add(self.sidebar, minsize=180, width=self.store._sidebar_width)
|
||||
self.sidebar.add_callback = self._add_server
|
||||
self.sidebar.edit_callback = self._edit_server
|
||||
self.sidebar.delete_callback = self._delete_server
|
||||
@@ -647,9 +647,16 @@ class App(ctk.CTk):
|
||||
pass
|
||||
|
||||
def _on_close(self):
|
||||
# Save window geometry (size + position)
|
||||
# Save window geometry (size + position) and sidebar width
|
||||
try:
|
||||
self.store._window_geometry = self.geometry()
|
||||
# Save sidebar width from PanedWindow sash position
|
||||
try:
|
||||
sash_pos = self._paned.sash_coord(0)
|
||||
if sash_pos:
|
||||
self.store._sidebar_width = sash_pos[0]
|
||||
except Exception:
|
||||
pass
|
||||
self.store._save_settings()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
BIN
releases/ServerManager-v1.8.96-win-x64.exe
Normal file
BIN
releases/ServerManager-v1.8.96-win-x64.exe
Normal file
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
"""Version info for ServerManager."""
|
||||
|
||||
__version__ = "1.8.95"
|
||||
__version__ = "1.8.96"
|
||||
__app_name__ = "ServerManager"
|
||||
__author__ = "aibot777"
|
||||
__description__ = "Desktop GUI for managing remote servers"
|
||||
|
||||
Reference in New Issue
Block a user