diff --git a/.$dashboard.drawio.bkp b/.$dashboard.drawio.bkp new file mode 100644 index 0000000..4ca7d62 --- /dev/null +++ b/.$dashboard.drawio.bkp @@ -0,0 +1,172 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Capture d’écran du 2025-11-23 14-41-36.png b/Capture d’écran du 2025-11-23 14-41-36.png new file mode 100644 index 0000000..28491e0 Binary files /dev/null and b/Capture d’écran du 2025-11-23 14-41-36.png differ diff --git a/Capture d’écran du 2025-11-23 14-41-53.png b/Capture d’écran du 2025-11-23 14-41-53.png new file mode 100644 index 0000000..89d6370 Binary files /dev/null and b/Capture d’écran du 2025-11-23 14-41-53.png differ diff --git a/Capture d’écran du 2025-11-23 14-42-03.png b/Capture d’écran du 2025-11-23 14-42-03.png new file mode 100644 index 0000000..95ccd69 Binary files /dev/null and b/Capture d’écran du 2025-11-23 14-42-03.png differ diff --git a/README.md b/README.md index cf696fe..a190b3c 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,236 @@ -# ssh-web-launcher +# SSH Launcher +Lanceur SSH et services via une extension GNOME Shell et une application GTK. +Permet d'accéder rapidement à vos machines depuis la barre GNOME. + +## Fonctionnalités + +- Icône dans la barre supérieure GNOME +- Popup avec liste des équipements et services +- Configuration via fichier YAML +- Support SSH, HTTP, HTTPS, VNC... +- Page HTML standalone alternative + +## Architecture + +``` +┌─────────────────────────────────────────────────────────┐ +│ GNOME Shell │ +│ ┌─────────────────────────────────────────────────┐ │ +│ │ Extension (icône barre) ──► App GTK + WebKit │ │ +│ └─────────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ config/equipements.yaml │ +│ │ │ +│ ▼ │ +│ Interface HTML dynamique │ +│ │ │ +│ ▼ │ +│ Clic ──► Handler système ──► Terminal/App │ +└─────────────────────────────────────────────────────────┘ +``` + +## Structure du projet + +``` +ssh-web-launcher/ +├── config/ +│ └── equipements.yaml # Configuration des machines +├── extension/ +│ ├── metadata.json # Métadonnées extension GNOME +│ ├── extension.js # Code de l'extension +│ └── stylesheet.css # Style de l'icône +├── app/ +│ ├── ssh-launcher-gtk.py # Application GTK4 + WebKit +│ └── ssh-launcher-gtk.desktop +├── scripts/ +│ ├── ssh-url # Handler SSH +│ └── install.sh # Script d'installation +├── desktop/ +│ └── ssh-url.desktop # Handler SSH .desktop +├── index.html # Page standalone +└── README.md +``` + +## Prérequis + +- GNOME Shell 42+ +- Python 3 +- GTK3 et WebKit2 4.1 + +### Installation des dépendances (Debian/Ubuntu) + +```bash +sudo apt install python3 python3-gi python3-yaml \ + gir1.2-gtk-3.0 gir1.2-webkit2-4.1 +``` + +## Installation rapide + +```bash +./scripts/install.sh +``` + +Le script : +1. Vérifie les dépendances +2. Configure les chemins +3. Installe le handler SSH +4. Installe l'extension GNOME Shell + +### Activer l'extension + +Après installation, redémarrez GNOME Shell (Alt+F2 → `r` → Entrée) puis : + +```bash +gnome-extensions enable ssh-launcher@local +``` + +Ou utilisez l'application "Extensions" / "Extension Manager". + +## Installation manuelle + +### 1. Rendre les scripts exécutables + +```bash +chmod +x scripts/ssh-url +chmod +x scripts/install.sh +chmod +x app/ssh-launcher-gtk.py +``` + +### 2. Installer le handler SSH + +```bash +cp desktop/ssh-url.desktop ~/.local/share/applications/ +xdg-mime default ssh-url.desktop x-scheme-handler/ssh +update-desktop-database ~/.local/share/applications/ +``` + +### 3. Installer l'extension GNOME + +```bash +mkdir -p ~/.local/share/gnome-shell/extensions/ssh-launcher@local +cp extension/* ~/.local/share/gnome-shell/extensions/ssh-launcher@local/ +``` + +Redémarrez GNOME Shell et activez l'extension. + +## Configuration + +Éditez `config/equipements.yaml` pour ajouter vos machines : + +```yaml +equipements: + - nom: "Mon Serveur" + ip: "192.168.1.100" + categorie: "Serveurs" + services: + - nom: "SSH" + icon: "utilities-terminal" + url: "ssh://admin@192.168.1.100" + - nom: "Web" + icon: "web-browser" + url: "https://192.168.1.100" +``` + +### Icônes disponibles + +| Icône | Nom | +|-------|-----| +| 💻 | `utilities-terminal` | +| 🌐 | `web-browser` | +| ⚙️ | `applications-system` | +| 🖥️ | `preferences-desktop-remote-desktop` | +| 🔒 | `security-high` | +| 🏠 | `go-home` | +| 🖧 | `network-server` | + +## Test + +### Tester l'application GTK directement + +```bash +python3 app/ssh-launcher-gtk.py +``` + +### Tester le handler SSH + +```bash +./scripts/ssh-url "ssh://user@localhost" +``` + +### Tester la page standalone + +```bash +firefox index.html +``` + +## Dépannage + +### L'extension n'apparaît pas + +1. Vérifiez que GNOME Shell est redémarré +2. Vérifiez l'activation : + ```bash + gnome-extensions list + gnome-extensions enable ssh-launcher@local + ``` + +### Erreur WebKit / écran noir + +Si vous avez des erreurs GPU (GBM, DRM), l'application désactive automatiquement +l'accélération matérielle. Si le problème persiste, lancez avec : +```bash +WEBKIT_DISABLE_COMPOSITING_MODE=1 python3 app/ssh-launcher-gtk.py +``` + +### Le handler SSH ne fonctionne pas + +```bash +xdg-mime query default x-scheme-handler/ssh +# Doit retourner : ssh-url.desktop +``` + +### Logs de l'extension + +```bash +journalctl -f -o cat /usr/bin/gnome-shell +``` + +## Désinstallation + +```bash +# Extension GNOME +rm -rf ~/.local/share/gnome-shell/extensions/ssh-launcher@local + +# Applications +rm ~/.local/share/applications/ssh-url.desktop +rm ~/.local/share/applications/ssh-launcher-gtk.desktop +update-desktop-database ~/.local/share/applications/ +``` + +## Utilisation standalone (sans extension) + +La page `index.html` peut être utilisée indépendamment : + +```bash +firefox index.html +``` + +Elle nécessite uniquement le handler SSH (`ssh-url.desktop`). + +## Mini-apps + +Le dossier `app/tools/` contient des petits outils autonomes comme `color-picker` : lancez-les via + +```bash +python3 app/tools/color-picker/color_picker.py +``` + +Le script `run_color_picker.py` sert uniquement pour les essais côté navigateur (il démarre un mini-serveur local). + +Le dashboard peut aussi démarrer ces scripts via la section `minitools` (voir `config/equipements.yaml`). + +## Licence + +Libre d'utilisation et de modification. diff --git a/app/__pycache__/ssh-launcher-gtk.cpython-313.pyc b/app/__pycache__/ssh-launcher-gtk.cpython-313.pyc new file mode 100644 index 0000000..8ca88d4 Binary files /dev/null and b/app/__pycache__/ssh-launcher-gtk.cpython-313.pyc differ diff --git a/app/ssh-launcher-gtk.desktop b/app/ssh-launcher-gtk.desktop new file mode 100644 index 0000000..26ed753 --- /dev/null +++ b/app/ssh-launcher-gtk.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Name=SSH Launcher +Comment=Interface de connexion SSH et services +Exec=python3 /home/gilles/Documents/vscode/ssh-web-launcher/app/ssh-launcher-gtk.py +Icon=utilities-terminal +Type=Application +Terminal=false +Categories=Network;Utility; +Keywords=ssh;terminal;remote;connexion; diff --git a/app/ssh-launcher-gtk.py b/app/ssh-launcher-gtk.py new file mode 100755 index 0000000..4753035 --- /dev/null +++ b/app/ssh-launcher-gtk.py @@ -0,0 +1,764 @@ +#!/usr/bin/env python3 +""" +Dashboard Launcher - Application GTK3 + WebKit2 +Affiche une interface web pour lancer des connexions SSH et autres services. +""" + +import os +import sys +import yaml +import gi +import subprocess +import re +import threading + +gi.require_version('Gtk', '3.0') +gi.require_version('WebKit2', '4.1') +gi.require_version('Gdk', '3.0') + +from gi.repository import Gtk, WebKit2, Gio, Gdk, GLib +import cairo + +PID_FILE = '/tmp/dashboard-launcher.pid' +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) +PROJECT_DIR = os.path.dirname(SCRIPT_DIR) +CONFIG_FILE = os.path.join(PROJECT_DIR, 'config', 'equipements.yaml') +ICONS_DIR = os.path.join(PROJECT_DIR, 'icons') + +# Cache pour les résultats de ping +ping_results = {} + + +def ping_host(ip, timeout=1): + """Ping une IP et retourne True si accessible.""" + try: + # Nettoyer l'IP (enlever les doubles points, etc.) + clean_ip = ip.replace('..', '.') + result = subprocess.run( + ['ping', '-c', '1', '-W', str(timeout), clean_ip], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + timeout=timeout + 1 + ) + return result.returncode == 0 + except Exception: + return False + + +def ping_all_hosts(hosts, callback): + """Ping toutes les IPs en parallèle et appelle callback avec les résultats.""" + def ping_thread(ip): + result = ping_host(ip) + ping_results[ip] = result + + threads = [] + for ip in hosts: + t = threading.Thread(target=ping_thread, args=(ip,)) + t.daemon = True + t.start() + threads.append(t) + + def wait_and_callback(): + for t in threads: + t.join(timeout=2) + GLib.idle_add(callback) + + threading.Thread(target=wait_and_callback, daemon=True).start() + + +def load_config(): + """Charge la configuration depuis le fichier YAML.""" + try: + with open(CONFIG_FILE, 'r', encoding='utf-8') as f: + return yaml.safe_load(f) or {} + except (FileNotFoundError, yaml.YAMLError) as e: + print(f"Erreur config: {e}") + return {} + + +def get_icon_html(icon_name, size=32): + """Retourne le HTML pour une icône.""" + if not icon_name: + return f'🔗' + + # Chemin vers image dans icons/ + if icon_name.startswith('icons/'): + icon_path = os.path.join(PROJECT_DIR, icon_name) + if os.path.exists(icon_path): + return f'' + + # Emojis de fallback + icons = { + 'utilities-terminal': '💻', 'terminal': '💻', 'web-browser': '🌐', + 'applications-system': '⚙️', 'server': '🖥️', 'network-server': '🖧', + 'preferences-desktop-remote-desktop': '🖥️', 'security-high': '🔒', + 'go-home': '🏠', 'system-file-manager': '📁', 'firefox': '🦊', + 'google-chrome': '🌐', 'code': '📝', + } + emoji = icons.get(icon_name, icon_name if len(icon_name) <= 2 else '🔗') + return f'{emoji}' + + +def generate_html(config): + """Génère le HTML avec layout: local en haut, distant à gauche, url à droite.""" + + apparence = config.get('apparence', {}) + fenetre = config.get('fenetre', {}) + theme = apparence.get('theme', 'dark') + font_size = apparence.get('police_taille', 14) + icon_distant = apparence.get('icon_taille_distant', 48) + icon_local = apparence.get('icon_taille_local', 64) + icon_url = apparence.get('icon_taille_url', 64) + icon_minitools = apparence.get('icon_taille_minitools', 32) + show_local_labels = apparence.get('afficher_label_local', True) + border_radius = apparence.get('border_radius', 12) + espacement_local = apparence.get('espacement_local', 10) + espacement_distant = apparence.get('espacement_distant', 6) + espacement_url = apparence.get('espacement_url', 8) + espacement_minitools = apparence.get('espacement_minitools', 8) + icon_fermer = apparence.get('icon_taille_fermer', 22) + icon_parametre = apparence.get('icon_taille_parametre', 22) + icon_theme_width = apparence.get('icon_taille_theme', 60) + icon_theme_height = int(icon_theme_width * 238 / 512) # Ratio de l'image night-day.png + border_radius_local = apparence.get('border_radius_local', 12) + + # Colonnes configurables + col_url = fenetre.get('section_url', {}).get('colonne', 2) + + distant = config.get('distant', []) + local = config.get('local', []) + urls = config.get('url', []) + + # Section DISTANT avec status ping + distant_html = "" + for machine in distant: + ip = machine.get('ip', '') + nom = machine.get('nom', '') + # Vérifier le statut ping (vert si up, rouge si down, gris si inconnu) + is_up = ping_results.get(ip) + if is_up is None: + status_class = "status-unknown" + elif is_up: + status_class = "status-up" + else: + status_class = "status-down" + + services_html = "" + for service in machine.get('services', []): + icon_html = get_icon_html(service.get('icon', ''), icon_distant) + services_html += f''' + + {icon_html} + {service.get('nom', '')} + ''' + distant_html += f''' +
+
{nom}
+
{ip}
+
{services_html}
+
''' + + # Section LOCAL + local_html = "" + for app in local: + icon_html = get_icon_html(app.get('icon', ''), icon_local) + label_html = f'{app.get("nom", "")}' if show_local_labels else '' + local_html += f''' + + {icon_html} + {label_html} + ''' + + minitools = config.get('minitools', []) + minitools_html = "" + for tool in minitools: + icon_html = get_icon_html(tool.get('icon', ''), icon_minitools) + label_html = f'{tool.get("nom", "")}' if show_local_labels else '' + href = '' + if tool.get('command'): + href = f"app://run/{tool.get('command')}" + elif tool.get('url'): + href = tool.get('url') + if not href: + continue + minitools_html += f''' + + {icon_html} + {label_html} + ''' + + # Section URL + url_html = "" + for bookmark in urls: + icon_html = get_icon_html(bookmark.get('icon', ''), icon_url) + url_html += f''' + + {icon_html} + {bookmark.get('nom', '')} + ''' + + # Couleurs thème Adwaita - box = fond équipement (clair), item = fond service (foncé) + if theme == 'light': + c = {'bg': '#fafafa', 'container': '#ffffff', 'header': '#ebebeb', 'border': '#d0d0d0', + 'text': '#2e2e2e', 'text2': '#5e5e5e', 'box': '#e8e8e8', 'item': '#f5f5f5', + 'hover': '#888b8f', 'status': '#2ec27e'} + else: + c = {'bg': '#1e1e1e', 'container': '#242424', 'header': '#303030', 'border': '#1a1a1a', + 'text': '#ffffff', 'text2': '#999999', 'box': '#3d3d3d', 'item': '#2d2d2d', + 'hover': '#888b8f', 'status': '#2ec27e'} + + # Surcharge avec les couleurs personnalisées si définies + if apparence.get('couleur_fond'): + c['container'] = apparence['couleur_fond'] + if apparence.get('couleur_header'): + c['header'] = apparence['couleur_header'] + else: + c['header'] = c['container'] # Par défaut, même couleur que le fond + if apparence.get('couleur_item'): + c['item'] = apparence['couleur_item'] + if apparence.get('couleur_bordure'): + c['border'] = apparence['couleur_bordure'] + if apparence.get('couleur_hover'): + c['hover'] = apparence['couleur_hover'] + + # Couleurs séparées pour chaque section (local, distant, url) + c['box_local'] = apparence.get('couleur_box_local') or c['box'] + c['box_distant'] = apparence.get('couleur_box_distant') or c['box'] + c['box_url'] = apparence.get('couleur_box_url') or c['box'] + c['box_minitools'] = apparence.get('couleur_box_minitools') or c['box_local'] + + # Image night-day: 512x238 (ratio 2.15:1) - affichée entièrement comme toggle + + html = f''' + + + + + +
+
+
{local_html}
+
+ + + +
+
+
+
{distant_html}
+
{url_html}
+
+
{minitools_html}
+
+''' + return html + + +class DashboardLauncherWindow(Gtk.Window): + def __init__(self): + super().__init__(title="Dashboard Launcher") + self.config = load_config() + self.fenetre_config = self.config.get('fenetre', {}) + + # Activer la transparence pour éviter le flash blanc + screen = self.get_screen() + visual = screen.get_rgba_visual() + if visual: + self.set_visual(visual) + self.set_app_paintable(True) + self.connect('draw', self.on_draw) + + self.set_type_hint(Gdk.WindowTypeHint.POPUP_MENU) + self.set_skip_taskbar_hint(True) + self.set_skip_pager_hint(True) + self.set_decorated(False) # Pas de décoration pour éviter le flash + self.set_resizable(False) + + if self.fenetre_config.get('toujours_au_dessus', False): + self.set_keep_above(True) + + largeur = self.fenetre_config.get('largeur', 800) + hauteur = self.fenetre_config.get('hauteur', 450) + self.set_default_size(largeur, hauteur) + + display = Gdk.Display.get_default() + ecran_num = self.fenetre_config.get('ecran', -1) + + if ecran_num is None or ecran_num < 0: + seat = display.get_default_seat() + pointer = seat.get_pointer() + _, mouse_x, mouse_y = pointer.get_position() + monitor = display.get_monitor_at_point(mouse_x, mouse_y) + else: + n_monitors = display.get_n_monitors() + monitor = display.get_monitor(ecran_num) if ecran_num < n_monitors else display.get_primary_monitor() or display.get_monitor(0) + + geom = monitor.get_geometry() + centrer = self.fenetre_config.get('centrer', True) + x = self.fenetre_config.get('x', 0) + y = self.fenetre_config.get('y', 50) + + if centrer: + self.move(geom.x + (geom.width - largeur) // 2, geom.y + y) + else: + self.move(geom.x + x, geom.y + y) + + self.connect('key-press-event', self.on_key_press) + + settings = WebKit2.Settings() + settings.set_property('hardware-acceleration-policy', WebKit2.HardwareAccelerationPolicy.NEVER) + + self.webview = WebKit2.WebView() + self.webview.set_settings(settings) + self.webview.connect('decide-policy', self.on_decide_policy) + + # Fond transparent pour éviter le flash blanc + bg_color = Gdk.RGBA() + bg_color.red = 0 + bg_color.green = 0 + bg_color.blue = 0 + bg_color.alpha = 0 # Transparent + self.webview.set_background_color(bg_color) + + self.reload_content() + self.add(self.webview) + self.connect('destroy', self.on_destroy) + + # Lancer le ping en arrière-plan + self.ping_timer_id = None + self.autohide_timer_id = None + self.mouse_inside = True + self.start_ping_check() + + # Configurer le ping périodique + ping_interval = self.config.get('apparence', {}).get('ping_intervalle', 360) + if ping_interval > 0: + self.ping_timer_id = GLib.timeout_add_seconds(ping_interval, self.on_ping_timer) + + # Fermeture sur perte de focus (clic extérieur) + if self.fenetre_config.get('fermer_sur_clic_exterieur', True): + self.connect('focus-out-event', self.on_focus_out) + + # Autohide: fermer si souris hors fenêtre pendant X secondes + autohide_delay = self.fenetre_config.get('autohide', 0) + if autohide_delay > 0: + self.autohide_delay = autohide_delay + self.connect('enter-notify-event', self.on_mouse_enter) + self.connect('leave-notify-event', self.on_mouse_leave) + + def on_draw(self, widget, cr): + """Dessine un fond transparent.""" + cr.set_source_rgba(0, 0, 0, 0) + cr.set_operator(cairo.OPERATOR_SOURCE) + cr.paint() + return False + + def reload_content(self): + self.config = load_config() + self.webview.load_html(generate_html(self.config), 'file://') + + def start_ping_check(self): + """Lance le ping de toutes les IPs en arrière-plan.""" + distant = self.config.get('distant', []) + hosts = [m.get('ip', '') for m in distant if m.get('ip')] + ping_all_hosts(hosts, self.on_ping_complete) + + def on_ping_complete(self): + """Callback appelé quand tous les pings sont terminés.""" + self.reload_content() + + def on_ping_timer(self): + """Timer pour le ping périodique.""" + self.start_ping_check() + return True # Continuer le timer + + def on_destroy(self, widget): + """Nettoyage à la fermeture.""" + if self.ping_timer_id: + GLib.source_remove(self.ping_timer_id) + if self.autohide_timer_id: + GLib.source_remove(self.autohide_timer_id) + Gtk.main_quit() + + def on_focus_out(self, widget, event): + """Ferme la fenêtre quand elle perd le focus.""" + self.destroy() + return True + + def on_mouse_enter(self, widget, event): + """Souris entre dans la fenêtre - annuler le timer autohide.""" + self.mouse_inside = True + if self.autohide_timer_id: + GLib.source_remove(self.autohide_timer_id) + self.autohide_timer_id = None + return False + + def on_mouse_leave(self, widget, event): + """Souris quitte la fenêtre - démarrer le timer autohide.""" + self.mouse_inside = False + if self.autohide_timer_id: + GLib.source_remove(self.autohide_timer_id) + self.autohide_timer_id = GLib.timeout_add_seconds( + self.autohide_delay, self.on_autohide_timeout + ) + return False + + def on_autohide_timeout(self): + """Timer autohide expiré - fermer si souris toujours hors fenêtre.""" + if not self.mouse_inside: + self.destroy() + return False # Ne pas répéter + + def toggle_theme(self): + current = self.config.get('apparence', {}).get('theme', 'dark') + new_theme = 'light' if current == 'dark' else 'dark' + try: + with open(CONFIG_FILE, 'r', encoding='utf-8') as f: + content = f.read() + # Regex précise: uniquement "theme:" en début de ligne (après espaces) + content = re.sub(r'^(\s*)theme:\s*"?\w+"?', rf'\1theme: "{new_theme}"', content, flags=re.MULTILINE) + with open(CONFIG_FILE, 'w', encoding='utf-8') as f: + f.write(content) + self.reload_content() + except Exception as e: + print(f"Erreur thème: {e}") + + def on_key_press(self, widget, event): + if event.keyval == Gdk.KEY_Escape: + self.destroy() + return True + return False + + def on_decide_policy(self, webview, decision, decision_type): + if decision_type == WebKit2.PolicyDecisionType.NAVIGATION_ACTION: + uri = decision.get_navigation_action().get_request().get_uri() + + if uri == 'app://close': + self.destroy() + elif uri == 'app://toggle-theme': + self.toggle_theme() + elif uri == 'app://settings': + subprocess.Popen(['xdg-open', CONFIG_FILE]) + elif uri and uri.startswith('app://run/'): + subprocess.Popen(uri.replace('app://run/', '').split()) + elif uri and (uri.startswith('smb://') or uri.startswith('nfs://')): + # SMB/NFS: monter avec gio et ouvrir dans Nautilus + self.mount_and_open(uri) + elif uri and not uri.startswith('file://') and not uri.startswith('about:'): + try: + Gio.AppInfo.launch_default_for_uri(uri, None) + except Exception as e: + print(f"Erreur URI: {e}") + else: + return False + decision.ignore() + return True + return False + + def mount_and_open(self, uri): + """Monte un partage SMB/NFS avec gio et l'ouvre dans Nautilus.""" + def open_nautilus(): + subprocess.Popen(['nautilus', uri]) + return False # Ne pas répéter + + def mount_thread(): + try: + # Monter le partage avec gio mount + subprocess.run( + ['gio', 'mount', uri], + capture_output=True, + text=True, + timeout=30 + ) + except subprocess.TimeoutExpired: + print(f"Timeout montage: {uri}") + except Exception as e: + print(f"Erreur montage {uri}: {e}") + finally: + # Ouvrir dans Nautilus une seule fois + GLib.idle_add(open_nautilus) + + # Lancer le montage en arrière-plan pour ne pas bloquer l'UI + threading.Thread(target=mount_thread, daemon=True).start() + + +def is_running(): + if os.path.exists(PID_FILE): + try: + with open(PID_FILE, 'r') as f: + pid = int(f.read().strip()) + os.kill(pid, 0) + return pid + except (OSError, ValueError): + pass + return None + + +def main(): + if '--toggle' in sys.argv and is_running(): + import signal + os.kill(is_running(), signal.SIGTERM) + if os.path.exists(PID_FILE): + os.remove(PID_FILE) + sys.exit(0) + + if is_running(): + print(f"Dashboard déjà en cours") + sys.exit(0) + + os.environ['WEBKIT_DISABLE_COMPOSITING_MODE'] = '1' + + with open(PID_FILE, 'w') as f: + f.write(str(os.getpid())) + + import atexit + atexit.register(lambda: os.path.exists(PID_FILE) and os.remove(PID_FILE)) + + win = DashboardLauncherWindow() + win.show_all() + Gtk.main() + + +if __name__ == '__main__': + main() diff --git a/app/tools/color-picker/DEVELOPMENT_PROMPT.md b/app/tools/color-picker/DEVELOPMENT_PROMPT.md new file mode 100644 index 0000000..311a982 --- /dev/null +++ b/app/tools/color-picker/DEVELOPMENT_PROMPT.md @@ -0,0 +1,45 @@ +# Prompt de développement – Color Picker mini-app + +## Analyse du besoin +- Interface compacte avec roue chromatique, zone palette, volet latéral repliable "Theme CSS" affichant styles/typo. +- Actions attendues : sélectionner une couleur, afficher sa complémentaire, copier les codes HEX/RGB, afficher aperçu (pipette), streamer palettes CSS/typos stockées. +- Stockage : dossiers `themes/` et `palettes/` pour y déposer JSON/CSS réutilisables. +- Intégration : doit pouvoir être lancé depuis la barre mini-tools du dashboard et cohabiter avec d'autres mini-apps. + +## Proposition d'architecture +1. Frontend web léger (HTML/CSS/JS) rendu via Tauri/Electron ou WebView GTK, pour un look moderne. +2. Backend minimal (Node.js ou Python) servant les fichiers statiques et exposant une API (ex. `GET /themes`, `GET /palettes`). +3. Composants clés : + - Colonne droite : palette avec carte couleur + bouton copier + couleur complémentaire. + - Centre : roue chromatique interactive + pipette et aperçu couleur sélectionnée. + - Volet gauche repliable : aperçu "Theme CSS" (fond, typographies, boutons) chargé depuis `themes/`. + - Barre inférieure : actions rapides (copier, exporter en CSS, ouvrir palette). +4. Données : + - `palettes/*.json` (nom, description, code couleur, tags, langage associé). + - `themes/*.css` (aperçu d'un thème complet plus métadonnées). + +## Choix techniques +- **Langage** : JavaScript/TypeScript + Svelte ou Vue pour agile; empaquetage via Tauri pour faible empreinte. Alternativement, Python + GTK si l’environnement cible préfère GTK. +- **UI** : `chroma.js` ou `tinycolor` pour la palette/roue et le calcul des complémentaires, Web APIs pour la pipette (canvas + input color). +- **Données** : JSON/YAML stockés dans `app/tools/color-picker/` et chargés dynamiquement côté front. +- **Lancement** : un script `scripts/color-picker.sh` ou un mini-wrapper Python qui ouvre la WebView / Tauri. + +## Plan +1. Créer la structure `app/tools/color-picker/{src, themes, palettes, assets}`. +2. Initialiser un petit serveur `main.ts` ou `main.py` (selon stack) pour servir l’interface et exposer les APIs. +3. Développer l’UI (roue, volet thèmes, palettes, pipette) avec interactions décrites. +4. Charger dynamiquement `palettes/*.json` et `themes/*.css`, proposer un panneau pour ajouter/modifier. +5. Ajouter boutons “copier” qui placent HEX/RGB dans le presse-papier. +6. Documenter le lancement et l’intégration dans `config/equipements.yaml` via mini-tool. + +## Tests +- Test manuel : lancer l’app (via Tauri ou un script) et valider roue réactive, volet dépliable, copie de couleur. +- Tests unitaires (JS) sur le parseur de palettes et les calculs de couleurs (complémentaire, contraste). + +## TODO +1. Définir format palette/metadata. +2. Créer jeux de palettes (Monokai, Solarized, thèmes web populaires). +3. Implémenter pipette + copie automatique des codes. +4. Ajouter vue “Theme CSS” avec CSS/Font preview. +5. Préparer commande de lancement utilisable dans `minitools`. +6. Documenter la mini-app dans `README`. diff --git a/app/tools/color-picker/README.md b/app/tools/color-picker/README.md new file mode 100644 index 0000000..9ed3d22 --- /dev/null +++ b/app/tools/color-picker/README.md @@ -0,0 +1,22 @@ +# Color Picker Mini-App + +Cette mini-application fournit une roue chromatique, des palettes prédéfinies, un volet "Theme CSS" et la capacité de copier des couleurs (HEX / complémentaire). + +## Structure +- `run_color_picker.py` : lance un serveur HTTP local (`127.0.0.1:9005`) et ouvre le navigateur sur `src/index.html`. +- `src/` : interface brutes (`index.html`, `style.css`, `app.js`). +- `palettes/` : fichiers JSON décrivant les palettes disponibles. +- `themes/` : thèmes CSS et manifeste (`themes.json`). +- `assets/` : placeholders pour icônes, images, etc. + +## Développement +1. Mettre à jour `palettes/*.json` pour ajouter de nouvelles palettes ou variantes. +2. Ajouter un thème CSS dans `themes/` puis l’enregistrer dans `themes/themes.json` pour qu’il apparaisse dans la liste. +3. Modifier `src/app.js` pour ajouter des interactions (pipette, export CSS, etc.). +4. Lancer l’outil avec : + +```bash +python3 app/tools/color-picker/run_color_picker.py +``` + +Une fois prêt, créez une entrée `minitools` dans `config/equipements.yaml` qui lance ce script (par exemple `command: "python3 /home/.../run_color_picker.py"`). diff --git a/app/tools/color-picker/color_picker.py b/app/tools/color-picker/color_picker.py new file mode 100755 index 0000000..dec8307 --- /dev/null +++ b/app/tools/color-picker/color_picker.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +"""Popup GTK qui affiche l'interface Color Picker via WebKit""" +import os +import sys +from pathlib import Path + +import gi + +# for systems blocking direct GBM access we force WebKit to stay in software mode +os.environ.setdefault('WEBKIT_DISABLE_COMPOSITING_MODE', '1') +os.environ.setdefault('GDK_BACKEND', 'x11') + +gi.require_version('Gtk', '3.0') +gi.require_version('WebKit2', '4.1') +from gi.repository import Gtk, WebKit2, GLib + +SCRIPT_DIR = Path(__file__).resolve().parent +SRC_FILE = SCRIPT_DIR / 'src' / 'index.html' + + +class ColorPickerWindow(Gtk.Window): + def __init__(self): + super().__init__(title='Color Picker') + self.set_default_size(980, 640) + self.set_position(Gtk.WindowPosition.CENTER) + self.set_border_width(8) + self.set_resizable(True) + + settings = WebKit2.Settings() + settings.set_property('enable-developer-extras', False) + settings.set_property('enable-accelerated-2d-canvas', False) + + self.webview = WebKit2.WebView() + self.webview.set_settings(settings) + self.add(self.webview) + + uri = GLib.filename_to_uri(str(SRC_FILE), None) + self.webview.load_uri(uri) + + self.connect('destroy', Gtk.main_quit) + self.show_all() + + +def main() -> None: + if not SRC_FILE.exists(): + print(f'Fichier introuvable : {SRC_FILE}', file=sys.stderr) + sys.exit(1) + win = ColorPickerWindow() + Gtk.main() + + +if __name__ == '__main__': + main() diff --git a/app/tools/color-picker/palettes/default.json b/app/tools/color-picker/palettes/default.json new file mode 100644 index 0000000..f3176c2 --- /dev/null +++ b/app/tools/color-picker/palettes/default.json @@ -0,0 +1,47 @@ +{ + "palettes": [ + { + "name": "Monokai Pro", + "description": "Palette chaude / turquoise", + "language": "CSS", + "colors": [ + "#f8f8f2", + "#f92672", + "#fd971f", + "#a6e22e", + "#66d9ef", + "#9effff", + "#ae81ff", + "#ffffff" + ] + }, + { + "name": "Solarized", + "description": "Teintes douces pour coding", + "language": "SCSS", + "colors": [ + "#002b36", + "#073642", + "#586e75", + "#657b83", + "#839496", + "#b58900", + "#cb4b16", + "#dc322f" + ] + }, + { + "name": "Material", + "description": "Couleurs saturées modernes", + "language": "CSS", + "colors": [ + "#0f9d58", + "#f4b400", + "#4285f4", + "#db4437", + "#ab47bc", + "#00acc1" + ] + } + ] +} diff --git a/app/tools/color-picker/run_color_picker.py b/app/tools/color-picker/run_color_picker.py new file mode 100755 index 0000000..e593b7a --- /dev/null +++ b/app/tools/color-picker/run_color_picker.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +"""Serve l'interface Color Picker depuis un mini-serveur HTTP local.""" +import http.server +import socketserver +import threading +import webbrowser +from pathlib import Path +import signal +import os + +PORT = int(os.environ.get('COLOR_PICKER_PORT', '9005')) +BASE_DIR = Path(__file__).resolve().parent +SRC_DIR = BASE_DIR / 'src' + +class ColorRequestHandler(http.server.SimpleHTTPRequestHandler): + def __init__(self, *args, **kwargs): + super().__init__(*args, directory=str(BASE_DIR), **kwargs) + + +def main() -> None: + with socketserver.TCPServer(('127.0.0.1', PORT), ColorRequestHandler) as httpd: + url = f'http://127.0.0.1:{PORT}/src/index.html' + print(f'Color Picker disponible sur {url}') + threading.Timer(0.3, lambda: webbrowser.open(url)).start() + + def stop(signum, frame): + print('Arrêt du serveur Color Picker...') + httpd.shutdown() + + signal.signal(signal.SIGINT, stop) + signal.signal(signal.SIGTERM, stop) + try: + httpd.serve_forever() + except KeyboardInterrupt: + pass + + +if __name__ == '__main__': + main() diff --git a/app/tools/color-picker/src/app.js b/app/tools/color-picker/src/app.js new file mode 100644 index 0000000..e6c1e27 --- /dev/null +++ b/app/tools/color-picker/src/app.js @@ -0,0 +1,159 @@ +const colorInput = document.getElementById('color-input'); +const selectedHexNode = document.getElementById('selected-hex'); +const complementHexNode = document.getElementById('complement-hex'); +const selectedColorBox = document.getElementById('selected-color'); +const complementColorBox = document.getElementById('complement-color'); +const paletteContainer = document.getElementById('palette-container'); +const themeSelector = document.getElementById('theme-selector'); +const themePreview = document.getElementById('theme-preview-content'); +const themeDescription = document.getElementById('theme-description'); + +const paletteEndpoint = '../palettes/default.json'; +const themesEndpoint = '../themes/themes.json'; +let palettes = []; +let themes = []; + +async function fetchPalettes() { + try { + const response = await fetch(paletteEndpoint); + const data = await response.json(); + palettes = data.palettes || []; + renderPalettes(palettes); + } catch (err) { + paletteContainer.textContent = 'Impossible de charger les palettes.'; + console.error(err); + } +} + +async function fetchThemes() { + try { + const response = await fetch(themesEndpoint); + const data = await response.json(); + themes = data.themes || []; + populateThemeSelector(themes); + } catch (err) { + themeDescription.textContent = 'Erreur lors du chargement des thèmes.'; + console.error(err); + } +} + +function populateThemeSelector(themeList) { + themeSelector.innerHTML = ''; + themeList.forEach((theme, index) => { + const option = document.createElement('option'); + option.value = theme.file; + option.textContent = theme.name; + themeSelector.appendChild(option); + if (index === 0) { + loadTheme(theme); + } + }); +} + +async function loadTheme(theme) { + try { + const response = await fetch(`../themes/${theme.file}`); + const content = await response.text(); + themeDescription.textContent = theme.description || 'Aperçu CSS interactif.'; + themePreview.textContent = content.trim(); + } catch (err) { + themePreview.textContent = 'Impossible de charger le fichier.'; + console.error(err); + } +} + +function renderPalettes(list) { + paletteContainer.innerHTML = ''; + list.forEach(palette => { + const card = document.createElement('article'); + card.className = 'palette-card'; + card.innerHTML = ` +
+

${palette.name}

+ ${palette.description} +
+
+ + `; + const colorGrid = card.querySelector('.palette-colors'); + palette.colors.forEach(color => { + const swatch = document.createElement('span'); + swatch.style.background = color; + swatch.dataset.hex = color; + swatch.title = color; + swatch.addEventListener('click', () => updateColor(color)); + colorGrid.appendChild(swatch); + }); + const copyButton = card.querySelector('button'); + copyButton.addEventListener('click', () => { + const text = palette.colors.join(', '); + copyToClipboard(text, copyButton); + }); + paletteContainer.appendChild(card); + }); +} + +function updateColor(hex) { + const normalized = hex.startsWith('#') ? hex : `#${hex}`; + colorInput.value = normalized; + selectedHexNode.textContent = normalized; + selectedColorBox.style.background = normalized; + const complement = getComplement(normalized); + complementColorBox.style.background = complement; + complementHexNode.textContent = complement; +} + +function getComplement(hex) { + const r = parseInt(hex.slice(1, 3), 16); + const g = parseInt(hex.slice(3, 5), 16); + const b = parseInt(hex.slice(5, 7), 16); + const complement = [255 - r, 255 - g, 255 - b] + .map(value => value.toString(16).padStart(2, '0')) + .join(''); + return `#${complement}`; +} + +function copyToClipboard(text, button) { + navigator.clipboard.writeText(text).then(() => { + if (button) { + const original = button.dataset.origText || button.textContent; + button.dataset.origText = original; + button.textContent = 'Copié'; + button.classList.add('copied'); + setTimeout(() => { + button.textContent = original; + button.classList.remove('copied'); + }, 1200); + } + }).catch(err => { + console.error('Impossible de copier', err); + }); +} + +colorInput.addEventListener('input', (event) => { + updateColor(event.target.value); +}); + +document.querySelectorAll('button[data-copy]').forEach(button => { + button.addEventListener('click', () => { + const target = button.dataset.copy; + const text = target === 'selected' ? selectedHexNode.textContent : complementHexNode.textContent; + copyToClipboard(text, button); + }); +}); + +themeSelector.addEventListener('change', () => { + const theme = themes.find(t => t.file === themeSelector.value); + if (theme) { + loadTheme(theme); + } +}); + +const themePanel = document.getElementById('theme-panel'); +document.getElementById('theme-toggle').addEventListener('click', () => { + themePanel.classList.toggle('collapsed'); +}); + +fetchPalettes(); +fetchThemes(); +updateColor(colorInput.value); diff --git a/app/tools/color-picker/src/index.html b/app/tools/color-picker/src/index.html new file mode 100644 index 0000000..48eaf87 --- /dev/null +++ b/app/tools/color-picker/src/index.html @@ -0,0 +1,72 @@ + + + + + + Color Picker + + + +
+ +
+
+
+

Color Picker

+

Roue chromatique, palettes et CSS rapide.

+
+
+ + + +
+
+
+ +
+ + +
+
+
+
+ + +
+
+
+
+

Palettes web

+

Chaque palette contient un ensemble de couleurs recommandées et leur complément.

+
+
+
+
+
+ + + diff --git a/app/tools/color-picker/src/style.css b/app/tools/color-picker/src/style.css new file mode 100644 index 0000000..5b5f478 --- /dev/null +++ b/app/tools/color-picker/src/style.css @@ -0,0 +1,294 @@ +:root { + color-scheme: dark; +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + min-height: 100vh; + font-family: 'Inter', 'Segoe UI', sans-serif; + background: radial-gradient(circle at top, #1a1a1a, #050505 75%); + color: #f6f6f6; +} + +.app-shell { + display: grid; + grid-template-columns: 260px auto; + gap: 14px; + padding: 18px; + min-height: 100vh; +} + +.theme-panel { + background: rgba(30, 30, 30, 0.95); + border: 1px solid #2f2f2f; + border-radius: 18px; + display: flex; + flex-direction: column; + padding: 14px; + transition: transform 200ms ease; + box-shadow: 0 20px 45px rgba(0, 0, 0, 0.55); +} + +.theme-panel.collapsed { + transform: translateX(-228px); +} + +.theme-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 12px; +} + +.theme-header button { + background: transparent; + border: 1px solid #3d3d3d; + color: #f6f6f6; + border-radius: 12px; + width: 36px; + height: 36px; + cursor: pointer; +} + +.theme-header select { + background: #1c1f2b; + border: 1px solid #323248; + border-radius: 8px; + color: #f6f6f6; + padding: 6px 10px; +} + +.theme-preview { + flex: 1; + background: linear-gradient(145deg, #1f1f28, #14141b); + border-radius: 12px; + border: 1px solid #353545; + padding: 10px; + overflow: auto; +} + +.theme-preview pre { + font-size: 0.8rem; + white-space: pre-wrap; +} + +.workspace { + background: rgba(15, 15, 24, 0.92); + border-radius: 28px; + border: 1px solid #1d1f38; + padding: 22px; + display: flex; + flex-direction: column; + gap: 18px; + box-shadow: 0 28px 45px rgba(6, 7, 32, 0.65); +} + +.workspace-header { + display: flex; + justify-content: space-between; + align-items: center; + color: #f1f4ff; +} + +.workspace-header h1 { + margin: 0; + font-size: 1.5rem; +} + +.workspace-header p { + margin: 4px 0 0; + color: #a9aacf; +} + +.window-actions button { + background: #26273a; + border: 1px solid #3d3d3d; + color: #fff; + width: 32px; + height: 32px; + margin-left: 6px; + border-radius: 8px; + cursor: pointer; +} + +.color-panel { + display: flex; + gap: 32px; + align-items: center; + background: rgba(20, 20, 32, 0.95); + border-radius: 22px; + border: 1px solid #2f354f; + padding: 18px; + box-shadow: inset 0 0 60px rgba(0, 0, 0, 0.35); +} + +.color-wheel { + width: 220px; + height: 220px; + border-radius: 50%; + border: 5px solid rgba(255, 255, 255, 0.12); + background: conic-gradient( + #f94144, + #f3722c, + #f8961e, + #f9c74f, + #90be6d, + #43aa8b, + #4d908e, + #577590, + #277da1, + #4b4bfb, + #7209b7, + #f94144); + box-shadow: 0 16px 40px rgba(0, 0, 0, 0.5); +} + +.color-info { + flex: 1; + background: rgba(16, 16, 24, 0.92); + border-radius: 22px; + border: 1px solid #303046; + padding: 18px; + display: flex; + flex-direction: column; + gap: 14px; +} + +.section-label { + text-transform: uppercase; + letter-spacing: 0.2em; + font-size: 0.7rem; + color: #a3a7bc; + margin: 0; +} + +.color-info input[type='color'] { + width: 100%; + height: 56px; + border-radius: 16px; + border: none; + cursor: pointer; +} + +.color-swatch-row { + display: flex; + gap: 12px; +} + +.color-box.large { + flex: 1; + height: 60px; + border-radius: 16px; + border: 1px solid #373a4f; + box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.6); +} + +.color-metadata { + display: flex; + justify-content: space-between; + align-items: center; + font-size: 0.85rem; +} + +.color-metadata span { + font-size: 0.6rem; + letter-spacing: 0.15em; + text-transform: uppercase; + color: #7c81ad; +} + +.color-metadata strong { + margin-left: 8px; + font-size: 1.1rem; +} + +.color-metadata button { + background: #2f72ff; + border: none; + border-radius: 8px; + color: #fff; + padding: 6px 12px; + cursor: pointer; + font-size: 0.72rem; +} +.color-metadata button.copied, +.palette-card button.copied { + background: #4ccc97; + color: #0b2a1c; +} + +.palette-section { + background: rgba(15, 15, 20, 0.9); + border-radius: 24px; + padding: 22px 18px 18px; + border: 1px solid #27293b; +} + +.palette-section header { + margin-bottom: 22px; +} + +.palette-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); + gap: 12px; +} + +.palette-card { + background: rgba(20, 20, 30, 0.9); + border-radius: 18px; + border: 1px solid #30305c; + padding: 12px; + display: flex; + flex-direction: column; + gap: 10px; +} + +.palette-card h3 { + margin: 0; + font-size: 1rem; + text-transform: uppercase; + letter-spacing: 0.04em; +} + +.palette-card .palette-colors { + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: 6px; +} + +.palette-card .palette-colors span { + width: 100%; + padding-top: 100%; + border-radius: 10px; + border: 1px solid #1b1b25; +} + +.palette-card small { + color: #9ea1c6; +} + +.palette-card button { + border: none; + background: transparent; + color: #54f0b1; + cursor: pointer; + padding: 0; + font-size: 0.8rem; + text-transform: uppercase; + letter-spacing: 0.08em; +} + +@media (max-width: 960px) { + .app-shell { + grid-template-columns: 1fr; + } + + .theme-panel { + order: 2; + } +} diff --git a/app/tools/color-picker/themes/monokai.css b/app/tools/color-picker/themes/monokai.css new file mode 100644 index 0000000..212dc75 --- /dev/null +++ b/app/tools/color-picker/themes/monokai.css @@ -0,0 +1,16 @@ +:root { + --bg: #272822; + --fg: #f8f8f2; + --primary: #f92672; + --success: #a6e22e; +} +body { + background: var(--bg); + color: var(--fg); + font-family: 'Fira Code', 'JetBrains Mono', monospace; +} +button { + background: var(--primary); + color: var(--fg); + border: none; +} diff --git a/app/tools/color-picker/themes/solarized-light.css b/app/tools/color-picker/themes/solarized-light.css new file mode 100644 index 0000000..c4dab0c --- /dev/null +++ b/app/tools/color-picker/themes/solarized-light.css @@ -0,0 +1,13 @@ +:root { + --bg: #fdf6e3; + --fg: #657b83; + --primary: #268bd2; + --accent: #2aa198; +} +body { + background: var(--bg); + color: var(--fg); +} +header, .palette-card { + border-color: var(--accent); +} diff --git a/app/tools/color-picker/themes/themes.json b/app/tools/color-picker/themes/themes.json new file mode 100644 index 0000000..b28e288 --- /dev/null +++ b/app/tools/color-picker/themes/themes.json @@ -0,0 +1,16 @@ +{ + "themes": [ + { + "name": "Monokai", + "file": "monokai.css", + "description": "Palette sombre inspirée de Monokai", + "type": ".css" + }, + { + "name": "Solarized Light", + "file": "solarized-light.css", + "description": "Theme clair Solarized", + "type": ".css" + } + ] +} diff --git a/claude_prompt_ssh_app.md b/claude_prompt_ssh_app.md new file mode 100644 index 0000000..c3b0b4e --- /dev/null +++ b/claude_prompt_ssh_app.md @@ -0,0 +1,106 @@ +# Projet : Lanceur SSH via page web + +## Rôle de l’IA + +Tu es un expert en : +- Linux (bureau + ligne de commande) +- Intégration d’URL handlers (`x-scheme-handler`) +- Développement web simple (HTML/CSS basique) +- Organisation de projet et documentation (`README.md`) + +Tu m’aides à développer une petite application qui permet : +1. D’afficher une page HTML avec une liste de machines (PC, serveurs, etc.). +2. Pour chaque machine, un lien de type `ssh://user@ip` sur lequel je clique depuis le navigateur. +3. Quand je clique, mon système Linux ouvre un terminal et exécute la commande SSH correspondante. + +## Objectif fonctionnel + +- Je veux pouvoir ouvrir un fichier HTML dans mon navigateur (Firefox/Chrome). +- Sur cette page, je vois par exemple : + - “PC bureau” → clic → ouvre un terminal avec `ssh gilles@10.0.0.24` + - “Serveur Proxmox” → clic → ouvre un terminal avec `ssh root@10.0.0.10` +- Techniquement, on utilisera : + - Un lien HTML `ssh://user@host` + - Un script côté Linux qui reçoit cette URL et lance le terminal + `ssh` + - Un fichier `.desktop` qui déclare le handler `x-scheme-handler/ssh` + +## Plateforme cible + +- OS : Linux (bureau, type Debian/Ubuntu ou équivalent) +- Environnement : navigateur web + terminal graphique (gnome-terminal, xfce4-terminal, konsole, etc.) +- Éditeur : VS Code avec Claude Code + +## Contraintes / préférences + +- Solution simple, sans serveur web complexe si possible : + - Une page HTML statique (`index.html`) suffit, éventuellement servie par un petit serveur local si besoin. +- Le handler SSH doit : + - Prendre en entrée une URL de type `ssh://gilles@10.0.0.24` + - Extraire `gilles@10.0.0.24` + - Ouvrir un terminal graphique et lancer `ssh gilles@10.0.0.24` +- Le projet doit contenir une documentation claire dans `README.md` : + - But du projet + - Pré-requis + - Installation / configuration du handler `ssh://` + - Exemple d’utilisation + - Comment ajouter/modifier des machines dans la page HTML + +## Architecture souhaitée du projet + +Propose et mets en place une structure simple, par exemple : + +- `index.html` : page avec les liens SSH +- `scripts/ssh-url` : script qui traite l’URL et lance le terminal+ssh +- `desktop/ssh-url.desktop` : fichier `.desktop` déclarant le handler `x-scheme-handler/ssh` +- `README.md` : documentation du projet +- (optionnel) `config/machines.yaml` ou `.json` si on veut générer la page HTML à partir d’une config + +Tu peux ajuster la structure si tu vois plus logique, mais garde quelque chose de simple et lisible. + +## Tâches à réaliser par l’IA + +1. **Conception** + - Décrire rapidement l’architecture retenue (fichiers, rôles, flux). + - Expliquer comment le navigateur → handler `ssh://` → script → terminal → ssh s’enchaînent. + +2. **Implémentation** + - Écrire : + - La page `index.html` avec quelques exemples de liens (`ssh://gilles@10.0.0.24`, etc.). + - Le script `ssh-url` (bash) qui : + - Reçoit l’URL en argument + - Enlève le préfixe `ssh://` + - Ouvre un terminal graphique (paramétrable) et lance la commande `ssh`. + - Le fichier `.desktop` `ssh-url.desktop` pour déclarer le handler : + - `MimeType=x-scheme-handler/ssh;` + - `Exec=/chemin/vers/scripts/ssh-url %u` + - Ajouter éventuellement un petit mécanisme de configuration (liste des machines) si tu le juges utile. + +3. **Instructions d’installation** + - Rédiger un `README.md` avec : + - Étapes d’installation détaillées + - Commandes à lancer (par ex. `chmod +x scripts/ssh-url`, `xdg-mime default ssh-url.desktop x-scheme-handler/ssh`, etc.) + - Comment tester : ouvrir `index.html` et cliquer sur un lien. + +4. **Portabilité** + - Expliquer clairement où je dois adapter : + - Le nom du terminal (`gnome-terminal`, `xfce4-terminal`, etc.) + - Les chemins dans `ssh-url.desktop` + - Les IP / users dans les liens `ssh://`. + +## Style de réponse attendu + +- Toujours proposer un **plan** avant de commencer les modifications de fichiers. +- Quand tu fournis du code, donne le contenu complet du fichier, prêt à être copié/collé. +- Utiliser du français clair, concis, avec des blocs de code bien séparés. + +## Commandes que j’utiliserai avec toi + +Je pourrai t’envoyer des messages de ce type : + +- `PLAN:` → tu proposes ou mets à jour le plan de travail. +- `CREATE:` → tu crées les fichiers manquants ou les versions complètes. +- `UPDATE:` → tu modifies les fichiers existants que je te montre. +- `DOC:` → tu améliores ou complètes la documentation (`README.md`). + +Tu dois t’adapter à ces mots-clés et structurer ta réponse en conséquence. + diff --git a/config/equipements.yaml b/config/equipements.yaml new file mode 100644 index 0000000..2109d0c --- /dev/null +++ b/config/equipements.yaml @@ -0,0 +1,330 @@ +# Configuration des équipements et services +# Modifiez ce fichier pour ajouter vos machines + +# ============================================================================= +# Configuration de la fenêtre popup +# ============================================================================= +fenetre: + ecran: 0 # 0 = principal, 1 = secondaire, -1 = souris + centrer: false # Centrer horizontalement + #alignement: # center,left,right + #decalage_x: # decalage par rapport a l'alignement >0 pour left et center et <0 pour right + #decalage_y: # decalage par rapport a la barre du haut + x: 1500 # Position X (si centrer: false) + y: 0 # Position Y depuis le haut + largeur: 1100 + hauteur: 820 + fermer_sur_clic_exterieur: true + toujours_au_dessus: false + autohide: 1 # Fermer après X secondes si souris hors fenêtre (0 = désactivé) + section_distant: + colonne: 4 + section_local: + colonne: 6 + section_url: + colonne: 5 + +# ============================================================================= +# Apparence +# ============================================================================= +apparence: + theme: "dark" # "dark" ou "light" + police_taille: 14 # Taille de police en pixels + icon_taille_distant: 48 # Taille icônes section Distant + icon_taille_local: 52 # Taille icônes section Local + icon_taille_url: 48 # Taille icônes section URL + afficher_label_local: false # Afficher le texte sous les icônes locales + ping_intervalle: 360 # Intervalle entre les pings en secondes (0 = désactivé) + border_radius: 25 # Arrondi des coins de la fenêtre en pixels + espacement_local: 2 # Espacement entre icônes section Local (px) + espacement_distant: 6 # Espacement entre icônes section Distant (px) + espacement_url: 6 # Espacement entre icônes section URL (px) + icon_taille_fermer: 22 # Taille icône fermer (px) + icon_taille_parametre: 22 # Taille icône paramètre (px) + icon_taille_theme: 30 # Largeur icône night-day (px) - hauteur auto selon ratio + border_radius_local: 12 # Arrondi de la section locale (px) + # Couleurs personnalisées (laisser vide pour utiliser le thème par défaut) + couleur_fond: "#2a2a2a" # Fond principal (ex: "#1e1e1e") + couleur_header: "#2a2a2a" # Fond du header (ex: "#2a2a2a") + couleur_box_local: "#2a2a2a" # Fond section LOCAL (ex: "#3d3d3d") + couleur_box_distant: "#1e1e1e" # Fond boxes DISTANT (ex: "#3d3d3d") + couleur_box_url: "#3d3d3d" # Fond section URL (ex: "#3d3d3d") + couleur_item: "#1e1e1e" # Fond des items services (ex: "#2d2d2d") + couleur_bordure: "#111a1a" # Couleur des bordures (ex: "#1a1a1a") + couleur_hover: "#515555" # Couleur au survol souris (ex: "#555555") + icon_taille_minitools: 52 # Taille des icônes de la barre mini-tools + espacement_minitools: 6 # Espacement entre les mini outils (px) + couleur_box_minitools: "#252525" # Fond de la barre mini tools (optionnel) + +# ============================================================================= +# Section DISTANT - Machines distantes (à gauche) +# ============================================================================= +distant: + - ip: "10.0.0.24" + nom: "PC Dashboard" + services: + - nom: "SSH" + icon: "icons/ssh.png" + url: "ssh://gilles@10.0.0.24" + - nom: "VNC" + icon: "icons/terminal.png" + url: "vnc://10.0.0.24:5900" + + - ip: "10.0.1.232" + nom: "m710q" + services: + - nom: "proxmox" + icon: "icons/proxmox.png" + url: "https://10.0.1.232:8006" + - nom: "SSH" + icon: "icons/ssh.png" + url: "ssh://root@10.0.1.232" + + - ip: "10.0.0.101" + nom: "EliteDesk" + services: + - nom: "SSH" + icon: "icons/ssh.png" + url: "ssh://root@10.0.0.101" + - nom: "Proxmox" + icon: "icons/proxmox.png" + url: "https://10.0.0.101:8006/" + + - ip: "10.0.0.5" + nom: "VM 5" + services: + - nom: "SSH" + icon: "icons/ssh.png" + url: "ssh://gilles@10.0.0.5" + password: "gilles" #copy password to clipboard + - nom: "Gitea" + icon: "icons/gitea.png" + url: "https://gitea.maison43.duckdns.org/" + - nom: "Arcane" + icon: "icons/arcane.png" + url: "http://10.0.0.5:3552/" + - nom: "Hortus_fox" + icon: "icons/hortusfox.png" + url: "http://10.0.0.5:8081/" + - nom: "Termix" + icon: "icons/termix.png" + url: "http://10.0.0.5:6080/" + - nom: "upsnap" + icon: "icons/upsnap.png" + url: "http://10.0.0.5:8090/" + - nom: "samba" # automount samba in nautilus and open in nautilus + icon: "icons/smb.png" + url: "smb://10.0.0.5/" + user: gilles #user for samba + password: gilles #password for samba + + + + - ip: "137.74.45.99" + nom: "Vps OVH" + services: + - nom: "SSH" + icon: "icons/ssh.png" + url: "ssh://debian@137.74.45.99" + password: "Misstibet5*" #copy password to clipboard + - nom: "VNC" + icon: "icons/terminal.png" + url: "vnc://137.74.45.99:5901" + + - ip: "10.0.0.2" + nom: "Home Assistant" + services: + - nom: "Home Assistant" + icon: "icons/home-assistant.png" + url: "http://10.0.0.2:8123" + + - ip: "10.0.0.205" + nom: "Proliant" + services: + - nom: "Proxmox" + icon: "icons/proxmox.png" + url: "https://10.0.0.205:8006/" + + - ip: "10.0.0.15" + nom: "Adguard" + services: + - nom: "Adguard" + icon: "icons/adguard-home.png" + url: "http://10.0.0.15/" + + - ip: "10.0.0.135" + nom: "Paperless" + services: + - nom: "Paperless" + icon: "icons/paperless.png" + url: "http://10.0.0.135:8777/" + - nom: "scanServ.js" + icon: "icons/scanservjs.png" + url: "http://10.0.0.135:8080/" + - nom: "SSH" + icon: "icons/ssh.png" + url: "ssh://gilles@10.0.0.135" + password: "gilles" #copy password to clipboard + + - ip: "10.0.0.116" + nom: "VM 116" + services: + - nom: "Arcane" + icon: "icons/arcane.png" + url: "http://10.0.0.116:3552/" + - nom: "Heimdall" + icon: "icons/heimdall.png" + url: "http://10.0.0.135:8002/" + - nom: "SSH" + icon: "icons/ssh.png" + url: "ssh://gilles@10.0.0.116" + password: "gilles" #copy password to clipboard + +# ============================================================================= +# Section LOCAL - Applications locales (en bas) +# ============================================================================= +local: + - nom: "Fichiers" + icon: "icons/nautilus.png" + command: "nautilus" + - nom: "Terminal" + icon: "icons/terminal.png" + command: "gnome-terminal" + - nom: "Firefox" + icon: "icons/firefox.png" + command: "firefox" + - nom: "Chrome" + icon: "icons/chrome.png" + command: "google-chrome" + - nom: "VS Code" + icon: "icons/vscode.png" + command: "code" + - nom: "Calculatrice" + icon: "icons/calculatrice.png" + command: "gnome-calculator" + - nom: "Gnumeric" + icon: "icons/gnumeric.png" + command: "gnumeric" + - nom: "Moniteur system" + icon: "icons/gparameter.png" + command: "gnome-control-center" + - nom: "Parametres" + icon: "icons/monitor.png" + command: "gnome-system-monitor" + +# ============================================================================= +# Section URL - Favoris web (à droite) +# ============================================================================= +url: + - nom: "Google" + icon: "icons/google.png" + url: "https://www.google.com/" + - nom: "Gmail" + icon: "icons/gmail.png" + url: "https://mail.google.com" + - nom: "YouTube" + icon: "icons/youtube.png" + url: "https://youtube.com" + - nom: "Dokuwiki" + icon: "icons/dokuwiki.png" + url: "http://10.0.0.5:8880/" + - nom: "Heimdall" + icon: "icons/heimdall.png" + url: "http://10.0.0.5:8002/" + - nom: "Claude" + icon: "icons/claude.png" + url: "https://claude.ai/" + - nom: "Chatgpt" + icon: "icons/chatgpt.png" + url: "https://chatgpt.com/" + - nom: "Vaultwarden" + icon: "icons/vaultwarden.png" + url: "https://vw.maison43.duckdns.org" + - nom: "Digiposte" + icon: "icons/digiposte.png" + url: "https://moncompte.laposte.fr/" + - nom: "Google Actu" + icon: "icons/googlenews.png" + url: "https://news.google.com/" + - nom: "Z2M ETH" + icon: "icons/zigbee2mqtt.png" + url: "http://10.0.0.105:8080" + - nom: "Z2M" + icon: "icons/zigbee2mqtt2.png" + url: "http://10.0.0.106:8080" + - nom: "nodered" + icon: "icons/nodered.png" + url: "http://10.0.0.9:1880/" + - nom: "memos" + icon: "icons/memos.png" + url: "http://10.0.0.5:5230/" + - nom: "gemini" + icon: "icons/gemini.png" + url: "https://gemini.google.com/app" + - nom: "Mistral" + icon: "icons/mistral.png" + url: "https://chat.mistral.ai/chat" + - nom: "Grok" + icon: "icons/grok.png" + url: "https://chat.mistral.ai/chat" + - nom: "Nginx Proxy Manager" + icon: "icons/nginx-proxy-manager.png" + url: "http://10.0.0.202:81/" + - nom: "selfhosted" + icon: "icons/selfh-st.png" + url: "https://selfh.st/" + - nom: "korben" + icon: "icons/korben.jpeg" + url: "https://korben.info/" + - nom: "amazon" + icon: "icons/amazon.png" + url: "https://www.amazon.fr/" + - nom: "prime" + icon: "icons/amazon-prime-video.png" + url: "https://www.primevideo.com/" + - nom: "Orange TV" + icon: "icons/orange_tv.png" + url: "https://tv.orange.fr/en-direct/programmes-en-cours" + - nom: "La Commère 43" + icon: "icons/comere43.jpeg" + url: "https://www.lacommere43.fr/fait-divers.html" + - nom: "Aliexpress" + icon: "icons/aliexpress.png" + url: "https://fr.aliexpress.com" + - nom: "Google Drive" + icon: "icons/gdrive.png" + url: "https://drive.google.com/" + - nom: "Camera detect" + icon: "icons/camera.png" + url: "http://10.0.0.13:8081/" + - nom: "Camera detect" + icon: "icons/camera.png" + url: "http://10.0.0.13:8081/" + +# ============================================================================= +# Section MINITOOLS - barre d'outils rapides (ligne en bas) +# ============================================================================= +minitools: + # indiquez soit un "command" (sera lancé via app://run/) soit une "url" + - nom: "Immich" + icon: "icons/immich.png" + url: "http://10.0.0.30:2283" + - nom: "MQTT Explorer" + icon: "icons/mqttexplorer.png" + url: "http://10.0.0.8:8088/" + - nom: "Hardware Benchtools" + icon: "icons/hardware_benchtools.png" + url: "http://10.0.0.50:8087" + - nom: "Excalidraw" + icon: "icons/excalidraw.png" + url: "http://10.0.1.123:3000/" + - nom: "IPWatch" + icon: "icons/ipwatch.png" + url: "http://10.0.0.8:8080/" + - nom: "VideoPlayer" + icon: "icons/videoplayer.png" + url: "http://10.0.0.50:8080/" + + - nom: "Palette couleurs" + icon: "icons/color-picker.svg" + command: "python3 /home/gilles/Documents/vscode/ssh-web-launcher/app/tools/color-picker/color_picker.py" diff --git a/config/equipements.yaml.yml b/config/equipements.yaml.yml new file mode 100644 index 0000000..f936619 --- /dev/null +++ b/config/equipements.yaml.yml @@ -0,0 +1,296 @@ +# Configuration des équipements et services +# Modifiez ce fichier pour ajouter vos machines + +# ============================================================================= +# Configuration de la fenêtre popup +# ============================================================================= +fenetre: + ecran: 0 # 0 = principal, 1 = secondaire, -1 = souris + centrer: false # Centrer horizontalement + #alignement: # center,left,right + #decalage_x: # decalage par rapport a l'alignement >0 pour left et center et <0 pour right + #decalage_y: # decalage par rapport a la barre du haut + x: 1500 # Position X (si centrer: false) + y: 0 # Position Y depuis le haut + largeur: 1100 + hauteur: 840 + fermer_sur_clic_exterieur: true + toujours_au_dessus: false + autohide: 1 # Fermer après X secondes si souris hors fenêtre (0 = désactivé) + section_distant: + colonne: 4 + section_local: + colonne: 6 + section_url: + colonne: 5 + +# ============================================================================= +# Apparence +# ============================================================================= +apparence: + theme: "dark" # "dark" ou "light" + police_taille: 14 # Taille de police en pixels + icon_taille_distant: 48 # Taille icônes section Distant + icon_taille_local: 52 # Taille icônes section Local + icon_taille_url: 48 # Taille icônes section URL + afficher_label_local: false # Afficher le texte sous les icônes locales + ping_intervalle: 360 # Intervalle entre les pings en secondes (0 = désactivé) + border_radius: 25 # Arrondi des coins de la fenêtre en pixels + espacement_local: 2 # Espacement entre icônes section Local (px) + espacement_distant: 6 # Espacement entre icônes section Distant (px) + espacement_url: 6 # Espacement entre icônes section URL (px) + icon_taille_fermer: 22 # Taille icône fermer (px) + icon_taille_parametre: 22 # Taille icône paramètre (px) + icon_taille_theme: 30 # Largeur icône night-day (px) - hauteur auto selon ratio + border_radius_local: 12 # Arrondi de la section locale (px) + # Couleurs personnalisées (laisser vide pour utiliser le thème par défaut) + couleur_fond: "#2a2a2a" # Fond principal (ex: "#1e1e1e") + couleur_header: "#2a2a2a" # Fond du header (ex: "#2a2a2a") + couleur_box_local: "#2a2a2a" # Fond section LOCAL (ex: "#3d3d3d") + couleur_box_distant: "#1e1e1e" # Fond boxes DISTANT (ex: "#3d3d3d") + couleur_box_url: "#3d3d3d" # Fond section URL (ex: "#3d3d3d") + couleur_item: "#1e1e1e" # Fond des items services (ex: "#2d2d2d") + couleur_bordure: "#111a1a" # Couleur des bordures (ex: "#1a1a1a") + couleur_hover: "#515555" # Couleur au survol souris (ex: "#555555") + +# ============================================================================= +# Section DISTANT - Machines distantes (à gauche) +# ============================================================================= +distant: + - ip: "10.0.0.24" + nom: "PC Dashboard" + services: + - nom: "SSH" + icon: "icons/ssh.png" + url: "ssh://gilles@10.0.0.24" + - nom: "VNC" + icon: "icons/terminal.png" + url: "vnc://10.0.0.24:5900" + + - ip: "10.0.1.232" + nom: "m710q" + services: + - nom: "proxmox" + icon: "icons/proxmox.png" + url: "https://10.0.1.232:8006" + - nom: "SSH" + icon: "icons/ssh.png" + url: "ssh://root@10.0.1.232" + + - ip: "10.0.0.101" + nom: "EliteDesk" + services: + - nom: "SSH" + icon: "icons/ssh.png" + url: "ssh://root@10.0.0.101" + - nom: "Proxmox" + icon: "icons/proxmox.png" + url: "https://10.0.0.101:8006/" + + - ip: "10.0.0.5" + nom: "VM 5" + services: + - nom: "SSH" + icon: "icons/ssh.png" + url: "ssh://gilles@10.0.0.5" + password: "gilles" #copy password to clipboard + - nom: "Gitea" + icon: "icons/gitea.png" + url: "https://gitea.maison43.duckdns.org/" + - nom: "Arcane" + icon: "icons/arcane.png" + url: "http://10.0.0.5:3552/" + - nom: "Hortus_fox" + icon: "icons/hortusfox.png" + url: "http://10.0.0.5:8081/" + - nom: "Termix" + icon: "icons/termix.png" + url: "http://10.0.0.5:6080/" + - nom: "upsnap" + icon: "icons/upsnap.png" + url: "http://10.0.0.5:8090/" + - nom: "samba" # automount samba in nautilus and open in nautilus + icon: "icons/smb.png" + url: "smb://10.0.0.5/" + user: gilles #user for samba + password: gilles #password for samba + + + + - ip: "137.74.45.99" + nom: "Vps OVH" + services: + - nom: "SSH" + icon: "icons/ssh.png" + url: "ssh://debian@137.74.45.99" + password: "Misstibet5*" #copy password to clipboard + - nom: "VNC" + icon: "icons/terminal.png" + url: "vnc://137.74.45.99:5901" + + - ip: "10.0.0.2" + nom: "Home Assistant" + services: + - nom: "Home Assistant" + icon: "icons/home-assistant.png" + url: "http://10.0.0.2:8123" + + - ip: "10.0.0.205" + nom: "Proliant" + services: + - nom: "Proxmox" + icon: "icons/proxmox.png" + url: "https://10.0.0.205:8006/" + + - ip: "10.0.0.15" + nom: "Adguard" + services: + - nom: "Adguard" + icon: "icons/adguard-home.png" + url: "http://10.0.0.15/" + + - ip: "10.0.0.135" + nom: "Paperless" + services: + - nom: "Paperless" + icon: "icons/paperless.png" + url: "http://10.0.0.135:8777/" + - nom: "scanServ.js" + icon: "icons/scanservjs.png" + url: "http://10.0.0.135:8080/" + - nom: "SSH" + icon: "icons/ssh.png" + url: "ssh://gilles@10.0.0.135" + password: "gilles" #copy password to clipboard + + - ip: "10.0.0.116" + nom: "VM 116" + services: + - nom: "Arcane" + icon: "icons/arcane.png" + url: "http://10.0.0.116:3552/" + - nom: "Heimdall" + icon: "icons/heimdall.png" + url: "http://10.0.0.135:8002/" + - nom: "SSH" + icon: "icons/ssh.png" + url: "ssh://gilles@10.0.0.116" + password: "gilles" #copy password to clipboard + +# ============================================================================= +# Section LOCAL - Applications locales (en bas) +# ============================================================================= +local: + - nom: "Fichiers" + icon: "icons/nautilus.png" + command: "nautilus" + - nom: "Terminal" + icon: "icons/terminal.png" + command: "gnome-terminal" + - nom: "Firefox" + icon: "icons/firefox.png" + command: "firefox" + - nom: "Chrome" + icon: "icons/chrome.png" + command: "google-chrome" + - nom: "VS Code" + icon: "icons/vscode.png" + command: "code" + - nom: "Calculatrice" + icon: "icons/calculatrice.png" + command: "gnome-calculator" + - nom: "Gnumeric" + icon: "icons/gnumeric.png" + command: "gnumeric" + - nom: "Moniteur system" + icon: "icons/gparameter.png" + command: "gnome-control-center" + - nom: "Parametres" + icon: "icons/monitor.png" + command: "gnome-system-monitor" + +# ============================================================================= +# Section URL - Favoris web (à droite) +# ============================================================================= +url: + - nom: "Google" + icon: "icons/google.png" + url: "https://www.google.com/" + - nom: "Gmail" + icon: "icons/gmail.png" + url: "https://mail.google.com" + - nom: "YouTube" + icon: "icons/youtube.png" + url: "https://youtube.com" + - nom: "Dokuwiki" + icon: "icons/dokuwiki.png" + url: "http://10.0.0.5:8880/" + - nom: "Heimdall" + icon: "icons/heimdall.png" + url: "http://10.0.0.5:8002/" + - nom: "Claude" + icon: "icons/claude.png" + url: "https://claude.ai/" + - nom: "Chatgpt" + icon: "icons/chatgpt.png" + url: "https://chatgpt.com/" + - nom: "Vaultwarden" + icon: "icons/vaultwarden.png" + url: "https://vw.maison43.duckdns.org" + - nom: "Digiposte" + icon: "icons/digiposte.png" + url: "https://moncompte.laposte.fr/" + - nom: "Google Actu" + icon: "icons/googlenews.png" + url: "https://news.google.com/" + - nom: "Z2M ETH" + icon: "icons/zigbee2mqtt.png" + url: "http://10.0.0.105:8080" + - nom: "Z2M" + icon: "icons/zigbee2mqtt2.png" + url: "http://10.0.0.106:8080" + - nom: "nodered" + icon: "icons/nodered.png" + url: "http://10.0.0.9:1880/" + - nom: "memos" + icon: "icons/memos.png" + url: "http://10.0.0.5:5230/" + - nom: "gemini" + icon: "icons/gemini.png" + url: "https://gemini.google.com/app" + - nom: "Mistral" + icon: "icons/mistral.png" + url: "https://chat.mistral.ai/chat" + - nom: "Grok" + icon: "icons/grok.png" + url: "https://chat.mistral.ai/chat" + - nom: "Nginx Proxy Manager" + icon: "icons/nginx-proxy-manager.png" + url: "http://10.0.0.202:81/" + - nom: "selfhosted" + icon: "icons/selfh-st.png" + url: "https://selfh.st/" + - nom: "korben" + icon: "icons/korben.jpeg" + url: "https://korben.info/" + - nom: "amazon" + icon: "icons/amazon.png" + url: "https://www.amazon.fr/" + - nom: "prime" + icon: "icons/amazon-prime-video.png" + url: "https://www.primevideo.com/" + - nom: "Orange TV" + icon: "icons/orange_tv.png" + url: "https://tv.orange.fr/en-direct/programmes-en-cours" + - nom: "La Commère 43" + icon: "icons/comere43.jpeg" + url: "https://www.lacommere43.fr/fait-divers.html" + - nom: "Aliexpress" + icon: "icons/aliexpress.png" + url: "https://fr.aliexpress.com" + - nom: "Google Drive" + icon: "icons/gdrive.png" + url: "https://drive.google.com/" + - nom: "Camera detect" + icon: "icons/camera.png" + url: "http://10.0.0.13:8081/" diff --git a/dashboard.drawio b/dashboard.drawio new file mode 100644 index 0000000..a26edd8 --- /dev/null +++ b/dashboard.drawio @@ -0,0 +1,178 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/desktop/ssh-url.desktop b/desktop/ssh-url.desktop new file mode 100644 index 0000000..1dbd677 --- /dev/null +++ b/desktop/ssh-url.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Name=SSH URL Handler +Comment=Ouvre les liens ssh:// dans un terminal +Exec=/home/gilles/Documents/vscode/ssh-web-launcher/scripts/ssh-url %u +Type=Application +Terminal=false +NoDisplay=true +MimeType=x-scheme-handler/ssh; +Categories=Network; diff --git a/extension/extension.js b/extension/extension.js new file mode 100644 index 0000000..01cc69e --- /dev/null +++ b/extension/extension.js @@ -0,0 +1,111 @@ +/* extension.js + * Extension GNOME Shell - Dashboard Launcher + * Ajoute une icône dans la barre supérieure pour lancer/fermer le Dashboard + * Toggle: clic = affiche, re-clic = ferme + * + * Compatible GNOME 45+ (ESM) + */ + +'use strict'; + +import GObject from 'gi://GObject'; +import St from 'gi://St'; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js'; + +const APP_PATH = GLib.build_filenamev(['/home/gilles/Documents/vscode/ssh-web-launcher', 'app', 'ssh-launcher-gtk.py']); +const PID_FILE = '/tmp/dashboard-launcher.pid'; + +const DashboardLauncherIndicator = GObject.registerClass( +class DashboardLauncherIndicator extends PanelMenu.Button { + _init() { + super._init(0.0, 'Dashboard Launcher'); + + // Icône dans la barre (style dashboard/grille) + this._icon = new St.Icon({ + icon_name: 'view-app-grid-symbolic', + style_class: 'system-status-icon dashboard-launcher-icon', + }); + + this.add_child(this._icon); + + // Connexion du clic + this.connect('button-press-event', this._onClick.bind(this)); + } + + _onClick() { + this._toggleApp(); + return true; + } + + _isRunning() { + // Vérifie si le dashboard est en cours d'exécution + try { + let file = Gio.File.new_for_path(PID_FILE); + if (file.query_exists(null)) { + let [success, contents] = file.load_contents(null); + if (success) { + let pid = parseInt(new TextDecoder().decode(contents).trim()); + // Vérifier si le processus existe (signal 0) + try { + let checkProc = new Gio.Subprocess({ + argv: ['kill', '-0', pid.toString()], + flags: Gio.SubprocessFlags.NONE, + }); + checkProc.init(null); + checkProc.wait(null); + return checkProc.get_successful(); + } catch (e) { + return false; + } + } + } + } catch (e) { + // Fichier n'existe pas ou erreur de lecture + } + return false; + } + + _toggleApp() { + try { + if (this._isRunning()) { + // Dashboard ouvert -> le fermer avec --toggle + let subprocess = new Gio.Subprocess({ + argv: ['python3', APP_PATH, '--toggle'], + flags: Gio.SubprocessFlags.NONE, + }); + subprocess.init(null); + } else { + // Dashboard fermé -> l'ouvrir + let subprocess = new Gio.Subprocess({ + argv: ['python3', APP_PATH], + flags: Gio.SubprocessFlags.NONE, + }); + subprocess.init(null); + } + } catch (e) { + logError(e, 'Dashboard Launcher: Erreur lors du toggle'); + } + } +}); + +export default class DashboardLauncherExtension { + constructor() { + this._indicator = null; + } + + enable() { + this._indicator = new DashboardLauncherIndicator(); + Main.panel.addToStatusArea('dashboard-launcher', this._indicator); + } + + disable() { + if (this._indicator) { + this._indicator.destroy(); + this._indicator = null; + } + } +} diff --git a/extension/metadata.json b/extension/metadata.json new file mode 100644 index 0000000..bff0c96 --- /dev/null +++ b/extension/metadata.json @@ -0,0 +1,8 @@ +{ + "name": "SSH Launcher", + "description": "Lance une interface de connexion SSH depuis la barre GNOME", + "uuid": "ssh-launcher@local", + "version": 1, + "shell-version": ["42", "43", "44", "45", "46", "47", "48"], + "url": "" +} diff --git a/extension/stylesheet.css b/extension/stylesheet.css new file mode 100644 index 0000000..bea1c93 --- /dev/null +++ b/extension/stylesheet.css @@ -0,0 +1,10 @@ +/* stylesheet.css - Style de l'extension SSH Launcher */ + +.ssh-launcher-icon { + /* Icône légèrement colorée pour la distinguer */ + color: #00d9ff; +} + +.ssh-launcher-icon:hover { + color: #ffffff; +} diff --git a/icons original/amazon-prime-video.png b/icons original/amazon-prime-video.png new file mode 100644 index 0000000..7b474aa Binary files /dev/null and b/icons original/amazon-prime-video.png differ diff --git a/icons original/amazon.png b/icons original/amazon.png new file mode 100644 index 0000000..f1c81ab Binary files /dev/null and b/icons original/amazon.png differ diff --git a/icons original/arcane.png b/icons original/arcane.png new file mode 100644 index 0000000..e07efda Binary files /dev/null and b/icons original/arcane.png differ diff --git a/icons original/calculatrice.png b/icons original/calculatrice.png new file mode 100644 index 0000000..b94cf26 Binary files /dev/null and b/icons original/calculatrice.png differ diff --git a/icons original/chatgpt.png b/icons original/chatgpt.png new file mode 100644 index 0000000..d9b137a Binary files /dev/null and b/icons original/chatgpt.png differ diff --git a/icons original/chrome.png b/icons original/chrome.png new file mode 100644 index 0000000..cd86adb Binary files /dev/null and b/icons original/chrome.png differ diff --git a/icons original/claude.png b/icons original/claude.png new file mode 100644 index 0000000..50d96af Binary files /dev/null and b/icons original/claude.png differ diff --git a/icons original/digiposte.png b/icons original/digiposte.png new file mode 100644 index 0000000..5e0b014 Binary files /dev/null and b/icons original/digiposte.png differ diff --git a/icons original/dokuwiki.png b/icons original/dokuwiki.png new file mode 100644 index 0000000..6231734 Binary files /dev/null and b/icons original/dokuwiki.png differ diff --git a/icons original/excalidraw.png b/icons original/excalidraw.png new file mode 100644 index 0000000..853b3d4 Binary files /dev/null and b/icons original/excalidraw.png differ diff --git a/icons original/fermer.png b/icons original/fermer.png new file mode 100644 index 0000000..bb47fb5 Binary files /dev/null and b/icons original/fermer.png differ diff --git a/icons original/fermer2.png b/icons original/fermer2.png new file mode 100644 index 0000000..ec5e599 Binary files /dev/null and b/icons original/fermer2.png differ diff --git a/icons original/firefox.png b/icons original/firefox.png new file mode 100644 index 0000000..081de12 Binary files /dev/null and b/icons original/firefox.png differ diff --git a/icons original/gemini.png b/icons original/gemini.png new file mode 100644 index 0000000..15aaef4 Binary files /dev/null and b/icons original/gemini.png differ diff --git a/icons original/gitea.png b/icons original/gitea.png new file mode 100644 index 0000000..1e0d56e Binary files /dev/null and b/icons original/gitea.png differ diff --git a/icons original/gmail.png b/icons original/gmail.png new file mode 100644 index 0000000..3b73d57 Binary files /dev/null and b/icons original/gmail.png differ diff --git a/icons original/gnumeric.png b/icons original/gnumeric.png new file mode 100644 index 0000000..4514192 Binary files /dev/null and b/icons original/gnumeric.png differ diff --git a/icons original/google.png b/icons original/google.png new file mode 100644 index 0000000..c23eed0 Binary files /dev/null and b/icons original/google.png differ diff --git a/icons original/googlenews.png b/icons original/googlenews.png new file mode 100644 index 0000000..a7cf45a Binary files /dev/null and b/icons original/googlenews.png differ diff --git a/icons original/gparameter.png b/icons original/gparameter.png new file mode 100644 index 0000000..b4ef233 Binary files /dev/null and b/icons original/gparameter.png differ diff --git a/icons original/grok.png b/icons original/grok.png new file mode 100644 index 0000000..1ba65e2 Binary files /dev/null and b/icons original/grok.png differ diff --git a/icons original/hardware_benchtools.png b/icons original/hardware_benchtools.png new file mode 100644 index 0000000..f4f97bf Binary files /dev/null and b/icons original/hardware_benchtools.png differ diff --git a/icons original/heimdall.png b/icons original/heimdall.png new file mode 100644 index 0000000..299fed3 Binary files /dev/null and b/icons original/heimdall.png differ diff --git a/icons original/home-assistant.png b/icons original/home-assistant.png new file mode 100644 index 0000000..fc6b190 Binary files /dev/null and b/icons original/home-assistant.png differ diff --git a/icons original/hortusfox.png b/icons original/hortusfox.png new file mode 100644 index 0000000..fd72f9e Binary files /dev/null and b/icons original/hortusfox.png differ diff --git a/icons original/immich.png b/icons original/immich.png new file mode 100644 index 0000000..b5f16a1 Binary files /dev/null and b/icons original/immich.png differ diff --git a/icons original/ipwatch.png b/icons original/ipwatch.png new file mode 100644 index 0000000..1475850 Binary files /dev/null and b/icons original/ipwatch.png differ diff --git a/icons original/memos.png b/icons original/memos.png new file mode 100644 index 0000000..17be6b7 Binary files /dev/null and b/icons original/memos.png differ diff --git a/icons original/monitor.png b/icons original/monitor.png new file mode 100644 index 0000000..43e30c5 Binary files /dev/null and b/icons original/monitor.png differ diff --git a/icons original/mqttexplorer.png b/icons original/mqttexplorer.png new file mode 100644 index 0000000..b381802 Binary files /dev/null and b/icons original/mqttexplorer.png differ diff --git a/icons original/nautilus.png b/icons original/nautilus.png new file mode 100644 index 0000000..82e36bb Binary files /dev/null and b/icons original/nautilus.png differ diff --git a/icons original/nfs.png b/icons original/nfs.png new file mode 100644 index 0000000..206bdce Binary files /dev/null and b/icons original/nfs.png differ diff --git a/icons original/nginx-proxy-manager.png b/icons original/nginx-proxy-manager.png new file mode 100644 index 0000000..ad0854b Binary files /dev/null and b/icons original/nginx-proxy-manager.png differ diff --git a/icons original/night-day.png b/icons original/night-day.png new file mode 100644 index 0000000..bf1c1b7 Binary files /dev/null and b/icons original/night-day.png differ diff --git a/icons original/night-day2.png b/icons original/night-day2.png new file mode 100644 index 0000000..3bbb722 Binary files /dev/null and b/icons original/night-day2.png differ diff --git a/icons original/nodered.png b/icons original/nodered.png new file mode 100644 index 0000000..cc7eb0d Binary files /dev/null and b/icons original/nodered.png differ diff --git a/icons original/ollama.png b/icons original/ollama.png new file mode 100644 index 0000000..54c1c2b Binary files /dev/null and b/icons original/ollama.png differ diff --git a/icons original/paperless.png b/icons original/paperless.png new file mode 100644 index 0000000..a9ac9cb Binary files /dev/null and b/icons original/paperless.png differ diff --git a/icons original/parameter.png b/icons original/parameter.png new file mode 100644 index 0000000..7f618b4 Binary files /dev/null and b/icons original/parameter.png differ diff --git a/icons original/parametre.png b/icons original/parametre.png new file mode 100644 index 0000000..06283cf Binary files /dev/null and b/icons original/parametre.png differ diff --git a/icons original/parametre2.png b/icons original/parametre2.png new file mode 100644 index 0000000..9b5bfca Binary files /dev/null and b/icons original/parametre2.png differ diff --git a/icons original/proxmox.png b/icons original/proxmox.png new file mode 100644 index 0000000..650c0fb Binary files /dev/null and b/icons original/proxmox.png differ diff --git a/icons original/selfh-st.png b/icons original/selfh-st.png new file mode 100644 index 0000000..15fa14c Binary files /dev/null and b/icons original/selfh-st.png differ diff --git a/icons original/smb.png b/icons original/smb.png new file mode 100644 index 0000000..eb940d3 Binary files /dev/null and b/icons original/smb.png differ diff --git a/icons original/ssh.png b/icons original/ssh.png new file mode 100644 index 0000000..0b9fca8 Binary files /dev/null and b/icons original/ssh.png differ diff --git a/icons original/terminal.png b/icons original/terminal.png new file mode 100644 index 0000000..c719926 Binary files /dev/null and b/icons original/terminal.png differ diff --git a/icons original/termix.png b/icons original/termix.png new file mode 100644 index 0000000..b5dd7d3 Binary files /dev/null and b/icons original/termix.png differ diff --git a/icons original/upsnap.png b/icons original/upsnap.png new file mode 100644 index 0000000..99fcff1 Binary files /dev/null and b/icons original/upsnap.png differ diff --git a/icons original/vaultwarden.png b/icons original/vaultwarden.png new file mode 100644 index 0000000..b0b64ea Binary files /dev/null and b/icons original/vaultwarden.png differ diff --git a/icons original/videoplayer.png b/icons original/videoplayer.png new file mode 100644 index 0000000..ba001d4 Binary files /dev/null and b/icons original/videoplayer.png differ diff --git a/icons original/vscode.png b/icons original/vscode.png new file mode 100644 index 0000000..b4bd206 Binary files /dev/null and b/icons original/vscode.png differ diff --git a/icons original/watchip.png b/icons original/watchip.png new file mode 100644 index 0000000..66a2015 Binary files /dev/null and b/icons original/watchip.png differ diff --git a/icons original/youtube.png b/icons original/youtube.png new file mode 100644 index 0000000..654c3de Binary files /dev/null and b/icons original/youtube.png differ diff --git a/icons original/zigbee2mqtt.png b/icons original/zigbee2mqtt.png new file mode 100644 index 0000000..3eab252 Binary files /dev/null and b/icons original/zigbee2mqtt.png differ diff --git a/icons original/zigbee2mqtt2.png b/icons original/zigbee2mqtt2.png new file mode 100644 index 0000000..c343aef Binary files /dev/null and b/icons original/zigbee2mqtt2.png differ diff --git a/icons/adguard-home.png b/icons/adguard-home.png new file mode 100644 index 0000000..a47c528 Binary files /dev/null and b/icons/adguard-home.png differ diff --git a/icons/aliexpress.png b/icons/aliexpress.png new file mode 100644 index 0000000..122f5c0 Binary files /dev/null and b/icons/aliexpress.png differ diff --git a/icons/amazon-prime-video.png b/icons/amazon-prime-video.png new file mode 100644 index 0000000..1b89084 Binary files /dev/null and b/icons/amazon-prime-video.png differ diff --git a/icons/amazon.png b/icons/amazon.png new file mode 100644 index 0000000..be8de41 Binary files /dev/null and b/icons/amazon.png differ diff --git a/icons/arcane.png b/icons/arcane.png new file mode 100644 index 0000000..b3b3d25 Binary files /dev/null and b/icons/arcane.png differ diff --git a/icons/calculatrice.png b/icons/calculatrice.png new file mode 100644 index 0000000..2101426 Binary files /dev/null and b/icons/calculatrice.png differ diff --git a/icons/chatgpt.png b/icons/chatgpt.png new file mode 100644 index 0000000..a910d73 Binary files /dev/null and b/icons/chatgpt.png differ diff --git a/icons/chrome.png b/icons/chrome.png new file mode 100644 index 0000000..5840f82 Binary files /dev/null and b/icons/chrome.png differ diff --git a/icons/claude.png b/icons/claude.png new file mode 100644 index 0000000..34dd1ab Binary files /dev/null and b/icons/claude.png differ diff --git a/icons/color-picker.svg b/icons/color-picker.svg new file mode 100644 index 0000000..b838e9f --- /dev/null +++ b/icons/color-picker.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/icons/comere43.jpeg b/icons/comere43.jpeg new file mode 100644 index 0000000..46a5bc4 Binary files /dev/null and b/icons/comere43.jpeg differ diff --git a/icons/digiposte.png b/icons/digiposte.png new file mode 100644 index 0000000..0952d52 Binary files /dev/null and b/icons/digiposte.png differ diff --git a/icons/dokuwiki.png b/icons/dokuwiki.png new file mode 100644 index 0000000..49311db Binary files /dev/null and b/icons/dokuwiki.png differ diff --git a/icons/excalidraw.png b/icons/excalidraw.png new file mode 100644 index 0000000..16182ff Binary files /dev/null and b/icons/excalidraw.png differ diff --git a/icons/fermer.png b/icons/fermer.png new file mode 100644 index 0000000..41542ed Binary files /dev/null and b/icons/fermer.png differ diff --git a/icons/fermer2.png b/icons/fermer2.png new file mode 100644 index 0000000..750d068 Binary files /dev/null and b/icons/fermer2.png differ diff --git a/icons/firefox.png b/icons/firefox.png new file mode 100644 index 0000000..32b2396 Binary files /dev/null and b/icons/firefox.png differ diff --git a/icons/gdrive.png b/icons/gdrive.png new file mode 100644 index 0000000..b3efbda Binary files /dev/null and b/icons/gdrive.png differ diff --git a/icons/gemini.png b/icons/gemini.png new file mode 100644 index 0000000..71abfad Binary files /dev/null and b/icons/gemini.png differ diff --git a/icons/gitea.png b/icons/gitea.png new file mode 100644 index 0000000..3fa86c4 Binary files /dev/null and b/icons/gitea.png differ diff --git a/icons/gmail.png b/icons/gmail.png new file mode 100644 index 0000000..2754146 Binary files /dev/null and b/icons/gmail.png differ diff --git a/icons/gnumeric.png b/icons/gnumeric.png new file mode 100644 index 0000000..88fb4d3 Binary files /dev/null and b/icons/gnumeric.png differ diff --git a/icons/google.png b/icons/google.png new file mode 100644 index 0000000..c9e82a7 Binary files /dev/null and b/icons/google.png differ diff --git a/icons/googlenews.png b/icons/googlenews.png new file mode 100644 index 0000000..0aba3d3 Binary files /dev/null and b/icons/googlenews.png differ diff --git a/icons/gparameter.png b/icons/gparameter.png new file mode 100644 index 0000000..6b299cb Binary files /dev/null and b/icons/gparameter.png differ diff --git a/icons/grok.png b/icons/grok.png new file mode 100644 index 0000000..15f30e8 Binary files /dev/null and b/icons/grok.png differ diff --git a/icons/hardware_benchtools.png b/icons/hardware_benchtools.png new file mode 100644 index 0000000..f4f97bf Binary files /dev/null and b/icons/hardware_benchtools.png differ diff --git a/icons/heimdall.png b/icons/heimdall.png new file mode 100644 index 0000000..c943103 Binary files /dev/null and b/icons/heimdall.png differ diff --git a/icons/home-assistant.png b/icons/home-assistant.png new file mode 100644 index 0000000..10e36de Binary files /dev/null and b/icons/home-assistant.png differ diff --git a/icons/hortusfox.png b/icons/hortusfox.png new file mode 100644 index 0000000..bed33d9 Binary files /dev/null and b/icons/hortusfox.png differ diff --git a/icons/immich.png b/icons/immich.png new file mode 100644 index 0000000..f3b71a5 Binary files /dev/null and b/icons/immich.png differ diff --git a/icons/ipwatch.png b/icons/ipwatch.png new file mode 100644 index 0000000..c6a6085 Binary files /dev/null and b/icons/ipwatch.png differ diff --git a/icons/korben.jpeg b/icons/korben.jpeg new file mode 100644 index 0000000..74982e8 Binary files /dev/null and b/icons/korben.jpeg differ diff --git a/icons/memos.png b/icons/memos.png new file mode 100644 index 0000000..e627934 Binary files /dev/null and b/icons/memos.png differ diff --git a/icons/mistral.png b/icons/mistral.png new file mode 100644 index 0000000..c5f3d5a Binary files /dev/null and b/icons/mistral.png differ diff --git a/icons/monitor.png b/icons/monitor.png new file mode 100644 index 0000000..eeeaf8b Binary files /dev/null and b/icons/monitor.png differ diff --git a/icons/mqttexplorer.png b/icons/mqttexplorer.png new file mode 100644 index 0000000..ec7d254 Binary files /dev/null and b/icons/mqttexplorer.png differ diff --git a/icons/nautilus.png b/icons/nautilus.png new file mode 100644 index 0000000..8620896 Binary files /dev/null and b/icons/nautilus.png differ diff --git a/icons/nfs.png b/icons/nfs.png new file mode 100644 index 0000000..a9cf020 Binary files /dev/null and b/icons/nfs.png differ diff --git a/icons/nginx-proxy-manager.png b/icons/nginx-proxy-manager.png new file mode 100644 index 0000000..c13803c Binary files /dev/null and b/icons/nginx-proxy-manager.png differ diff --git a/icons/night-day.png b/icons/night-day.png new file mode 100644 index 0000000..c4514bd Binary files /dev/null and b/icons/night-day.png differ diff --git a/icons/night-day2.png b/icons/night-day2.png new file mode 100644 index 0000000..bca693a Binary files /dev/null and b/icons/night-day2.png differ diff --git a/icons/nodered.png b/icons/nodered.png new file mode 100644 index 0000000..5b77b3c Binary files /dev/null and b/icons/nodered.png differ diff --git a/icons/ollama.png b/icons/ollama.png new file mode 100644 index 0000000..c290f4e Binary files /dev/null and b/icons/ollama.png differ diff --git a/icons/orange_tv.png b/icons/orange_tv.png new file mode 100644 index 0000000..4edcc35 Binary files /dev/null and b/icons/orange_tv.png differ diff --git a/icons/paperless.png b/icons/paperless.png new file mode 100644 index 0000000..7aec8ac Binary files /dev/null and b/icons/paperless.png differ diff --git a/icons/parameter.png b/icons/parameter.png new file mode 100644 index 0000000..a10c299 Binary files /dev/null and b/icons/parameter.png differ diff --git a/icons/parametre.png b/icons/parametre.png new file mode 100644 index 0000000..0a50107 Binary files /dev/null and b/icons/parametre.png differ diff --git a/icons/parametre2.png b/icons/parametre2.png new file mode 100644 index 0000000..b0f38b3 Binary files /dev/null and b/icons/parametre2.png differ diff --git a/icons/proxmox.png b/icons/proxmox.png new file mode 100644 index 0000000..dc3da0a Binary files /dev/null and b/icons/proxmox.png differ diff --git a/icons/scanservjs.png b/icons/scanservjs.png new file mode 100644 index 0000000..3d7699f Binary files /dev/null and b/icons/scanservjs.png differ diff --git a/icons/selfh-st.png b/icons/selfh-st.png new file mode 100644 index 0000000..fb26ca3 Binary files /dev/null and b/icons/selfh-st.png differ diff --git a/icons/smb.png b/icons/smb.png new file mode 100644 index 0000000..d44c799 Binary files /dev/null and b/icons/smb.png differ diff --git a/icons/ssh.png b/icons/ssh.png new file mode 100644 index 0000000..2cf88c0 Binary files /dev/null and b/icons/ssh.png differ diff --git a/icons/terminal.png b/icons/terminal.png new file mode 100644 index 0000000..6baa9a3 Binary files /dev/null and b/icons/terminal.png differ diff --git a/icons/termix.png b/icons/termix.png new file mode 100644 index 0000000..f93dfa7 Binary files /dev/null and b/icons/termix.png differ diff --git a/icons/upsnap.png b/icons/upsnap.png new file mode 100644 index 0000000..a47bd4d Binary files /dev/null and b/icons/upsnap.png differ diff --git a/icons/vaultwarden.png b/icons/vaultwarden.png new file mode 100644 index 0000000..63c9597 Binary files /dev/null and b/icons/vaultwarden.png differ diff --git a/icons/videoplayer.png b/icons/videoplayer.png new file mode 100644 index 0000000..ba001d4 Binary files /dev/null and b/icons/videoplayer.png differ diff --git a/icons/vscode.png b/icons/vscode.png new file mode 100644 index 0000000..f3b4f08 Binary files /dev/null and b/icons/vscode.png differ diff --git a/icons/youtube.png b/icons/youtube.png new file mode 100644 index 0000000..e22fed7 Binary files /dev/null and b/icons/youtube.png differ diff --git a/icons/zigbee2mqtt.png b/icons/zigbee2mqtt.png new file mode 100644 index 0000000..b809ba6 Binary files /dev/null and b/icons/zigbee2mqtt.png differ diff --git a/icons/zigbee2mqtt2.png b/icons/zigbee2mqtt2.png new file mode 100644 index 0000000..1cb569d Binary files /dev/null and b/icons/zigbee2mqtt2.png differ diff --git a/image.png b/image.png new file mode 100644 index 0000000..78990a7 Binary files /dev/null and b/image.png differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..22f1787 --- /dev/null +++ b/index.html @@ -0,0 +1,148 @@ + + + + + + SSH Launcher + + + +
+

SSH Launcher

+ + +
+

Postes de travail

+
+
+

PC Bureau

+
gilles@10.0.0.24
+ Connexion SSH +
+
+

Laptop

+
gilles@10.0.0.25
+ Connexion SSH +
+
+
+ + +
+

Serveurs

+
+
+

Proxmox

+
root@10.0.0.10
+ Connexion SSH +
+
+

NAS

+
admin@10.0.0.11
+ Connexion SSH +
+
+

Web Server

+
www@10.0.0.12
+ Connexion SSH +
+
+
+ + +
+

Raspberry Pi

+
+
+

Pi Home Assistant

+
pi@10.0.0.50
+ Connexion SSH +
+
+

Pi DNS

+
pi@10.0.0.51
+ Connexion SSH +
+
+
+ + +
+ + diff --git a/resize_icons.sh b/resize_icons.sh new file mode 100755 index 0000000..3979744 --- /dev/null +++ b/resize_icons.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# Dossier contenant les icônes +ICON_DIR="./icons" + +# Vérification +if [ ! -d "$ICON_DIR" ]; then + echo "Dossier '$ICON_DIR' introuvable." + exit 1 +fi + +# Parcours des PNG +for img in "$ICON_DIR"/*.png; do + [ -e "$img" ] || continue # si aucun fichier + echo "Redimensionnement : $img" + convert "$img" -resize 64x "$img" +done + +echo "Terminé." diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100755 index 0000000..1a6a5c4 --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,135 @@ +#!/bin/bash +# +# install.sh - Script d'installation de SSH Launcher +# Installe l'extension GNOME Shell, l'application GTK et le handler SSH +# + +set -e + +# Couleurs pour l'affichage +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Répertoire du projet +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_DIR="$(dirname "$SCRIPT_DIR")" + +echo -e "${GREEN}=== Installation de SSH Launcher ===${NC}" +echo "Répertoire du projet: $PROJECT_DIR" +echo "" + +# Vérifier les dépendances +echo -e "${YELLOW}Vérification des dépendances...${NC}" + +check_dependency() { + if ! command -v "$1" &> /dev/null; then + echo -e "${RED}Erreur: $1 n'est pas installé${NC}" + echo "Installez-le avec: $2" + exit 1 + fi + echo -e " ✓ $1" +} + +check_python_module() { + if ! python3 -c "import $1" 2>/dev/null; then + echo -e "${RED}Erreur: module Python '$1' non trouvé${NC}" + echo "Installez-le avec: $2" + exit 1 + fi + echo -e " ✓ Python module: $1" +} + +check_dependency "python3" "sudo apt install python3" +check_dependency "gnome-shell" "Installez GNOME Shell" + +check_python_module "gi" "sudo apt install python3-gi" +check_python_module "yaml" "sudo apt install python3-yaml" + +# Vérifier GTK3 et WebKit2 +python3 -c "import gi; gi.require_version('Gtk', '3.0')" 2>/dev/null || { + echo -e "${RED}Erreur: GTK3 non disponible${NC}" + echo "Installez avec: sudo apt install gir1.2-gtk-3.0" + exit 1 +} +echo -e " ✓ GTK3" + +python3 -c "import gi; gi.require_version('WebKit2', '4.1')" 2>/dev/null || { + echo -e "${RED}Erreur: WebKit2 4.1 non disponible${NC}" + echo "Installez avec: sudo apt install gir1.2-webkit2-4.1" + exit 1 +} +echo -e " ✓ WebKit2 4.1" + +echo "" + +# Rendre les scripts exécutables +echo -e "${YELLOW}Configuration des permissions...${NC}" +chmod +x "$PROJECT_DIR/scripts/ssh-url" +chmod +x "$PROJECT_DIR/app/ssh-launcher-gtk.py" +echo -e " ✓ Scripts rendus exécutables" + +# Mettre à jour les chemins dans les fichiers +echo -e "${YELLOW}Configuration des chemins...${NC}" + +# Mettre à jour extension.js avec le bon chemin +sed -i "s|const APP_PATH = .*|const APP_PATH = GLib.build_filenamev(['$PROJECT_DIR', 'app', 'ssh-launcher-gtk.py']);|" \ + "$PROJECT_DIR/extension/extension.js" +echo -e " ✓ extension.js mis à jour" + +# Mettre à jour ssh-url.desktop +sed -i "s|Exec=.*|Exec=$PROJECT_DIR/scripts/ssh-url %u|" \ + "$PROJECT_DIR/desktop/ssh-url.desktop" +echo -e " ✓ ssh-url.desktop mis à jour" + +# Mettre à jour ssh-launcher-gtk.desktop +sed -i "s|Exec=.*|Exec=python3 $PROJECT_DIR/app/ssh-launcher-gtk.py|" \ + "$PROJECT_DIR/app/ssh-launcher-gtk.desktop" +echo -e " ✓ ssh-launcher-gtk.desktop mis à jour" + +echo "" + +# Installer le handler SSH +echo -e "${YELLOW}Installation du handler SSH...${NC}" +cp "$PROJECT_DIR/desktop/ssh-url.desktop" ~/.local/share/applications/ +xdg-mime default ssh-url.desktop x-scheme-handler/ssh +echo -e " ✓ Handler ssh:// installé" + +# Installer l'application dans les applications locales +echo -e "${YELLOW}Installation de l'application...${NC}" +cp "$PROJECT_DIR/app/ssh-launcher-gtk.desktop" ~/.local/share/applications/ +echo -e " ✓ Application ajoutée au menu" + +# Mettre à jour la base de données des applications +update-desktop-database ~/.local/share/applications/ 2>/dev/null || true +echo -e " ✓ Base de données mise à jour" + +echo "" + +# Installer l'extension GNOME Shell +echo -e "${YELLOW}Installation de l'extension GNOME Shell...${NC}" +EXTENSION_DIR="$HOME/.local/share/gnome-shell/extensions/ssh-launcher@local" + +mkdir -p "$EXTENSION_DIR" +cp "$PROJECT_DIR/extension/metadata.json" "$EXTENSION_DIR/" +cp "$PROJECT_DIR/extension/extension.js" "$EXTENSION_DIR/" +cp "$PROJECT_DIR/extension/stylesheet.css" "$EXTENSION_DIR/" +echo -e " ✓ Extension copiée dans $EXTENSION_DIR" + +echo "" +echo -e "${GREEN}=== Installation terminée ===${NC}" +echo "" +echo -e "${YELLOW}Prochaines étapes :${NC}" +echo "1. Redémarrez GNOME Shell (Alt+F2, tapez 'r', Entrée) ou déconnectez-vous" +echo "2. Activez l'extension avec:" +echo " gnome-extensions enable ssh-launcher@local" +echo "" +echo "3. Ou activez via GNOME Extensions / Extension Manager" +echo "" +echo -e "${YELLOW}Test immédiat de l'application :${NC}" +echo " python3 $PROJECT_DIR/app/ssh-launcher-gtk.py" +echo "" +echo -e "${YELLOW}Configuration :${NC}" +echo " Éditez $PROJECT_DIR/config/equipements.yaml" +echo "" diff --git a/scripts/ssh-url b/scripts/ssh-url new file mode 100755 index 0000000..9ae7411 --- /dev/null +++ b/scripts/ssh-url @@ -0,0 +1,47 @@ +#!/bin/bash +# +# ssh-url - Handler pour les URLs ssh:// +# Reçoit une URL ssh://user@host et ouvre un terminal avec la connexion SSH +# + +# URL reçue en argument (ex: ssh://gilles@10.0.0.24) +URL="$1" + +if [ -z "$URL" ]; then + echo "Usage: $0 ssh://user@host" + exit 1 +fi + +# Extraire la partie user@host en supprimant le préfixe ssh:// +SSH_TARGET="${URL#ssh://}" + +# Supprimer un éventuel / final +SSH_TARGET="${SSH_TARGET%/}" + +if [ -z "$SSH_TARGET" ]; then + echo "Erreur: impossible d'extraire la cible SSH de l'URL: $URL" + exit 1 +fi + +# Détecter le terminal disponible et lancer SSH +# Liste des terminaux supportés (ordre de préférence) +if command -v gnome-terminal &> /dev/null; then + gnome-terminal -- ssh "$SSH_TARGET" +elif command -v xfce4-terminal &> /dev/null; then + xfce4-terminal -e "ssh $SSH_TARGET" +elif command -v konsole &> /dev/null; then + konsole -e ssh "$SSH_TARGET" +elif command -v xterm &> /dev/null; then + xterm -e ssh "$SSH_TARGET" +elif command -v terminator &> /dev/null; then + terminator -e "ssh $SSH_TARGET" +elif command -v tilix &> /dev/null; then + tilix -e "ssh $SSH_TARGET" +else + # Fallback: notification d'erreur si possible + if command -v notify-send &> /dev/null; then + notify-send "SSH Handler" "Aucun terminal graphique trouvé" + fi + echo "Erreur: aucun terminal graphique trouvé" + exit 1 +fi diff --git a/upgrade_1.md b/upgrade_1.md new file mode 100644 index 0000000..8280d0e --- /dev/null +++ b/upgrade_1.md @@ -0,0 +1,189 @@ +# upgrade_1.md +## Extension GNOME Shell : Icône dans la barre + Popup configurable + YAML + +Ce document définit les nouvelles directives pour faire évoluer l’application actuelle vers une intégration complète dans GNOME Shell. + +--- + +# 1. Objectif général + +L’application doit désormais inclure **une extension GNOME Shell** qui : + +1. Ajoute **une icône dans la barre supérieure** (Top Bar). +2. Ouvre, au clic sur cette icône, **une fenêtre popup dédiée** affichant un tableau interactif d’équipements et de services. +3. Génère ce tableau **à partir d’un fichier YAML**, permettant de modifier les équipements sans toucher au code. +4. Gère l’ouverture d’URL (SSH, HTTP, HTTPS…) associées à chaque service. + +Ce fichier décrit également le **processus à suivre pour Claude Code** afin d’analyser, proposer, puis implémenter les fonctionnalités. + +--- + +# 2. Nouvelle architecture fonctionnelle + +## 2.1 Extension GNOME Shell : icône dans la barre supérieure + +L’extension doit : + +- Ajouter une **icône** dans le panel, visible en permanence. +- Au clic, afficher un **popup** ou un **panneau flottant** contenant la liste des équipements. +- Le popup doit pouvoir être **fermé**, soit via un second clic sur l’icône, soit par toute action prévue par GNOME Shell. + +### Contenu du popup + +Le popup devra afficher : + +- Une liste d’**équipements** (PC, serveurs, VM…) +- Pour chaque équipement : + - Un label (nom) + - Une liste de **services** associés + - Pour chaque service : + - une icône + - un nom + - une URL à ouvrir + +--- + +# 3. Fichier de configuration YAML + +Créer le fichier : + +``` +config/equipements.yaml +``` + +Structure attendue : + +```yaml +equipements: + - nom: "PC Bureau" + ip: "10.0.0.24" + services: + - nom: "SSH" + icon: "terminal" + url: "ssh://gilles@10.0.0.24" + - nom: "Interface Web" + icon: "web-browser" + url: "http://10.0.0.24:8080" + + - nom: "Serveur Proxmox" + ip: "10.0.0.10" + services: + - nom: "SSH" + icon: "terminal" + url: "ssh://root@10.0.0.10" + - nom: "Proxmox UI" + icon: "server" + url: "https://10.0.0.10:8006" +``` + +Notes : + +- Un équipement peut contenir plusieurs services. +- Chaque service doit afficher une icône. +- Tous les champs doivent être extensibles (ajouter plus d’attributs dans le futur si besoin). + +--- + +# 4. Contraintes techniques GNOME Shell + +Il est important de tenir compte des restrictions : + +- **WebKitGTK est interdite dans les extensions GNOME Shell**. + Impossible d’intégrer du vrai HTML directement. + +Donc le popup devra être : + +- soit un **ensemble de widgets St.*** créés depuis le YAML, +- soit une **fenêtre externe GTK** (possible via un script ou app séparée). + +L’analyse devra tenir compte de ces contraintes. + +--- + +# 5. Analyse obligatoire avant développement (Claude) + +Claude doit impérativement produire une **analyse complète** avant toute modification du projet. + +## 5.1 Ce que Claude doit analyser + +Claude doit étudier plusieurs architectures possibles, notamment : + +### **A : Popup GNOME Shell natif (widgets St)** +- Parsing YAML dans l’extension. +- Construction d’interfaces entièrement via St.BoxLayout / St.Icon / St.Button. + +### **B : Extension + application GTK externe (WebKit)** +- L’extension déclenche une application GTK qui affiche du vrai HTML. + +### **C : Extension GNOME Shell + navigateur en mode app** +- L’extension ouvre une fenêtre Chromium/Webkit en mode application. + +### **D : Serveur local + widgets GNOME Shell générés depuis un JSON/YAML** +- Génération HTML ou JSON via fichier local. + +Pour chaque solution, Claude doit fournir : + +- avantages, +- inconvénients, +- complexité, +- compatibilité, +- performances, +- ergonomie. + +### Il doit produire : + +- un **tableau comparatif**, +- une **recommandation ordonnée**. + +**Aucun code ne doit être généré avant mon choix explicite.** + +--- + +# 6. Protocole de travail imposé à Claude Code + +### **`UPGRADE:1 ANALYSE`** +Claude réalise : +- analyse complète, +- comparaison des options, +- pas de code. + +### **`UPGRADE:1 CHOIX `** +Je choisis l’architecture souhaitée. + +Claude : +- confirme l’impact, +- annonce quels fichiers seront créés ou modifiés, +- pas encore de code. + +### **`UPGRADE:1 APPLY`** +Claude applique : +- création et mise à jour des fichiers, +- ajout de l’extension GNOME Shell, +- parser YAML, +- popup dynamique, +- URLs fonctionnelles. + +### **`UPGRADE:1 DOC`** +Claude met à jour la documentation utilisateur. + +--- + +# 7. Résultat attendu + +À la fin de cette évolution : + +- Une **icône** apparaît dans la barre GNOME. +- Un **popup dynamique** s’ouvre avec la liste des équipements. +- Le contenu du popup provient du fichier **YAML**. +- Les clics déclenchent l’ouverture des URLs. +- L’extension est propre, documentée, testable. + +--- + +# 8. Résumé + +Ce fichier sert de **référence officielle** pour diriger l’analyse et le développement par Claude Code : + +- Analyse → Choix → Implémentation → Documentation. +- Aucun code avant `UPGRADE:1 APPLY`. +