Files
server-manager/gui/app.py
chrome-storm-c442 42a6a876d3 Add Claude Code integration: shared config + Setup tab
- 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>
2026-02-23 09:01:22 -05:00

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()