feat: rendre les operations SFTP asynchrones
RemoteFilePanel appelait sftp.listdir_attr/mkdir/rename/remove/chmod/get directement depuis le thread GUI : un dossier distant lent ou une latence reseau gelait toute l'interface. Seuls upload/download passaient deja par un thread dedie (TransferWorker), et celui-ci reutilisait le meme SFTPClient que la navigation - paramiko.SFTPClient n'est pas concu pour etre pilote par plusieurs threads a la fois. - app/sftp/worker.py : SftpWorker, un thread proprietaire exclusif d'un canal SFTP, avec une file de jobs (callables) et les resultats renvoyes par signal Qt (marshalling automatique vers le thread GUI). - RemoteFilePanel soumet desormais toutes ses operations via ce worker avec callbacks succes/erreur ; un compteur de generation ignore les reponses de navigations devenues obsoletes (double-clic rapide, edition puis navigation ailleurs pendant le renvoi). - TransferWorker ouvre son propre canal SFTP (`sftp_factory`) au lieu de partager celui de la navigation : un gros transfert ne bloque plus le listage de dossiers. SFTPPanel(sftp, transfer_sftp_factory=...) permet d'injecter ce second canal (paramiko.SFTPClient.from_transport) ; a defaut, le canal de navigation est reutilise tel quel (cas des tests). - SessionTabWidget/SFTPTabWidget ouvrent ce second canal et appellent SFTPPanel.shutdown() a la fermeture de l'onglet (arrete proprement les threads avant destruction des widgets). tests/test_sftp.py adapte pour pomper la boucle d'evenements Qt apres les operations desormais asynchrones (meme couverture, timing different). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in: