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:
chrome-storm-c442
2026-02-24 14:37:37 -05:00
parent 142b68515c
commit 4959004a3f
30 changed files with 596 additions and 134 deletions

View File

@@ -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 ─────────────────────────────