- Shared servers.json at ~/.server-connections/ (GUI + Claude Code) - Setup tab: one-click install of ssh.py, /ssh skill, SSH key - Duplicate checks — safe to run multiple times - tools/ssh.py + tools/skill-ssh.md bundled - Updated README with integration docs (EN/RU/ZH) - Deploy guide for new machines Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
113 lines
3.5 KiB
Python
113 lines
3.5 KiB
Python
"""
|
|
Main application window — sidebar + tabview layout.
|
|
"""
|
|
|
|
import customtkinter as ctk
|
|
from tkinter import messagebox
|
|
|
|
from core.server_store import ServerStore
|
|
from core.status_checker import StatusChecker
|
|
from gui.sidebar import Sidebar
|
|
from gui.server_dialog import ServerDialog
|
|
from gui.tabs.terminal_tab import TerminalTab
|
|
from gui.tabs.files_tab import FilesTab
|
|
from gui.tabs.info_tab import InfoTab
|
|
from gui.tabs.keys_tab import KeysTab
|
|
from gui.tabs.setup_tab import SetupTab
|
|
|
|
|
|
class App(ctk.CTk):
|
|
def __init__(self):
|
|
super().__init__()
|
|
|
|
# Window config
|
|
self.title("ServerManager")
|
|
self.geometry("1100x700")
|
|
self.minsize(900, 500)
|
|
|
|
ctk.set_appearance_mode("dark")
|
|
ctk.set_default_color_theme("blue")
|
|
|
|
# Core
|
|
self.store = ServerStore()
|
|
self.checker = StatusChecker(self.store, interval=60)
|
|
|
|
# Layout
|
|
self._build_layout()
|
|
|
|
# Status checker
|
|
self.checker.set_gui_callback(lambda: self.after(0, self._on_status_update))
|
|
self.checker.start()
|
|
self.checker.check_all_now()
|
|
|
|
# Cleanup on close
|
|
self.protocol("WM_DELETE_WINDOW", self._on_close)
|
|
|
|
def _build_layout(self):
|
|
# Sidebar
|
|
self.sidebar = Sidebar(self, self.store, on_select=self._on_server_select)
|
|
self.sidebar.pack(side="left", fill="y")
|
|
self.sidebar.add_callback = self._add_server
|
|
self.sidebar.edit_callback = self._edit_server
|
|
self.sidebar.delete_callback = self._delete_server
|
|
|
|
# Main area
|
|
main = ctk.CTkFrame(self, fg_color="transparent")
|
|
main.pack(side="right", fill="both", expand=True)
|
|
|
|
# Tabview
|
|
self.tabview = ctk.CTkTabview(main)
|
|
self.tabview.pack(fill="both", expand=True, padx=10, pady=10)
|
|
|
|
# Tabs
|
|
self.tabview.add("Terminal")
|
|
self.tabview.add("Files")
|
|
self.tabview.add("Info")
|
|
self.tabview.add("Keys")
|
|
self.tabview.add("Setup")
|
|
|
|
self.terminal_tab = TerminalTab(self.tabview.tab("Terminal"), self.store)
|
|
self.terminal_tab.pack(fill="both", expand=True)
|
|
|
|
self.files_tab = FilesTab(self.tabview.tab("Files"), self.store)
|
|
self.files_tab.pack(fill="both", expand=True)
|
|
|
|
self.info_tab = InfoTab(self.tabview.tab("Info"), self.store, edit_callback=self._edit_server)
|
|
self.info_tab.pack(fill="both", expand=True)
|
|
|
|
self.keys_tab = KeysTab(self.tabview.tab("Keys"), self.store)
|
|
self.keys_tab.pack(fill="both", expand=True)
|
|
|
|
self.setup_tab = SetupTab(self.tabview.tab("Setup"), self.store)
|
|
self.setup_tab.pack(fill="both", expand=True)
|
|
|
|
def _on_server_select(self, alias: str):
|
|
self.terminal_tab.set_server(alias)
|
|
self.files_tab.set_server(alias)
|
|
self.info_tab.set_server(alias)
|
|
self.keys_tab.set_server(alias)
|
|
|
|
def _add_server(self):
|
|
dialog = ServerDialog(self, self.store)
|
|
self.wait_window(dialog)
|
|
|
|
def _edit_server(self, alias: str):
|
|
server = self.store.get_server(alias)
|
|
if server:
|
|
dialog = ServerDialog(self, self.store, server=server)
|
|
self.wait_window(dialog)
|
|
self.info_tab.refresh()
|
|
|
|
def _delete_server(self, alias: str):
|
|
if messagebox.askyesno("Delete Server", f"Remove '{alias}'?"):
|
|
self.store.remove_server(alias)
|
|
self._on_server_select(None)
|
|
|
|
def _on_status_update(self):
|
|
self.sidebar.update_statuses()
|
|
self.info_tab.refresh()
|
|
|
|
def _on_close(self):
|
|
self.checker.stop()
|
|
self.destroy()
|