fix: editable alias in server dialog + Ctrl+Z undo for all input fields
- Alias field no longer disabled when editing server profile - Duplicate alias check on rename, session pool migration - Enable undo (Ctrl+Z) on all CTkEntry widgets across the project Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
10
gui/app.py
10
gui/app.py
@@ -129,7 +129,15 @@ class App(ctk.CTk):
|
||||
if server:
|
||||
dialog = ServerDialog(self, self.store, server=server)
|
||||
self.wait_window(dialog)
|
||||
self.info_tab.refresh()
|
||||
# If alias was changed, re-select the new alias
|
||||
if dialog.result and dialog.result.get("alias") != alias:
|
||||
new_alias = dialog.result["alias"]
|
||||
# Sidebar auto-refreshes via store subscription
|
||||
self.sidebar._select(new_alias)
|
||||
# Migrate session pool reference
|
||||
self.session_pool.rename_server(alias, new_alias)
|
||||
else:
|
||||
self.info_tab.refresh()
|
||||
|
||||
def _delete_server(self, alias: str):
|
||||
if messagebox.askyesno(t("delete_server"), t("delete_confirm").format(alias=alias)):
|
||||
|
||||
@@ -26,6 +26,7 @@ class ServerDialog(ctk.CTkToplevel):
|
||||
super().__init__(master)
|
||||
self.store = store
|
||||
self.editing = server
|
||||
self._original_alias = server["alias"] if server else None
|
||||
self.result = None
|
||||
|
||||
self.title(t("edit_server") if server else t("add_server"))
|
||||
@@ -46,11 +47,15 @@ class ServerDialog(ctk.CTkToplevel):
|
||||
ctk.CTkLabel(self, text=t("alias"), anchor="w").pack(fill="x", **pad)
|
||||
self.alias_entry = ctk.CTkEntry(self, placeholder_text=t("placeholder_alias"))
|
||||
self.alias_entry.pack(fill="x", **entry_pad)
|
||||
# Enable undo functionality
|
||||
self.alias_entry._entry.config(undo=True)
|
||||
|
||||
# IP
|
||||
ctk.CTkLabel(self, text=t("ip"), anchor="w").pack(fill="x", **pad)
|
||||
self.ip_entry = ctk.CTkEntry(self, placeholder_text=t("placeholder_ip"))
|
||||
self.ip_entry.pack(fill="x", **entry_pad)
|
||||
# Enable undo functionality
|
||||
self.ip_entry._entry.config(undo=True)
|
||||
|
||||
# Type + Port row
|
||||
row = ctk.CTkFrame(self, fg_color="transparent")
|
||||
@@ -71,6 +76,8 @@ class ServerDialog(ctk.CTkToplevel):
|
||||
ctk.CTkLabel(port_frame, text=t("port"), anchor="w").pack(fill="x")
|
||||
self.port_entry = ctk.CTkEntry(port_frame, placeholder_text=t("placeholder_port"))
|
||||
self.port_entry.pack(fill="x")
|
||||
# Enable undo functionality
|
||||
self.port_entry._entry.config(undo=True)
|
||||
|
||||
# Network interface
|
||||
ctk.CTkLabel(self, text=t("network_interface"), anchor="w").pack(fill="x", **pad)
|
||||
@@ -90,6 +97,8 @@ class ServerDialog(ctk.CTkToplevel):
|
||||
ctk.CTkLabel(self, text=t("username"), anchor="w").pack(fill="x", **pad)
|
||||
self.user_entry = ctk.CTkEntry(self, placeholder_text=t("placeholder_user"))
|
||||
self.user_entry.pack(fill="x", **entry_pad)
|
||||
# Enable undo functionality
|
||||
self.user_entry._entry.config(undo=True)
|
||||
|
||||
# Password
|
||||
ctk.CTkLabel(self, text=t("password"), anchor="w").pack(fill="x", **pad)
|
||||
@@ -97,6 +106,8 @@ class ServerDialog(ctk.CTkToplevel):
|
||||
pass_frame.pack(fill="x", padx=20, pady=(2, 5))
|
||||
self.password_entry = ctk.CTkEntry(pass_frame, show="*", placeholder_text=t("placeholder_password"))
|
||||
self.password_entry.pack(side="left", fill="x", expand=True, padx=(0, 5))
|
||||
# Enable undo functionality
|
||||
self.password_entry._entry.config(undo=True)
|
||||
self.show_pass = ctk.CTkButton(pass_frame, text=t("show"), width=60, command=self._toggle_password)
|
||||
self.show_pass.pack(side="right")
|
||||
self._pass_visible = False
|
||||
@@ -106,6 +117,8 @@ class ServerDialog(ctk.CTkToplevel):
|
||||
self.totp_entry = ctk.CTkEntry(self, placeholder_text=t("placeholder_totp_secret"),
|
||||
font=ctk.CTkFont(family="Consolas", size=12))
|
||||
self.totp_entry.pack(fill="x", **entry_pad)
|
||||
# Enable undo functionality
|
||||
self.totp_entry._entry.config(undo=True)
|
||||
|
||||
# Skip status checks
|
||||
self.skip_check_var = ctk.BooleanVar(value=False)
|
||||
@@ -118,6 +131,8 @@ class ServerDialog(ctk.CTkToplevel):
|
||||
ctk.CTkLabel(self, text=t("notes"), anchor="w").pack(fill="x", **pad)
|
||||
self.notes_entry = ctk.CTkEntry(self, placeholder_text=t("placeholder_notes"))
|
||||
self.notes_entry.pack(fill="x", **entry_pad)
|
||||
# Enable undo functionality
|
||||
self.notes_entry._entry.config(undo=True)
|
||||
|
||||
# Buttons
|
||||
btn_frame = ctk.CTkFrame(self, fg_color="transparent")
|
||||
@@ -128,7 +143,6 @@ class ServerDialog(ctk.CTkToplevel):
|
||||
# Fill values if editing
|
||||
if server:
|
||||
self.alias_entry.insert(0, server.get("alias", ""))
|
||||
self.alias_entry.configure(state="disabled")
|
||||
self.ip_entry.insert(0, server.get("ip", ""))
|
||||
self.type_var.set(server.get("type", "ssh"))
|
||||
self.port_entry.insert(0, str(server.get("port", 22)))
|
||||
@@ -213,7 +227,11 @@ class ServerDialog(ctk.CTkToplevel):
|
||||
|
||||
try:
|
||||
if self.editing:
|
||||
self.store.update_server(alias, server_data)
|
||||
# Check for alias conflict if alias was changed
|
||||
if alias != self._original_alias and self.store.get_server(alias):
|
||||
self._show_error(t("alias_exists").format(alias=alias))
|
||||
return
|
||||
self.store.update_server(self._original_alias, server_data)
|
||||
else:
|
||||
self.store.add_server(server_data)
|
||||
self.result = server_data
|
||||
|
||||
@@ -29,6 +29,8 @@ class Sidebar(ctk.CTkFrame):
|
||||
self.search_var.trace_add("write", lambda *_: self._refresh_list())
|
||||
self.search_entry = ctk.CTkEntry(self, placeholder_text=t("search"), textvariable=self.search_var)
|
||||
self.search_entry.pack(fill="x", padx=10, pady=(5, 10))
|
||||
# Enable undo functionality
|
||||
self.search_entry._entry.config(undo=True)
|
||||
|
||||
# Server list
|
||||
self.list_frame = ctk.CTkScrollableFrame(self, fg_color="transparent")
|
||||
|
||||
@@ -131,6 +131,8 @@ class FilesTab(ctk.CTkFrame):
|
||||
self._local_path_entry = ctk.CTkEntry(left_header, height=28)
|
||||
self._local_path_entry.pack(side="left", fill="x", expand=True, padx=(4, 0))
|
||||
self._local_path_entry.bind("<Return>", lambda e: self._local_go_to_path())
|
||||
# Enable undo functionality
|
||||
self._local_path_entry._entry.config(undo=True)
|
||||
|
||||
self._local_list = FileListWidget(
|
||||
left_pane,
|
||||
@@ -177,6 +179,8 @@ class FilesTab(ctk.CTkFrame):
|
||||
self._remote_path_entry = ctk.CTkEntry(right_header, height=28)
|
||||
self._remote_path_entry.pack(side="left", fill="x", expand=True, padx=(4, 0))
|
||||
self._remote_path_entry.bind("<Return>", lambda e: self._remote_go_to_path())
|
||||
# Enable undo functionality
|
||||
self._remote_path_entry._entry.config(undo=True)
|
||||
|
||||
self._remote_list = FileListWidget(
|
||||
right_pane,
|
||||
|
||||
@@ -106,6 +106,8 @@ class TOTPTab(ctk.CTkFrame):
|
||||
font=ctk.CTkFont(family="Consolas", size=12)
|
||||
)
|
||||
self.secret_entry.pack(side="left", fill="x", expand=True, padx=(0, 5))
|
||||
# Enable undo functionality
|
||||
self.secret_entry._entry.config(undo=True)
|
||||
|
||||
self.show_secret_btn = ctk.CTkButton(
|
||||
entry_row, text=t("show"), width=70,
|
||||
|
||||
Reference in New Issue
Block a user