You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Toutes les routes SSH requièrent le header `X-SSH-Session` contenant l'UUID de session.
112
137
@@ -385,6 +410,54 @@ git tag v0.1.0 && git push --tags # déclenche le workflow
385
410
- Conversion des chemins WSL → Windows via `wslpath -w` avant de passer en argument
386
411
-`second-instance` handler : `w.show()` + `setAlwaysOnTop(true/false)` pour forcer la fenêtre au premier plan sur Windows (contourne la protection anti-focus-stealing)
387
412
413
+
### Electron preload (IPC bridge)
414
+
-`electron/preload.cjs` : script de preload Electron — expose deux API au renderer via `contextBridge.exposeInMainWorld`
415
+
-**`window.electronSession`** : `getInitialRoot()` (lit `?root=` depuis l'URL de la fenêtre), `setRoot(path)` (IPC `session:setRoot` pour persister le dossier courant)
416
+
-**`window.electronTerminal`** : `create(opts)`, `write(id, data)`, `resize(id, cols, rows)`, `kill(id)`, `onData(cb)` → retourne unlisten, `onExit(cb)` → retourne unlisten — pont IPC vers node-pty dans le process principal
417
+
418
+
### Launcher Unix (`bin/locode`)
419
+
- Script shell `bin/locode` : résout l'argument de chemin en absolu, cherche une AppImage dans `dist/`, la lance si trouvée, sinon fallback sur `npx electron electron/main.cjs` (mode dev)
420
+
- Usage : `bin/locode .` ou `bin/locode /path/to/project`
421
+
- Différent du CLI stub Windows (`cli/locode.exe` installé dans PATH) — celui-ci est pour macOS/Linux sans installation globale
422
+
423
+
### Session collaborative (partage)
424
+
425
+
#### Vue d'ensemble
426
+
-`app/composables/useShare.ts` : état de partage global — `isHost`, `isGuest`, `shareId`, `guestId`, `shareInfo` (rootPath, allowTerminal, hostName, guests), WebSocket de contrôle, relay WS (desktop host), fonctions `startShare()`, `stopShare()`, `joinShare()`, `leaveShare()`
427
+
-`app/components/ShareModal.vue` : modal UI — création de partage (copie lien, liste guests, toggle terminal, stop), join session (input lien + nom optionnel)
428
+
-`server/utils/share.ts` : gestionnaire de sessions (`Map<shareId, ShareSession>`), CRUD, relay request/response avec timeout 30s, registry terminaux partagés avec buffer replay 50KB, `broadcastControl()` vers tous les peers de contrôle
-**Mode direct** (web host, SSH sur Railway) : requêtes fichiers guests proxifiées directement via `getSftp(hostSessionId)` ou fs Node.js local
431
+
-**Mode relay** (desktop host) : Railway envoie la requête via `_share-relay` WS → desktop exécute localement → réponse retournée → Railway répond au guest
432
+
- Sécurité : `isPathWithinRoot()` valide tous les chemins fichiers guests contre le `rootPath` de la session
433
+
434
+
#### Architecture relay pour terminaux
435
+
- En mode relay (desktop host), les terminaux partagés utilisent le pont IPC Electron (`window.electronTerminal`) pour spawner le PTY dans le process principal Electron — pas le WebSocket `/_terminal` (Nuxt subprocess) qui échoue avec `posix_spawnp failed` dans ce contexte
436
+
-`index.vue` gère une `Map<string, RelayTerminalHandle>` (`relayTerminals`) qui abstrait Electron IPC ou WebSocket selon le contexte (Electron vs web/SSH)
437
+
- Chaque handle expose `sendInput(data)`, `sendResize(cols, rows)`, `close()` — les callbacks relay (`onRelayTerminalCreate/Input/Resize/Close`) appellent la méthode appropriée selon l'environnement
438
+
439
+
#### Pattern setter ESM pour callbacks relay
440
+
- Les variables `onRelayTerminalCreate`, `onRelayTerminalInput`, `onRelayTerminalResize`, `onRelayTerminalClose`, `onShareClosed` dans `useShare.ts` sont des `let` privés exposés via des fonctions setter exportées (`setOnRelayTerminalCreate`, etc.)
441
+
-**Raison** : les bindings `export let` sont en lecture seule dans les namespaces ES modules — l'assignation `mod.onXxx = callback` échoue silencieusement dans les builds Rollup/Vite production. Les setters contournent ce problème
442
+
-`index.vue` importe et appelle directement ces setters de manière synchrone (plus d'IIFE async `await import(...)`)
443
+
444
+
#### Nettoyage propre à l'arrêt du partage
445
+
- Le watcher `isSharing` dans `TerminalPanel.vue` gère les deux transitions (start **et** stop) : vide la liste des sessions et réinitialise l'état dans les deux cas
446
+
- À l'arrêt (`isSharing` passe à `false`) : `index.vue` appelle `terminalPanelRef.value?.ensureSession()` pour créer des sessions terminales normales fraîches
447
+
- Les handles relay (`relayTerminals`) sont tous fermés dans `onShareStopped()` avant la re-création des sessions
448
+
449
+
#### `terminal-ready` pour les abonnés
450
+
-`server/routes/_share-terminal.ts` : le handler `subscribe` envoie maintenant un message `{ type: "terminal-ready", terminalId, name }` en confirmation — permet au client de définir `sharedTerminalId` et d'activer correctement le routage input/resize
451
+
- Sans cette confirmation, les guests abonnés à un terminal existant ne pouvaient pas envoyer d'input
452
+
453
+
#### Sync dimensions PTY après `terminal-ready`
454
+
-`Terminal.client.vue` : après réception de `terminal-ready` (création et abonnement), force un `doFit()` + envoie `resize` avec les dimensions réelles de l'xterm
455
+
- Corrige le glitch zsh `%` (PROMPT_SP/PROMPT_CR) : le caractère apparaissait quand les dimensions PTY ne correspondaient pas aux dimensions xterm au moment de l'abonnement
456
+
457
+
#### Restauration de session Electron (Windows/Linux)
458
+
-`electron/main.cjs` : sur Windows/Linux, `win.on("closed")` se déclenche **avant**`app.on("before-quit")` — au moment où `before-quit` s'exécute, la map `windows` est déjà vide, donc `saveSessions()` sauvegardait `[]`
459
+
- Fix : dans le handler `closed`, si c'est la dernière fenêtre et que `isQuitting` est false, écrire directement le fichier de session avant de supprimer la fenêtre de la map
460
+
388
461
### Refactoring code cleanup
389
462
- Suppression de `server/api/[...url].ts` (ancien proxy Deno catch-all, remplacé par routes `/api/local/*` et `/api/ssh/*`)
390
463
-`useApi.ts` simplifié : déduplication logique protocole dans `getWsUrl()`, appel unique `getStoredSSHTarget()` par fonction
0 commit comments