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._last_backup_hash: str = ""
|
||||||
self._terminal_font_size: int = 11
|
self._terminal_font_size: int = 11
|
||||||
self._window_geometry: str = ""
|
self._window_geometry: str = ""
|
||||||
|
self._sidebar_width: int = 250
|
||||||
self._servers_file: str = DEFAULT_SERVERS_FILE
|
self._servers_file: str = DEFAULT_SERVERS_FILE
|
||||||
# Update settings
|
# Update settings
|
||||||
self._update_mode: str = "auto-download" # "notify-only" | "auto-download" | "full-auto"
|
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._check_interval = settings.get("check_interval", 60)
|
||||||
self._terminal_font_size = settings.get("terminal_font_size", 11)
|
self._terminal_font_size = settings.get("terminal_font_size", 11)
|
||||||
self._window_geometry = settings.get("window_geometry", "")
|
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._update_mode = settings.get("update_mode", "auto-download")
|
||||||
self._last_update_check = settings.get("last_update_check", 0)
|
self._last_update_check = settings.get("last_update_check", 0)
|
||||||
self._skip_version = settings.get("skip_version", "")
|
self._skip_version = settings.get("skip_version", "")
|
||||||
@@ -100,6 +102,7 @@ class ServerStore:
|
|||||||
"check_interval": self._check_interval,
|
"check_interval": self._check_interval,
|
||||||
"terminal_font_size": self._terminal_font_size,
|
"terminal_font_size": self._terminal_font_size,
|
||||||
"window_geometry": self._window_geometry,
|
"window_geometry": self._window_geometry,
|
||||||
|
"sidebar_width": self._sidebar_width,
|
||||||
"update_mode": self._update_mode,
|
"update_mode": self._update_mode,
|
||||||
"last_update_check": self._last_update_check,
|
"last_update_check": self._last_update_check,
|
||||||
"skip_version": self._skip_version,
|
"skip_version": self._skip_version,
|
||||||
@@ -394,6 +397,89 @@ class ServerStore:
|
|||||||
self._save()
|
self._save()
|
||||||
self._notify()
|
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:
|
def get_ssh_key_path(self) -> str:
|
||||||
path = self._data.get("ssh_key", {}).get("path", "~/.ssh/id_ed25519")
|
path = self._data.get("ssh_key", {}).get("path", "~/.ssh/id_ed25519")
|
||||||
return os.path.expanduser(path)
|
return os.path.expanduser(path)
|
||||||
|
|||||||
11
gui/app.py
11
gui/app.py
@@ -125,7 +125,7 @@ class App(ctk.CTk):
|
|||||||
|
|
||||||
# Sidebar
|
# Sidebar
|
||||||
self.sidebar = Sidebar(self._paned, self.store, on_select=self._on_server_select, session_pool=self.session_pool)
|
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.add_callback = self._add_server
|
||||||
self.sidebar.edit_callback = self._edit_server
|
self.sidebar.edit_callback = self._edit_server
|
||||||
self.sidebar.delete_callback = self._delete_server
|
self.sidebar.delete_callback = self._delete_server
|
||||||
@@ -647,9 +647,16 @@ class App(ctk.CTk):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def _on_close(self):
|
def _on_close(self):
|
||||||
# Save window geometry (size + position)
|
# Save window geometry (size + position) and sidebar width
|
||||||
try:
|
try:
|
||||||
self.store._window_geometry = self.geometry()
|
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()
|
self.store._save_settings()
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
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 info for ServerManager."""
|
||||||
|
|
||||||
__version__ = "1.8.95"
|
__version__ = "1.8.96"
|
||||||
__app_name__ = "ServerManager"
|
__app_name__ = "ServerManager"
|
||||||
__author__ = "aibot777"
|
__author__ = "aibot777"
|
||||||
__description__ = "Desktop GUI for managing remote servers"
|
__description__ = "Desktop GUI for managing remote servers"
|
||||||
|
|||||||
Reference in New Issue
Block a user