v1.8.17: revert broken undo, keep only alias-edit feature
Fully reverted sidebar, files_tab, totp_tab, server_dialog to v1.8.10 base. Removed all entry_undo bindings that broke Ctrl+V paste globally. Only alias editing feature preserved (editable alias + rename support). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -359,7 +359,6 @@ class ServerStore:
|
|||||||
if s["alias"] == alias:
|
if s["alias"] == alias:
|
||||||
new_alias = updated.get("alias", alias)
|
new_alias = updated.get("alias", alias)
|
||||||
servers[i] = updated
|
servers[i] = updated
|
||||||
# If alias changed, migrate status
|
|
||||||
if new_alias != alias:
|
if new_alias != alias:
|
||||||
with self._statuses_lock:
|
with self._statuses_lock:
|
||||||
old_status = self._statuses.pop(alias, None)
|
old_status = self._statuses.pop(alias, None)
|
||||||
|
|||||||
@@ -129,12 +129,9 @@ class App(ctk.CTk):
|
|||||||
if server:
|
if server:
|
||||||
dialog = ServerDialog(self, self.store, server=server)
|
dialog = ServerDialog(self, self.store, server=server)
|
||||||
self.wait_window(dialog)
|
self.wait_window(dialog)
|
||||||
# If alias was changed, re-select the new alias
|
|
||||||
if dialog.result and dialog.result.get("alias") != alias:
|
if dialog.result and dialog.result.get("alias") != alias:
|
||||||
new_alias = dialog.result["alias"]
|
new_alias = dialog.result["alias"]
|
||||||
# Sidebar auto-refreshes via store subscription
|
|
||||||
self.sidebar._select(new_alias)
|
self.sidebar._select(new_alias)
|
||||||
# Migrate session pool reference
|
|
||||||
self.session_pool.rename_server(alias, new_alias)
|
self.session_pool.rename_server(alias, new_alias)
|
||||||
else:
|
else:
|
||||||
self.info_tab.refresh()
|
self.info_tab.refresh()
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ Server add/edit dialog — modal window with all server fields.
|
|||||||
import customtkinter as ctk
|
import customtkinter as ctk
|
||||||
from core.server_store import SERVER_TYPES, DEFAULT_PORTS
|
from core.server_store import SERVER_TYPES, DEFAULT_PORTS
|
||||||
from core.i18n import t
|
from core.i18n import t
|
||||||
from gui.widgets.entry_undo import enable_undo
|
|
||||||
|
|
||||||
|
|
||||||
def _get_network_interfaces() -> list[tuple[str, str]]:
|
def _get_network_interfaces() -> list[tuple[str, str]]:
|
||||||
@@ -48,13 +47,11 @@ class ServerDialog(ctk.CTkToplevel):
|
|||||||
ctk.CTkLabel(self, text=t("alias"), anchor="w").pack(fill="x", **pad)
|
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 = ctk.CTkEntry(self, placeholder_text=t("placeholder_alias"))
|
||||||
self.alias_entry.pack(fill="x", **entry_pad)
|
self.alias_entry.pack(fill="x", **entry_pad)
|
||||||
enable_undo(self.alias_entry)
|
|
||||||
|
|
||||||
# IP
|
# IP
|
||||||
ctk.CTkLabel(self, text=t("ip"), anchor="w").pack(fill="x", **pad)
|
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 = ctk.CTkEntry(self, placeholder_text=t("placeholder_ip"))
|
||||||
self.ip_entry.pack(fill="x", **entry_pad)
|
self.ip_entry.pack(fill="x", **entry_pad)
|
||||||
enable_undo(self.ip_entry)
|
|
||||||
|
|
||||||
# Type + Port row
|
# Type + Port row
|
||||||
row = ctk.CTkFrame(self, fg_color="transparent")
|
row = ctk.CTkFrame(self, fg_color="transparent")
|
||||||
@@ -75,7 +72,6 @@ class ServerDialog(ctk.CTkToplevel):
|
|||||||
ctk.CTkLabel(port_frame, text=t("port"), anchor="w").pack(fill="x")
|
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 = ctk.CTkEntry(port_frame, placeholder_text=t("placeholder_port"))
|
||||||
self.port_entry.pack(fill="x")
|
self.port_entry.pack(fill="x")
|
||||||
enable_undo(self.port_entry)
|
|
||||||
|
|
||||||
# Network interface
|
# Network interface
|
||||||
ctk.CTkLabel(self, text=t("network_interface"), anchor="w").pack(fill="x", **pad)
|
ctk.CTkLabel(self, text=t("network_interface"), anchor="w").pack(fill="x", **pad)
|
||||||
@@ -95,7 +91,6 @@ class ServerDialog(ctk.CTkToplevel):
|
|||||||
ctk.CTkLabel(self, text=t("username"), anchor="w").pack(fill="x", **pad)
|
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 = ctk.CTkEntry(self, placeholder_text=t("placeholder_user"))
|
||||||
self.user_entry.pack(fill="x", **entry_pad)
|
self.user_entry.pack(fill="x", **entry_pad)
|
||||||
enable_undo(self.user_entry)
|
|
||||||
|
|
||||||
# Password
|
# Password
|
||||||
ctk.CTkLabel(self, text=t("password"), anchor="w").pack(fill="x", **pad)
|
ctk.CTkLabel(self, text=t("password"), anchor="w").pack(fill="x", **pad)
|
||||||
@@ -103,7 +98,6 @@ class ServerDialog(ctk.CTkToplevel):
|
|||||||
pass_frame.pack(fill="x", padx=20, pady=(2, 5))
|
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 = ctk.CTkEntry(pass_frame, show="*", placeholder_text=t("placeholder_password"))
|
||||||
self.password_entry.pack(side="left", fill="x", expand=True, padx=(0, 5))
|
self.password_entry.pack(side="left", fill="x", expand=True, padx=(0, 5))
|
||||||
enable_undo(self.password_entry)
|
|
||||||
self.show_pass = ctk.CTkButton(pass_frame, text=t("show"), width=60, command=self._toggle_password)
|
self.show_pass = ctk.CTkButton(pass_frame, text=t("show"), width=60, command=self._toggle_password)
|
||||||
self.show_pass.pack(side="right")
|
self.show_pass.pack(side="right")
|
||||||
self._pass_visible = False
|
self._pass_visible = False
|
||||||
@@ -113,7 +107,6 @@ class ServerDialog(ctk.CTkToplevel):
|
|||||||
self.totp_entry = ctk.CTkEntry(self, placeholder_text=t("placeholder_totp_secret"),
|
self.totp_entry = ctk.CTkEntry(self, placeholder_text=t("placeholder_totp_secret"),
|
||||||
font=ctk.CTkFont(family="Consolas", size=12))
|
font=ctk.CTkFont(family="Consolas", size=12))
|
||||||
self.totp_entry.pack(fill="x", **entry_pad)
|
self.totp_entry.pack(fill="x", **entry_pad)
|
||||||
enable_undo(self.totp_entry)
|
|
||||||
|
|
||||||
# Skip status checks
|
# Skip status checks
|
||||||
self.skip_check_var = ctk.BooleanVar(value=False)
|
self.skip_check_var = ctk.BooleanVar(value=False)
|
||||||
@@ -126,7 +119,6 @@ class ServerDialog(ctk.CTkToplevel):
|
|||||||
ctk.CTkLabel(self, text=t("notes"), anchor="w").pack(fill="x", **pad)
|
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 = ctk.CTkEntry(self, placeholder_text=t("placeholder_notes"))
|
||||||
self.notes_entry.pack(fill="x", **entry_pad)
|
self.notes_entry.pack(fill="x", **entry_pad)
|
||||||
enable_undo(self.notes_entry)
|
|
||||||
|
|
||||||
# Buttons
|
# Buttons
|
||||||
btn_frame = ctk.CTkFrame(self, fg_color="transparent")
|
btn_frame = ctk.CTkFrame(self, fg_color="transparent")
|
||||||
@@ -221,7 +213,6 @@ class ServerDialog(ctk.CTkToplevel):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
if self.editing:
|
if self.editing:
|
||||||
# Check for alias conflict if alias was changed
|
|
||||||
if alias != self._original_alias and self.store.get_server(alias):
|
if alias != self._original_alias and self.store.get_server(alias):
|
||||||
self._show_error(t("alias_exists").format(alias=alias))
|
self._show_error(t("alias_exists").format(alias=alias))
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ Sidebar — server list with search, add/edit/delete buttons.
|
|||||||
import customtkinter as ctk
|
import customtkinter as ctk
|
||||||
from core.i18n import t
|
from core.i18n import t
|
||||||
from gui.widgets.status_badge import StatusBadge
|
from gui.widgets.status_badge import StatusBadge
|
||||||
from gui.widgets.entry_undo import enable_undo
|
|
||||||
|
|
||||||
|
|
||||||
class Sidebar(ctk.CTkFrame):
|
class Sidebar(ctk.CTkFrame):
|
||||||
@@ -30,7 +29,6 @@ class Sidebar(ctk.CTkFrame):
|
|||||||
self.search_var.trace_add("write", lambda *_: self._refresh_list())
|
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 = ctk.CTkEntry(self, placeholder_text=t("search"), textvariable=self.search_var)
|
||||||
self.search_entry.pack(fill="x", padx=10, pady=(5, 10))
|
self.search_entry.pack(fill="x", padx=10, pady=(5, 10))
|
||||||
enable_undo(self.search_entry)
|
|
||||||
|
|
||||||
# Server list
|
# Server list
|
||||||
self.list_frame = ctk.CTkScrollableFrame(self, fg_color="transparent")
|
self.list_frame = ctk.CTkScrollableFrame(self, fg_color="transparent")
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ from tkinter import messagebox, filedialog
|
|||||||
import customtkinter as ctk
|
import customtkinter as ctk
|
||||||
|
|
||||||
from core.i18n import t
|
from core.i18n import t
|
||||||
from gui.widgets.entry_undo import enable_undo
|
|
||||||
from core.ssh_client import SFTPSession
|
from core.ssh_client import SFTPSession
|
||||||
from gui.widgets.file_list import FileListWidget
|
from gui.widgets.file_list import FileListWidget
|
||||||
|
|
||||||
@@ -132,7 +131,6 @@ class FilesTab(ctk.CTkFrame):
|
|||||||
self._local_path_entry = ctk.CTkEntry(left_header, height=28)
|
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.pack(side="left", fill="x", expand=True, padx=(4, 0))
|
||||||
self._local_path_entry.bind("<Return>", lambda e: self._local_go_to_path())
|
self._local_path_entry.bind("<Return>", lambda e: self._local_go_to_path())
|
||||||
enable_undo(self._local_path_entry)
|
|
||||||
|
|
||||||
self._local_list = FileListWidget(
|
self._local_list = FileListWidget(
|
||||||
left_pane,
|
left_pane,
|
||||||
@@ -179,7 +177,6 @@ class FilesTab(ctk.CTkFrame):
|
|||||||
self._remote_path_entry = ctk.CTkEntry(right_header, height=28)
|
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.pack(side="left", fill="x", expand=True, padx=(4, 0))
|
||||||
self._remote_path_entry.bind("<Return>", lambda e: self._remote_go_to_path())
|
self._remote_path_entry.bind("<Return>", lambda e: self._remote_go_to_path())
|
||||||
enable_undo(self._remote_path_entry)
|
|
||||||
|
|
||||||
self._remote_list = FileListWidget(
|
self._remote_list = FileListWidget(
|
||||||
right_pane,
|
right_pane,
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ Live countdown, one-click copy, per-server secrets.
|
|||||||
import threading
|
import threading
|
||||||
import customtkinter as ctk
|
import customtkinter as ctk
|
||||||
from core.i18n import t
|
from core.i18n import t
|
||||||
from gui.widgets.entry_undo import enable_undo
|
|
||||||
|
|
||||||
|
|
||||||
class TOTPTab(ctk.CTkFrame):
|
class TOTPTab(ctk.CTkFrame):
|
||||||
@@ -107,7 +106,6 @@ class TOTPTab(ctk.CTkFrame):
|
|||||||
font=ctk.CTkFont(family="Consolas", size=12)
|
font=ctk.CTkFont(family="Consolas", size=12)
|
||||||
)
|
)
|
||||||
self.secret_entry.pack(side="left", fill="x", expand=True, padx=(0, 5))
|
self.secret_entry.pack(side="left", fill="x", expand=True, padx=(0, 5))
|
||||||
enable_undo(self.secret_entry)
|
|
||||||
|
|
||||||
self.show_secret_btn = ctk.CTkButton(
|
self.show_secret_btn = ctk.CTkButton(
|
||||||
entry_row, text=t("show"), width=70,
|
entry_row, text=t("show"), width=70,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"""Version info for ServerManager."""
|
"""Version info for ServerManager."""
|
||||||
|
|
||||||
__version__ = "1.8.13"
|
__version__ = "1.8.17"
|
||||||
__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