""" Setup tab — one-click installation for Claude Code integration. """ import threading import customtkinter as ctk from core.claude_setup import check_status, install_all, install_ssh_script, install_skill, generate_ssh_key class SetupTab(ctk.CTkFrame): def __init__(self, master, store): super().__init__(master, fg_color="transparent") self.store = store # Header ctk.CTkLabel( self, text="Claude Code Integration", font=ctk.CTkFont(size=20, weight="bold") ).pack(padx=20, pady=(20, 5)) ctk.CTkLabel( self, text="Setup everything so Claude Code can manage your servers via /ssh skill.\n" "Both GUI and Claude Code share the same servers.json — add a server here,\n" "Claude sees it immediately.", text_color="#9ca3af", justify="center" ).pack(padx=20, pady=(0, 15)) # Status card self.status_frame = ctk.CTkFrame(self) self.status_frame.pack(fill="x", padx=20, pady=10) ctk.CTkLabel( self.status_frame, text="Status", font=ctk.CTkFont(size=14, weight="bold"), anchor="w" ).pack(fill="x", padx=15, pady=(10, 5)) self._status_labels: dict[str, ctk.CTkLabel] = {} status_items = [ ("shared_dir", "Shared config dir (~/.server-connections)"), ("servers_json", "servers.json"), ("ssh_script", "ssh.py (CLI tool)"), ("skill_installed", "/ssh skill for Claude Code"), ("ssh_key_exists", "SSH key (ed25519)"), ] for key, label in status_items: row = ctk.CTkFrame(self.status_frame, fg_color="transparent") row.pack(fill="x", padx=15, pady=2) indicator = ctk.CTkLabel(row, text="\u25cf", width=20, text_color="#6b7280") indicator.pack(side="left") ctk.CTkLabel(row, text=label, anchor="w").pack(side="left", fill="x", expand=True) self._status_labels[key] = indicator # Buttons btn_frame = ctk.CTkFrame(self, fg_color="transparent") btn_frame.pack(fill="x", padx=20, pady=15) self.install_all_btn = ctk.CTkButton( btn_frame, text="Install Everything", font=ctk.CTkFont(size=14, weight="bold"), height=40, fg_color="#22c55e", hover_color="#16a34a", command=self._install_all ) self.install_all_btn.pack(fill="x", pady=(0, 10)) # Individual buttons row ind_frame = ctk.CTkFrame(btn_frame, fg_color="transparent") ind_frame.pack(fill="x") ctk.CTkButton(ind_frame, text="ssh.py", width=100, fg_color="#6b7280", command=self._install_script).pack(side="left", padx=(0, 5)) ctk.CTkButton(ind_frame, text="/ssh skill", width=100, fg_color="#6b7280", command=self._install_skill).pack(side="left", padx=5) ctk.CTkButton(ind_frame, text="SSH key", width=100, fg_color="#6b7280", command=self._gen_key).pack(side="left", padx=5) ctk.CTkButton(ind_frame, text="Refresh", width=80, fg_color="#3b82f6", command=self._refresh_status).pack(side="right") # Log self.log = ctk.CTkTextbox(self, height=150, font=ctk.CTkFont(family="Consolas", size=11), state="disabled") self.log.pack(fill="both", expand=True, padx=20, pady=(5, 20)) # Initial status check self._refresh_status() def _log(self, text: str): self.log.configure(state="normal") self.log.insert("end", text + "\n") self.log.configure(state="disabled") self.log.see("end") def _refresh_status(self): status = check_status() for key, label in self._status_labels.items(): if status.get(key, False): label.configure(text="\u25cf", text_color="#22c55e") # green else: label.configure(text="\u25cf", text_color="#ef4444") # red def _install_all(self): self.install_all_btn.configure(state="disabled", text="Installing...") def _do(): results = install_all() for msg in results: self.after(0, lambda m=msg: self._log(m)) self.after(0, self._refresh_status) self.after(0, lambda: self._log("\nDone! Claude Code can now use /ssh to manage your servers.")) self.after(0, lambda: self.install_all_btn.configure(state="normal", text="Install Everything")) threading.Thread(target=_do, daemon=True).start() def _install_script(self): msg = install_ssh_script() self._log(msg) self._refresh_status() def _install_skill(self): msg = install_skill() self._log(msg) self._refresh_status() def _gen_key(self): msg = generate_ssh_key() self._log(msg) self._refresh_status()