mirror of
https://github.com/andrey271192/ssh-manage.git
synced 2026-09-20 11:55:37 +00:00
fix: clipboard support — Cmd/Ctrl+V paste, right-click menu
All Entry widgets: Cmd+C/V/X/A (macOS), Ctrl+C/V/X/A (Windows), right-click context menu with Cut/Copy/Paste/Select All. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -490,6 +490,61 @@ def agent_search(query):
|
|||||||
return results[:12]
|
return results[:12]
|
||||||
|
|
||||||
|
|
||||||
|
def setup_entry_clipboard(entry):
|
||||||
|
"""Add clipboard support (Ctrl/Cmd+C/V/X/A) and right-click menu to tk.Entry."""
|
||||||
|
mod = "Command" if sys.platform == "darwin" else "Control"
|
||||||
|
|
||||||
|
def _paste(e=None):
|
||||||
|
try:
|
||||||
|
text = entry.clipboard_get()
|
||||||
|
if entry.selection_present():
|
||||||
|
entry.delete(tk.SEL_FIRST, tk.SEL_LAST)
|
||||||
|
entry.insert(tk.INSERT, text)
|
||||||
|
except tk.TclError:
|
||||||
|
pass
|
||||||
|
return "break"
|
||||||
|
|
||||||
|
def _copy(e=None):
|
||||||
|
if entry.selection_present():
|
||||||
|
entry.clipboard_clear()
|
||||||
|
entry.clipboard_append(entry.selection_get())
|
||||||
|
return "break"
|
||||||
|
|
||||||
|
def _cut(e=None):
|
||||||
|
if entry.selection_present():
|
||||||
|
_copy()
|
||||||
|
entry.delete(tk.SEL_FIRST, tk.SEL_LAST)
|
||||||
|
return "break"
|
||||||
|
|
||||||
|
def _select_all(e=None):
|
||||||
|
entry.select_range(0, tk.END)
|
||||||
|
entry.icursor(tk.END)
|
||||||
|
return "break"
|
||||||
|
|
||||||
|
def _context_menu(event):
|
||||||
|
menu = tk.Menu(entry, tearoff=0, bg="#313244", fg="#cdd6f4",
|
||||||
|
activebackground="#45475a")
|
||||||
|
menu.add_command(label="Вырезать", command=_cut)
|
||||||
|
menu.add_command(label="Копировать", command=_copy)
|
||||||
|
menu.add_command(label="Вставить", command=_paste)
|
||||||
|
menu.add_separator()
|
||||||
|
menu.add_command(label="Выделить всё", command=_select_all)
|
||||||
|
menu.tk_popup(event.x_root, event.y_root)
|
||||||
|
|
||||||
|
entry.bind(f"<{mod}-v>", _paste)
|
||||||
|
entry.bind(f"<{mod}-V>", _paste)
|
||||||
|
entry.bind(f"<{mod}-c>", _copy)
|
||||||
|
entry.bind(f"<{mod}-C>", _copy)
|
||||||
|
entry.bind(f"<{mod}-x>", _cut)
|
||||||
|
entry.bind(f"<{mod}-X>", _cut)
|
||||||
|
entry.bind(f"<{mod}-a>", _select_all)
|
||||||
|
entry.bind(f"<{mod}-A>", _select_all)
|
||||||
|
entry.bind("<Button-3>", _context_menu)
|
||||||
|
if sys.platform == "darwin":
|
||||||
|
entry.bind("<Button-2>", _context_menu)
|
||||||
|
entry.bind("<Control-Button-1>", _context_menu)
|
||||||
|
|
||||||
|
|
||||||
def app_dir():
|
def app_dir():
|
||||||
if getattr(sys, "frozen", False):
|
if getattr(sys, "frozen", False):
|
||||||
return Path(sys.executable).parent
|
return Path(sys.executable).parent
|
||||||
@@ -829,6 +884,7 @@ class SessionDialog(tk.Toplevel):
|
|||||||
entry.configure(show="*")
|
entry.configure(show="*")
|
||||||
entry.insert(0, default)
|
entry.insert(0, default)
|
||||||
entry.grid(row=i, column=1, sticky="ew", pady=3, padx=(8, 0))
|
entry.grid(row=i, column=1, sticky="ew", pady=3, padx=(8, 0))
|
||||||
|
setup_entry_clipboard(entry)
|
||||||
self.entries[key] = entry
|
self.entries[key] = entry
|
||||||
|
|
||||||
row_group = len(fields)
|
row_group = len(fields)
|
||||||
@@ -926,6 +982,7 @@ class AISettingsDialog(tk.Toplevel):
|
|||||||
font=("Consolas", 9), show="*")
|
font=("Consolas", 9), show="*")
|
||||||
self.claude_key.insert(0, config.get("claude_api_key", ""))
|
self.claude_key.insert(0, config.get("claude_api_key", ""))
|
||||||
self.claude_key.pack(fill=tk.X, pady=(2, 8))
|
self.claude_key.pack(fill=tk.X, pady=(2, 8))
|
||||||
|
setup_entry_clipboard(self.claude_key)
|
||||||
|
|
||||||
tk.Label(frame, text="DeepSeek API Key:", bg="#1e1e2e", fg="#cdd6f4",
|
tk.Label(frame, text="DeepSeek API Key:", bg="#1e1e2e", fg="#cdd6f4",
|
||||||
font=("Consolas", 10)).pack(anchor="w")
|
font=("Consolas", 10)).pack(anchor="w")
|
||||||
@@ -934,6 +991,7 @@ class AISettingsDialog(tk.Toplevel):
|
|||||||
font=("Consolas", 9), show="*")
|
font=("Consolas", 9), show="*")
|
||||||
self.deepseek_key.insert(0, config.get("deepseek_api_key", ""))
|
self.deepseek_key.insert(0, config.get("deepseek_api_key", ""))
|
||||||
self.deepseek_key.pack(fill=tk.X, pady=(2, 12))
|
self.deepseek_key.pack(fill=tk.X, pady=(2, 12))
|
||||||
|
setup_entry_clipboard(self.deepseek_key)
|
||||||
|
|
||||||
btn_frame = tk.Frame(frame, bg="#1e1e2e")
|
btn_frame = tk.Frame(frame, bg="#1e1e2e")
|
||||||
btn_frame.pack()
|
btn_frame.pack()
|
||||||
@@ -1192,11 +1250,13 @@ class App(tk.Tk):
|
|||||||
self.q_host = tk.Entry(row1, width=14, bg="#313244", fg="#cdd6f4",
|
self.q_host = tk.Entry(row1, width=14, bg="#313244", fg="#cdd6f4",
|
||||||
insertbackground="#cdd6f4", relief=tk.FLAT, font=("Consolas", 9))
|
insertbackground="#cdd6f4", relief=tk.FLAT, font=("Consolas", 9))
|
||||||
self.q_host.pack(side=tk.LEFT, padx=(4, 6))
|
self.q_host.pack(side=tk.LEFT, padx=(4, 6))
|
||||||
|
setup_entry_clipboard(self.q_host)
|
||||||
tk.Label(row1, text="Порт:", bg="#181825", fg="#6c7086", font=("Consolas", 9)).pack(side=tk.LEFT)
|
tk.Label(row1, text="Порт:", bg="#181825", fg="#6c7086", font=("Consolas", 9)).pack(side=tk.LEFT)
|
||||||
self.q_port = tk.Entry(row1, width=5, bg="#313244", fg="#cdd6f4",
|
self.q_port = tk.Entry(row1, width=5, bg="#313244", fg="#cdd6f4",
|
||||||
insertbackground="#cdd6f4", relief=tk.FLAT, font=("Consolas", 9))
|
insertbackground="#cdd6f4", relief=tk.FLAT, font=("Consolas", 9))
|
||||||
self.q_port.insert(0, "22")
|
self.q_port.insert(0, "22")
|
||||||
self.q_port.pack(side=tk.LEFT, padx=4)
|
self.q_port.pack(side=tk.LEFT, padx=4)
|
||||||
|
setup_entry_clipboard(self.q_port)
|
||||||
|
|
||||||
row2 = tk.Frame(qf, bg="#181825")
|
row2 = tk.Frame(qf, bg="#181825")
|
||||||
row2.pack(fill=tk.X, pady=1)
|
row2.pack(fill=tk.X, pady=1)
|
||||||
@@ -1205,10 +1265,12 @@ class App(tk.Tk):
|
|||||||
insertbackground="#cdd6f4", relief=tk.FLAT, font=("Consolas", 9))
|
insertbackground="#cdd6f4", relief=tk.FLAT, font=("Consolas", 9))
|
||||||
self.q_user.insert(0, "root")
|
self.q_user.insert(0, "root")
|
||||||
self.q_user.pack(side=tk.LEFT, padx=(4, 6))
|
self.q_user.pack(side=tk.LEFT, padx=(4, 6))
|
||||||
|
setup_entry_clipboard(self.q_user)
|
||||||
tk.Label(row2, text="Пароль:", bg="#181825", fg="#6c7086", font=("Consolas", 9)).pack(side=tk.LEFT)
|
tk.Label(row2, text="Пароль:", bg="#181825", fg="#6c7086", font=("Consolas", 9)).pack(side=tk.LEFT)
|
||||||
self.q_pass = tk.Entry(row2, width=10, bg="#313244", fg="#cdd6f4", show="*",
|
self.q_pass = tk.Entry(row2, width=10, bg="#313244", fg="#cdd6f4", show="*",
|
||||||
insertbackground="#cdd6f4", relief=tk.FLAT, font=("Consolas", 9))
|
insertbackground="#cdd6f4", relief=tk.FLAT, font=("Consolas", 9))
|
||||||
self.q_pass.pack(side=tk.LEFT, padx=4)
|
self.q_pass.pack(side=tk.LEFT, padx=4)
|
||||||
|
setup_entry_clipboard(self.q_pass)
|
||||||
|
|
||||||
tk.Button(qf, text="Подключиться", bg="#89b4fa", fg="#1e1e2e",
|
tk.Button(qf, text="Подключиться", bg="#89b4fa", fg="#1e1e2e",
|
||||||
activebackground="#6c8fff", relief=tk.FLAT,
|
activebackground="#6c8fff", relief=tk.FLAT,
|
||||||
@@ -1430,6 +1492,7 @@ class App(tk.Tk):
|
|||||||
insertbackground="#cdd6f4", relief=tk.FLAT,
|
insertbackground="#cdd6f4", relief=tk.FLAT,
|
||||||
font=("Consolas", 9))
|
font=("Consolas", 9))
|
||||||
self.cmd_search.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(4, 0))
|
self.cmd_search.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(4, 0))
|
||||||
|
setup_entry_clipboard(self.cmd_search)
|
||||||
self.cmd_search.bind("<KeyRelease>", self._filter_commands)
|
self.cmd_search.bind("<KeyRelease>", self._filter_commands)
|
||||||
|
|
||||||
# Commands tree
|
# Commands tree
|
||||||
@@ -1488,6 +1551,7 @@ class App(tk.Tk):
|
|||||||
insertbackground="#cdd6f4", relief=tk.FLAT,
|
insertbackground="#cdd6f4", relief=tk.FLAT,
|
||||||
font=("Consolas", 10))
|
font=("Consolas", 10))
|
||||||
self.agent_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 4))
|
self.agent_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 4))
|
||||||
|
setup_entry_clipboard(self.agent_entry)
|
||||||
self.agent_entry.insert(0, "как обновить ubuntu?")
|
self.agent_entry.insert(0, "как обновить ubuntu?")
|
||||||
self.agent_entry.bind("<Return>", lambda e: self._agent_ask())
|
self.agent_entry.bind("<Return>", lambda e: self._agent_ask())
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user