v1.8.52: icons module, Windows SSH sanitization, embedded RDP improvements, UI polish
- Add core/icons.py — centralized icon text helper with emoji/symbol support - Add Windows SSH command sanitization in ssh.py (Linux→Windows auto-translation) - Improve embedded RDP: launch tab connect/disconnect, fullscreen toggle - Refactor sidebar: cleaner server type badges - Update server_dialog: adaptive fields per server type - Add setup_openssh.bat tool - Update skill-ssh.md and CLAUDE.md docs for Windows SSH support - Cleanup old releases, add v1.8.48-v1.8.52 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -77,6 +77,22 @@ class LaunchTab(ctk.CTkFrame):
|
||||
)
|
||||
card_title.pack(fill="x", padx=15, pady=(12, 8))
|
||||
|
||||
# Resolution
|
||||
r_row = ctk.CTkFrame(self._settings_card, fg_color="transparent")
|
||||
r_row.pack(fill="x", padx=15, pady=3)
|
||||
ctk.CTkLabel(r_row, text=t("rdp_resolution"), width=140, anchor="w").pack(side="left")
|
||||
self._resolution_var = ctk.StringVar(value=t("rdp_resolution_auto"))
|
||||
resolution_values = [
|
||||
t("rdp_resolution_auto"),
|
||||
"800\u00d7600", "1024\u00d7768", "1280\u00d71024",
|
||||
"1366\u00d7768", "1600\u00d7900", "1920\u00d71080",
|
||||
]
|
||||
self._resolution_menu = ctk.CTkOptionMenu(
|
||||
r_row, values=resolution_values,
|
||||
variable=self._resolution_var, width=180,
|
||||
)
|
||||
self._resolution_menu.pack(side="left")
|
||||
|
||||
# Quality
|
||||
q_row = ctk.CTkFrame(self._settings_card, fg_color="transparent")
|
||||
q_row.pack(fill="x", padx=15, pady=3)
|
||||
@@ -184,6 +200,11 @@ class LaunchTab(ctk.CTkFrame):
|
||||
self._info_label.configure(text=t("launch_rdp_info").format(alias=alias))
|
||||
self._settings_card.pack(fill="x", padx=40, pady=(0, 15))
|
||||
# Load saved RDP settings from server
|
||||
res_raw = server.get("rdp_resolution", "auto")
|
||||
if res_raw == "auto":
|
||||
self._resolution_var.set(t("rdp_resolution_auto"))
|
||||
else:
|
||||
self._resolution_var.set(res_raw.replace("x", "\u00d7"))
|
||||
self._quality_var.set(self._quality_labels.get(
|
||||
server.get("rdp_quality", "auto"), self._quality_labels["auto"]
|
||||
))
|
||||
@@ -235,6 +256,13 @@ class LaunchTab(ctk.CTkFrame):
|
||||
"printers": self._printers_var.get(),
|
||||
}
|
||||
|
||||
# Parse resolution
|
||||
res = self._resolution_var.get()
|
||||
if res == t("rdp_resolution_auto"):
|
||||
settings["resolution"] = "auto"
|
||||
else:
|
||||
settings["resolution"] = res.replace("\u00d7", "x")
|
||||
|
||||
self._embedded_rdp = EmbeddedRDP(server, settings)
|
||||
|
||||
# Set callbacks
|
||||
@@ -251,6 +279,9 @@ class LaunchTab(ctk.CTkFrame):
|
||||
self._rdp_frame.update_idletasks()
|
||||
|
||||
parent_hwnd = self._rdp_frame.winfo_id()
|
||||
|
||||
# Always use frame size for the mstsc window;
|
||||
# session resolution is handled inside generate_rdp_file via settings
|
||||
w = max(self._rdp_frame.winfo_width(), 800)
|
||||
h = max(self._rdp_frame.winfo_height(), 600)
|
||||
|
||||
@@ -262,9 +293,26 @@ class LaunchTab(ctk.CTkFrame):
|
||||
text=t("rdp_connected").format(alias=self._current_alias),
|
||||
text_color="#22c55e",
|
||||
)
|
||||
# Re-normalize position after embed settles
|
||||
self.after(300, self._normalize_rdp_position)
|
||||
self.after(1000, self._normalize_rdp_position)
|
||||
# Start monitoring
|
||||
self._start_monitor()
|
||||
|
||||
def _normalize_rdp_position(self):
|
||||
"""Force mstsc to fill the frame at (0,0) — fixes post-embed offset."""
|
||||
if not self._embedded_rdp or not self._embedded_rdp.connected:
|
||||
return
|
||||
if self._is_fullscreen:
|
||||
return
|
||||
try:
|
||||
w = self._rdp_frame.winfo_width()
|
||||
h = self._rdp_frame.winfo_height()
|
||||
if w > 10 and h > 10:
|
||||
self._embedded_rdp.resize(w, h)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _on_rdp_failed(self, error: str):
|
||||
"""Called when embedding failed."""
|
||||
self._toolbar_status.configure(
|
||||
@@ -335,12 +383,18 @@ class LaunchTab(ctk.CTkFrame):
|
||||
h = self._rdp_frame.winfo_height()
|
||||
self._embedded_rdp.reattach(parent_hwnd, w, h)
|
||||
else:
|
||||
# Go fullscreen — detach
|
||||
# Go fullscreen — detach, then maximize after event loop settles
|
||||
self._is_fullscreen = True
|
||||
self._fullscreen_btn.configure(
|
||||
text=icon_text("back", t("rdp_exit_fullscreen")),
|
||||
)
|
||||
self._embedded_rdp.detach()
|
||||
self.after(300, self._maximize_detached)
|
||||
|
||||
def _maximize_detached(self):
|
||||
"""Called after delay — maximize the detached mstsc window."""
|
||||
if self._embedded_rdp:
|
||||
self._embedded_rdp.maximize()
|
||||
|
||||
# ── Resize handling with debounce ─────────────────────────────
|
||||
|
||||
|
||||
Reference in New Issue
Block a user