Dernière revue : 2026-08-08
Historique de ce qui est déjà livré dans AI-HISTORY.html. Pièges
techniques et invariants du code déjà en place dans AI-CONTEXT.html.
L'état et le détail des chantiers livrés sont au
registre du réalisé (option registre-livrés,
2026-08-08) — cette page ne garde de chacun qu'une ligne de renvoi, à sa position d'origine.
Ce qu'un chantier livré laisse malgré tout ouvert est isolé dans
Reste ouvert dans les chantiers livrés.
| Chantier | Statut | Priorité |
|---|---|---|
| Socle (favoris, statut live, drag & drop, menus contextuels, icônes) | ✅ Fait | — |
| Publication npm | ✅ Fait | — |
| Menu contextuel enrichi (Profils & Groupes) | ✅ Fait | — |
| Options & polissage UI | ✅ Vidé — plus aucun item ouvert (2026-08-08) | — |
| Vault Auto-Unlock | ⛔ Hors périmètre | Plugin séparé futur |
| Importateur MobaXterm | ⛔ Écarté | — |
| Éditeur par type de fichier (images) | ⛔ Écarté (2026-08-03) | — |
| Validation automatique (Auto-Enter) | ⛔ Hors périmètre (2026-08-03) | Utilité à établir avant tout portage |
| Durcir l'import de dossier collé (sécurité) | ✅ Corrigé (2026-08-08) | — |
| Envoyer une commande à N sessions choisies | 📋 Retenu, non ouvert (2026-08-08) | Après V1.0 |
Étapes, délais et expect dans un snippet | 📋 Retenu, non ouvert (2026-08-08) | Après V1.0 |
| Outils réseau au menu contextuel | ⚠️ Sans décision (2026-08-08) | — |
| Vulnérabilités npm de l'outillage de build | ✅ Lot sans majeure livré et validé (2026-08-14) | Surveillance — 18 restantes bloquées par Angular 15 / pug 2 |
| Montée de version TypeScript / webpack-cli | 📋 Retenu, non ouvert (2026-08-14) | Basse — période calme |
| CHANGELOG.md du dépôt | ✅ Fait — introduit avec la 1.0.3 (2026-08-14) | À tenir à chaque publication |
| Intégration X11 | ⛔ Écarté — déjà natif | — |
| Gestionnaire de macros visuel | ⛔ Écarté — redondant | — |
| Workspaces distants / partage d'équipe | ⛔ Écarté — contrainte intenable | — |
| Conformité de la gouvernance à la charte | ✅ À jour — remise à niveau 20260804-071239 (2026-08-08) |
Entretien récurrent |
Le tableau ci-dessus dit d'un chantier qu'il est fait dès que ce qui le justifiait est en service et validé — pas qu'il ne reste rien de son périmètre d'origine. Ce qui a été explicitement différé, laissé à arbitrer ou jamais exercé est donc rassemblé ici, chaque ligne renvoyant au chantier qui le porte. Cette liste n'a aucune existence propre : elle ne fait que rendre visible ce qui est déjà écrit dans les fiches — actives ici, ou au registre du réalisé pour les chantiers livrés —, et se vide en même temps qu'elles. Ne jamais y consigner un point qui ne serait pas aussi dans sa fiche — ce serait le deuxième endroit où lire le même fait.
| Ce qui reste | Chantier | Nature |
|---|---|---|
Flèches d'activité ↑/↓ sur la ligne du profil concerné
(profileActivity). |
SFTP contextuel | ✅ Fait (2026-08-08) |
Gel de la vue : livré comme épinglage automatique de l'onglet lié
(frozen), pas comme un interrupteur manuel. |
SFTP contextuel | ✅ Fait (2026-08-08) |
| Chargement de la liste par blocs — listing distant paginé. | SFTP contextuel | ✅ Fait (2026-08-08) |
Icône par workspace (champ icon sur SidebarWorkspace, picker ouvert
depuis le menu contextuel de l'onglet). |
Workspaces | ✅ Fait (2026-08-08) |
Sélecteur adaptatif : abandonné au profit d'un réglage explicite workspaceSelectorMode
(tabs/dropdown), la bascule automatique par ResizeObserver
ayant été refusée au test. |
Workspaces | ✅ Fait, redessiné (2026-08-08) |
Couleurs contextuelles — livrées via le champ color natif des profils (icône
teintée + barre sous l'onglet), pas via une bordure de terminal comme envisagé au cadrage. |
Workspaces | ✅ Fait, redessiné (2026-08-08) |
Export/import JSON ponctuel des workspaces (src/workspaceShare.ts). |
Workspaces | ✅ Fait (2026-08-08) |
| Les trois propositions du test manuel du 2026-07-29 : masquage par défaut des créations dans les workspaces existants et comportement configurable de la barre de filtre restent écartés en statu quo ; la levée de l'ambiguïté « réordonner » / « imbriquer » au glisser-déposer est livrée partiellement (surbrillance ciblée sur « imbriquer », ligne d'insertion non livrée). | Workspaces — propositions | ✅ Fait, partiel (2026-08-08) |
| États de transfert SFTP (vitesse, file d'attente) sur la ligne de session. | Sessions actives | ✅ Fait (2026-08-08) |
Chemin distant et session porteuse sur la ligne du gestionnaire de transferts — livrés en
infobulle de la ligne (champs plats composés à l'inscription, piège #54 respecté),
la ligne restant compacte. Contexte fourni par les appelants : le registre ne peut pas le deviner,
FileTransfer n'expose que getName(). |
Transferts SFTP | ✅ Fait (2026-08-07) |
| Multi-sélection dans la liste distante — fichiers et dossiers, préalable au déplacement et à la suppression par lot. | Transferts SFTP | ✅ Fait (2026-08-08) |
Confirmation avant annulation d'un transfert en cours (bouton poubelle/balai du gestionnaire).
Non pilotable par CDP — composant transferts monté par Angular en mode production, inatteignable —
délégué à l'œil de l'utilisateur ; mécanisme déjà prouvé via ConfirmModalComponent. |
Transferts SFTP | ✅ Fait, validé à l'œil (2026-08-08) |
Retour automatique sur la vue Profils quand plus aucun onglet n'a de SFTP actif — réglage
sftpAutoReturnToProfiles, activé par défaut, distinct du délai de
grâce de 3 s. |
Panneau SFTP — état de session | ✅ Fait (2026-08-08) |
Bascule sur exit : le code la couvre (isLiveSSHTab() lit le shell).
Exercée et validée par l'utilisateur le 2026-08-07 (profil gardant l'onglet ouvert
en fin de session). |
Panneau SFTP — état de session | ✅ Validé (2026-08-07) |
downloadFolder ne passe plus par la voie héritée : « Download directory » ouvre
notre pickDirectory(), descend l'arbre par downloadRemoteTree() (4 fichiers
de front, une ligne de transfert chacun), refuse les collisions à la façon Explorateur
(freeLocalName(), partagé avec la livraison du marqueur) — et une coupure dit
« interrompu à N % », vérifié en coupant le transport à 23 %. |
Panneau SFTP — état de session | ✅ Fait (2026-08-07) |
Contrôle « le fichier est-il bien arrivé ? » — livré sur toute voie à chemin
imposé (SftpTransfers.download()) : taille locale confrontée au listing, puis à une
relecture fraîche, puis à la cible résolue d'un lien symbolique (le premier passage
du contrôle a pris la taille du lien pour celle du fichier — résolveur borné à 8 sauts partagé dans
remoteEntry.ts). Échec = état unsound « incomplet à destination », distinct
d'« interrompu » : rien ne s'est cassé en vol, la copie n'est juste pas ce qu'elle devrait être. |
Panneau SFTP — état de session | ✅ Fait (2026-08-07) |
Le dialogue « Enregistrer sous » d'un téléchargement de fichier par menu
contextuel affiche « 127.0.0.1 » (l'hôte de l'URL de l'offre HTTP locale — le dossier n'y passe
plus). Texte arbitraire impossible ; les deux alternatives (« localhost » au risque IPv6, ou la
voie platform.startDownload() à dialogue) ont été présentées et
écartées par l'utilisateur le 2026-08-07 — l'adresse est exacte, c'est bien un
serveur local qui sert le fichier. |
Panneau SFTP — état de session | Écart assumé (2026-08-07) |
Repli du marqueur — livraison dans ~/Downloads quand il n'est pas retrouvé.
Exercé et validé par l'utilisateur le 2026-08-07. |
Marqueur de dépôt | ✅ Validé (2026-08-07) |
| État de reprise sur micro-coupure : livré, avec le dédoublonnage des tunnels multiplexés (la vraie cause des lignes en 3-4 exemplaires) — détail dans la fiche Tunnels SSH. | Tunnels SSH | ✅ Fait (2026-08-07) |
| Comparer les deux identifiants de charte en début de session, repropager les copies à chaque révision, corriger la dérive dès qu'elle est constatée. | Conformité de la gouvernance | Récurrent |
| Passe de test d'une release avant publication npm. Ne relève plus du ménage, qui est clos — c'est du test, pas du rangement. Menée le 2026-08-08 pour la 1.0.0 : 15 tests sur le paquet installé hors jonction, en build de production. Le protocole est réutilisable tel quel à chaque publication. | Ménage avant release | ✅ Fait (2026-08-08) |
Ces points ont été tranchés, vérifiés faux ou rendus sans objet. Les relire comme des manques ferait rouvrir un travail déjà fait :
Les deux fonctionnalités qui justifiaient de remplacer MobaXterm par Tabby + ce plugin — le SFTP contextuel et les workspaces. Tout ce qui a suivi est né de leur usage réel : cette phase a grossi au fil des passes de test, ce qui explique qu'elle porte aujourd'hui bien plus que deux entrées.
SFTP contextuel — au registre du réalisé.
Workspaces — au registre du réalisé.
Section "Sessions Actives" en haut de la sidebar — au registre du réalisé.
Transferts SFTP : gestionnaire horodaté & glisser-déposer — au registre du réalisé.
Panneau SFTP : suivre l'état réel de la session — au registre du réalisé.
Sessions actives visibles aussi en vue SFTP — au registre du réalisé.
Suppression SFTP par touche Suppr — au registre du réalisé.
Double-clic force un éditeur de code, jamais une exécution — au registre du réalisé.
Glisser-déposer sortant (téléchargement vers l'OS) — au registre du réalisé.
Le marqueur de dépôt (volet de ce chantier) est au registre du réalisé — ancre conservée pour les liens du journal.
Transferts par l'API officielle, fraîcheur des copies et purge des temporaires — au registre du réalisé.
Performance de la liste SFTP et rafraîchissement automatique — au registre du réalisé.
Renommer une entrée depuis le panneau SFTP — au registre du réalisé.
Ctrl+Entrée insère un saut de ligne dans le terminal — au registre du réalisé.
Intégrité des données au re-parentage et à la suppression — au registre du réalisé.
Édition d'un fichier atteint par un lien symbolique — au registre du réalisé.
Cycle de vie : démontage, fenêtres multiples, offres de glisser — au registre du réalisé.
Confirmations en modale HTML (audit de conformité) — au registre du réalisé.
Sélection multiple — au registre du réalisé.
Tunnels SSH — au registre du réalisé.
Ouverture groupée d'un dossier ("Group Exec") — au registre du réalisé.
Clone Express de profil — au registre du réalisé.
Passer les popups riches en modales centrées — au registre du réalisé.
Historique des Profils Récents — au registre du réalisé.
Quick Snippets (Macros de Sidebar) — au registre du réalisé.
Internationalisation (i18n) — au registre du réalisé.
Favoris d'icônes (distincts des « Récentes ») — au registre du réalisé.
Icônes dashboard-icons (logos de services IT) — au registre du réalisé.
Aucun de ses items n'a jamais été construit « pour lui-même » : chacun est arrivé en dépendance
d'un autre chantier, et le statut global n'a jamais suivi — d'où ce chapô, deux fois en retard sur
le code. L'agencement de la page de paramètres est arrivé avec les
interrupteurs par bloc ; les deux derniers points qu'il
annonçait encore ouverts au 2026-08-03 le sont depuis : l'icône d'engrenage vit
dans le pied de page de la sidebar (.sidebar-plus-gear, livrée avec lui en 0.3.0) et
le gel de la vue est livré comme épinglage de l'onglet lié
(frozen dans sftpPanel.component.ts, 2026-08-08). Constaté le 2026-08-08
en recensant ce qui restait ouvert ; rien à construire ici, la fiche reste pour la trace de ce qui
a été livré par ailleurs.
Les quatre sections décrites ci-dessous supposaient un classement par sujet. La page est finalement organisée par appartenance, sur la règle posée par l'utilisateur : une option vit avec l'interrupteur de la fonctionnalité dont elle dépend et disparaît avec elle ; seul ce qui régit le plugin entier ou Tabby reste en « Général ». Deux onglets suffisent alors — « Général » et « Fonctionnalités » — là où le plan en prévoyait quatre, parce que trois des quatre rubriques n'étaient que des regroupements thématiques de réglages qui appartiennent chacun à un bloc. Détail dans Interrupteurs par bloc.
Plan d'origine, conservé pour la trace :
Structurer l'interface de configuration du plugin en 4 sections distinctes :
sftpAutoRefreshSeconds,
0 par défaut ; restent l'interrupteur de gel de la vue et l'historique de file d'attente.tabby-settings) existe depuis le 2026-07-30
✅ ; l'icône d'engrenage discrète en bas à droite de la
sidebar, pointant vers les mêmes réglages, reste à construire.src/hostChrome.scss appliqué par
une classe que SidebarPlusMountService pose et retire — un plugin désactivé doit
laisser l'hôte tel qu'il l'a trouvé. Réglage hideNativeTransfersMenu, coché par
défaut. Le mécanisme est réutilisable pour tout autre composant natif à masquer.HotkeyProvider en place (src/hotkeys.ts), premier usage réel avec
Ctrl+Entrée. Les raccourcis envisagés ici —
changer de workspace, basculer vers le SFTP actif, afficher les tunnels — ne sont pas mappés : ils
se posent au coup par coup, la plomberie ne les bloque plus.tabby-better-vault
pour celui de « Better Sidebar » ✅. Demandé pendant la passe de
test du drag-out. L'onglet est né le 2026-07-30 avec une seule option et
n'avait jamais reçu de mise en forme propre ; l'autre plugin de l'utilisateur a servi de
référence, ce qui aligne les deux... portent la même classe que les entrées et sont
donc exclus du geste.Partage rapide de groupe (Presse-papiers) — au registre du réalisé.
Notes de profil (Mémento contextuel) — au registre du réalisé.
Indicateur de santé (Ping live) — au registre du réalisé.
Robustesse — vérifier le couplage à l'hôte au démarrage — au registre du réalisé.
Combler les points de veille restants — au registre du réalisé.
Édition de profil par l'API plutôt que par le DOM — au registre du réalisé.
Interrupteurs par bloc de la sidebar — au registre du réalisé.
20260804-071239 (2026-08-08)Revue faite le 2026-07-29 (A-15, Cas B) — écarts constatés et corrigés détaillés dans
AI-HISTORY, commit df8ef82. Ce n'est pas un état acquis :
la charte évolue, ce projet doit suivre.
20260804-071239
✅ Fait (2026-08-08)Trois révisions traversées depuis 20260803-182826 : 20260803-200821 et
20260804-071239 purement additives, 20260804-064238 touchant le noyau
(deuxième exception nommée d'A-4 — conversion de format du journal — et option
registre-livrés). Menée le 2026-08-08 : copies verbatim vérifiées par empreinte Git,
et les deux options ajoutées tranchées explicitement —
registre-livrés = oui (la roadmap dépassait 3 300 lignes, portées surtout
par des chantiers déjà livrés), journal-format = tableau (statu quo,
l'exception de conversion d'A-4 reste inutilisée). Motifs dans
PROFIL.md.
Application dans le même geste : bascule initiale de 33 chantiers livrés vers
annexes/REALISE.html, fiches reprises verbatim, la roadmap ne
gardant qu'une ligne de renvoi par chantier à sa position d'origine (ancres conservées — les liens
existants continuent d'aboutir). La fiche Écartée « Éditeur par type de fichier » a rejoint
« Hors périmètre », où vivent les idées tranchées. Les branches de sync du 2026-08-04, créées avant
la fusion de la phase 0.3.0 et donc périmées (les fusionner aurait annulé la phase), ont été
supprimées et la propagation refaite depuis master.
20260731-204511Engagée le 2026-07-31. Trois révisions traversées depuis la dernière conformité du projet :
20260731-150737 et 20260731-203812 touchent le noyau — elles ne
s'appliquent jamais d'office (A-7) —, 20260731-204511 est purement
additive. Vérifié à la fusion : aucune option ajoutée ou retirée (22 de part et
d'autre) et les quinze invariants sont inchangés, donc aucune question de cadrage
nouvelle à arbitrer. La remise à niveau est terminée : les cinq items ci-dessous
ont été menés dans l'ordre où ils sont listés, le pied de page en dernier comme le veut la règle
rappelée plus bas.
| Item | Statut |
|---|---|
Copies verbatim de GOUVERNANCE-IA.md et GABARITS.md —
20260731-204511, vérifiées par empreinte Git |
✅ Fait (2026-07-31) |
Créer PROFIL.md — cadrage complet, profil complet,
22 options tranchées |
✅ Fait (2026-07-31) |
Migrer les références « Règle N » vers les identifiants A-x |
✅ Fait (2026-07-31) |
Audit complet A-15, Cas B — les quatre axes menés le
2026-07-31, dates de dernière revue mises à jour en conséquence |
✅ Fait (2026-07-31) |
Mention de conformité du pied de page de
README.html : « du 2026-07-30 », forme datée antérieure aux
identifiants, devient « version 20260731-204511 » |
✅ Fait (2026-07-31) |
| Axe | Résultat |
|---|---|
Contexte — 46 pièges confrontés au code de src/ |
Trois corrigés en place, aucun supprimé : #19 décrivait un correctif
(min-height: 8px inconditionnel) qui n'existe plus, remplacé par celui de
#26 ; #33 supposait
qu'on montait SFTPPanelComponent lui-même, alors que le code monte une sous-classe
à template propre dont les :host hérités ne s'appliquent jamais ;
#5 justifiait --ignore-scripts par une
dépendance retirée depuis, le flag restant nécessaire pour une autre. |
| Roadmap — 16 statuts « Fait » confrontés au dépôt | Aucun à faire redescendre : chacun porte une trace de code et de validation ou de
mise en service. Le glisser-déposer sortant était le cas d'école tenu
correctement — code commité (8a89787), statut maintenu à « Proposé » faute de test
à cette date ; il est passé à « Fait » le lendemain, une fois testé. |
Journal — conformité A-4 |
Conforme, rien à corriger : aucun (commit en cours) survivant, aucune formule
ambiguë, et les treize hashes cités pointent tous vers des commits réels. |
| Index et cohérence croisée | Un écart : ce document annonçait « 24 options de part et d'autre » là où la charte en compte
22 — un comptage qui incluait par erreur deux valeurs de l'option
seuil. L'erreur était symétrique, la conclusion tenait ; le chiffre contredisait
PROFIL.md. Corrigé. |
Socle et Publication npm n'ont pas de mention « testé et validé par l'utilisateur » : cette convention d'écriture n'existait pas encore quand ils ont été livrés. Leur statut repose donc sur la mise en service constatée — paquet publié puis réinstallé et vérifié, jonction NTFS de dev active — plutôt que sur une validation consignée. Les faire redescendre appliquerait rétroactivement une convention postérieure à des chantiers en service.
20260731-204511 le 2026-08-03
Elle dit sous quelle version les options ont été tranchées, pas quelle version le projet possède.
Elle ne devait bouger qu'au moment où le cadrage serait effectivement relu — l'avancer sans
relecture aurait écrit un fait faux dans le fichier qui sert précisément à savoir sous quelle
version chaque option a été décidée. Cette relecture a eu lieu : entretien complet
rouvert le 2026-08-03, vingt-deux clés confrontées une à une, une seule valeur changée
(seuil → roadmap-libre). Détail dans
AI-HISTORY.
Il atteste que le projet suit la charte, pas que sa copie est à jour — d'où son rang
dans la liste ci-dessus, après l'audit et jamais avant. Tant que celui-ci n'a pas eu lieu, l'écart
entre lui et l'identifiant de GOUVERNANCE-IA.md est l'état normal et
lisible que décrit A-7 : « une copie à jour posée à côté d'un projet non remis à niveau
… l'écart entre les deux identifiants est le signal ». La règle vaut pour la prochaine
révision : l'avancer plus tôt afficherait une conformité que personne n'a vérifiée.
Les migrations « Règle N » d'AI-HISTORY.html n'ont
pas été faites, et ne le seront pas : A-4 impose un journal en ajout
seul, et ces mentions nommaient la règle telle qu'elle existait à la date de l'entrée.
Ce qui reste à faire de façon récurrente, indépendamment de toute remise à niveau :
AAAAMMJJ-HHMMSS, monotone : une comparaison de chaînes suffit. Si le premier est
inférieur, signaler l'écart et proposer une remise à niveau — jamais l'appliquer
d'office, un projet pouvant légitimement rester sur une convention antérieure.GOUVERNANCE-IA.md et GABARITS.md à
chaque révision de l'original. L'original canonique vit dans un dépôt public dédié,
indépendant de ce workspace et de toute machine ; les fichiers posés à la racine de
Développement/ n'en sont qu'un miroir de commodité, jamais la source. Les copies de
ce dossier doivent rester conformes : jamais adaptées au projet, comparables à l'original
par un diff — en tenant compte du piège
#47 sur les fins de ligne.Ménage avant release soignée — au registre du réalisé.
Issus d'un backlog de cinq idées proposé par l'utilisateur le 2026-08-08 et
instruit contre le code réellement installé (Tabby 1.0.235, sources TypeScript livrées). Trois des
cinq sont parties en « Hors périmètre » ci-dessous ; les deux qui suivent sont ce qu'il en reste
d'utile. Aucune n'est ouverte : elles attendent une consigne explicite
(option roadmap-avant-code).
Ce qui subsiste de l'idée « Multi-Exec / Broadcasting » une fois retiré ce que Tabby fait déjà.
Le broadcast natif existe — MultifocusService de
tabby-terminal, raccourcis Ctrl-Shift-I (panes du split) et
Ctrl-Alt-Shift-I (tous les onglets), plus une entrée du menu contextuel d'onglet — donc
un interrupteur maison qui rejoue la frappe serait redondant, et inférieur : le service
s'annule au premier clic dans un terminal sans exposer aucun moyen de l'observer, si bien qu'un
interrupteur afficherait « actif » sur une diffusion morte.
Deux manques réels, que la sidebar est la seule à pouvoir combler puisqu'elle tient déjà les onglets vivants dans « Sessions actives » :
sudo sur un seul hôte avale des caractères destinés à tous. Un composeur « je tape,
je relis, j'envoie la ligne » est plus sûr autant que plus utile.La saisie synchronisée a déjà été refusée une fois — « taper une commande sur douze serveurs de production ne doit pas être l'effet de bord d'un ouvrir tout » (Group Exec, clos le 2026-08-03). Le refus portait sur le couplage à l'ouverture groupée, pas sur la fonction ; un composeur à sélection explicite n'est pas ce qui a été écarté. Mais rien ne doit diffuser par effet de bord : sélection opt-in ligne par ligne, jamais « tout » par défaut.
À trancher avant de coder : miroir de frappe ou envoi de ligne (recommandation :
envoi de ligne) ; le seuil de sessions au-delà duquel une confirmation s'impose ; et le retour par
ligne — sendInput() fait this.session?.feedFromTerminal(...), donc sur une
session morte rien ne se passe et rien ne le dit, ce qui ferait croire la commande
passée partout.
expect dans un snippet
📋 Retenu, non ouvert (2026-08-08)Ce qui subsiste de l'idée « Visual Macro Manager », dont la fiche du registre porte déjà le nom (Quick Snippets — Macros de Sidebar) : un snippet est un texte multi-lignes envoyé tel quel dans le pty, donc déjà une macro multi-commandes, avec variables, héritage profil → dossier → racine et trois réglages tri-état. Ne manquent que deux choses :
launchDelayMs), et seulement après un lancement automatique. Petit.expect : attendre un motif de sortie avant de
continuer. C'est le seul ajout à vraie valeur technique, et il répare une faiblesse que ce
document reconnaît déjà — le délai fixe « ne résout rien, il remplace la chance par un nombre
que l'on choisit ». Atteignable par SessionMiddleware de tabby-terminal
(exporté, vérifié aux deux niveaux), avec un précédent livré dans Tabby lui-même :
tabby-auto-sudo-password lit la sortie et répond.Piège à ne pas rater : l'expect natif compare chunk par
chunk, sans tampon inter-chunks — une invite coupée entre deux paquets est ratée. Un
expect honnête doit tenir son propre tampon glissant et retirer les séquences ANSI.
Voir aussi l'idée non tranchée plus bas.
Quatrième idée du backlog (Ping / Traceroute / Nmap depuis le menu d'un profil, dans un onglet local). Instruite, mais volontairement laissée sans décision : ni retenue, ni écartée. Consignée pour que l'instruction ne soit pas à refaire.
local synthétique passé à
openNewTabForProfile() avec pauseAfterExit: true. La chaîne va jusqu'à
execvp (Unix) ou CreateProcessW (Windows) sans jamais passer par
un shell : un hôte valant x; rm -rf ~ arrive littéralement à
ping, qui répond « hôte inconnu ».-f 10.0.0.1 en deux arguments. Sur
nmap, --script exécute un script Lua arbitraire : c'est une exécution de
code. D'où : pas de champ « chaîne de commande » libre, mais une liste
d'arguments dont un élément vaut {host}, substitué entier, plus une liste blanche sur
l'hôte refusant tout ce qui commence par -. Et pas de Nmap.src/ping.service.ts argumente déjà,
noir sur blanc, le refus d'ICMP — « couramment filtré, ce qui afficherait rouge sur un lien
parfaitement sain », et « aucun processus externe, contrairement à ping.exe ».
Ajouter une entrée « Ping » n'est pas contradictoire (l'un sonde une session vivante, l'autre un
hôte injoignable) mais oblige à écrire la distinction. S'y ajoute que sur un profil à rebond ou
proxy — ceux qu'on diagnostique justement — un ping local vers options.host ne
mesure rien du chemin réel.ping.service.ts ne peut pas
couvrir par construction.Deux pièges relevés à l'instruction, à ne pas redécouvrir : lire
options.port sans regarder type donne COM3 sur un profil
série (l'entrée doit être masquée hors SSH/Telnet) ; et sous Windows, un binaire absent fait ouvrir
un onglet vide qui ne se ferme jamais, sans message — le renderer reçoit son id
avant que le processus principal ne lève l'exception. Détecter avant de lancer.
Issus des trois notes du tableau de bord GitHub de l'utilisateur
(users/TooMuhtsh/projects/1, écrites vers le 2026-08-10), consignées et instruites ici
le 2026-08-14. Les mêmes notes visent aussi tabby-better-vault — sa part se traite
dans ses propres sessions, rien ici ne la couvre. Aucune n'est ouverte : elles attendent une
consigne explicite (option roadmap-avant-code).
Validé par l'utilisateur le 2026-08-14 (SVG de test collé au sélecteur
d'icônes), fusionné sur master (e5b9a55). La passe de validation a fait
naître un correctif connexe, livré et validé le même jour : l'avertissement de retraits passe par
un toast (fusion 4d5c6a3, journal). Reste en surveillance : les 18
vulnérabilités restantes sont bloquées par Angular 15 (imposé par Tabby amont) et pug 2 — rien à
faire ici tant que l'amont ne bouge pas ; re-mesurer à chaque montée de version de Tabby.
Lot « sans majeure » exécuté le 2026-08-14 sur la branche
fix/audit-sans-majeure, sur consigne de l'utilisateur : npm audit fix nu,
22 → 18 vulnérabilités (1 low, 3 moderate, 14 high), les quatre attendues
corrigées — dompurify 3.4.12 → 3.4.13, nanoid 3.3.16 → 3.3.18,
fast-uri 3.1.4 → 3.1.5, brace-expansion 2.1.2 → 2.1.4. Seul
package-lock.json a bougé, package.json est intact ; builds de production
et de développement verts, aucun warning nouveau, dist/ laissé en build de dev pour la
jonction.
Compatibilité de dompurify — le seul du lot qui voyage dans le bundle — instruite
contre l'amont : l'advisory corrigée (GHSA-55q2-fjhq-7xh7) exige
IN_PLACE et un hook de retrait, que le plugin n'utilise nulle part (un unique
appel dans svgSanitizer.ts, profil SVG + allow-lists passées à l'appel, aucun hook) —
nous n'étions pas vulnérables en 3.4.12. Le seul changement du diff sur notre
chemin est le hoisting des enfants par déplacement au lieu de clonage : optimisation, même arbre
final attendu. Validation avant fusion (le décompte « N retirés » alimente un
message utilisateur) : coller dans le sélecteur d'icônes un SVG mêlant balises autorisées et non
autorisées (defs, use, text autour de path/rect)
et vérifier que le rendu et le décompte sont ceux d'avant.
Mesuré le 2026-08-14 : npm audit rend 22 vulnérabilités (1 low, 4 moderate,
17 high), toutes dans les devDependencies. Provenance établie le même jour :
@angular/*, @ng-bootstrap et les pairs
tabby-core/-settings/-terminal, ces trois derniers marqués
fixAvailable: false. Correction = Angular 15 → 19+, bloquée tant que Tabby
amont reste sur Angular 15. Tous sont dans les externals webpack : rien de
ce groupe ne voyage dans notre paquet, c'est le runtime de Tabby qui les porte.pug@2 (pug, pug-code-gen, pug-loader —
outil de build pur, jamais livré). Sortie réelle : pug 2 → 3 plus remplacement du
loader (pug-loader ne déclare que pug ^2 en peer) — le « correctif »
que npm propose est un downgrade de pug-loader en 1.0.2, à ne jamais appliquer
par --force.fast-uri (via
html-validate), nanoid (via css-loader), brace-expansion.dompurify — la seule des 22 qui atteint le paquet
publié : bundlée dans dist/ (utilisée par svgSanitizer.ts et
icons.ts, absente des externals). Correction publiée en
3.4.13, dans la plage ^3.4.12 — aucune majeure.Un npm audit fix sans --force réglerait donc 4 des 22
(dompurify compris) ; les 18 restantes sont conditionnées à Angular 19+ (bloqué par Tabby) et à
pug 3. Non urgent, jugé ainsi par l'utilisateur dans la note d'origine — mais si une seule chose
doit se faire vite, c'est le bump de dompurify.
Le préalable posé par la note — « vérifier si apply-loader est encore nécessaire » —
est instruit : il est utilisé (webpack.config.js,
{ test: /\.pug$/, use: ['apply-loader', 'pug-loader'] }). pug-loader rend
une fonction de template et apply-loader l'applique, ce qui donne la chaîne
que template: require('./x.pug') attend. S'en passer n'est pas un retrait mais un
changement de chaîne de chargement pug, à instruire comme tel. Il est par ailleurs
abandonné de fait — dernière publication le 2017-04-28 (2.0.0, vérifié au registre
le 2026-08-14) — et ne tient sous webpack 5 que parce que la config ne lui passe aucune
option : constat convergent avec l'instruction du même sujet menée côté
tabby-better-vault (rapportée par l'utilisateur le 2026-08-14). Posture commune aux
deux plugins : en surveillance, pas en urgence — toute évolution de la config qui
voudrait lui passer des options rouvre la question du remplacement.
Contrainte à ne pas perdre : pug@^2.0 et les versions liées sont
verrouillés par Angular 15 (piège #4) — un
npm audit fix --force monterait ou rétrograderait des majeures et casserait
le build. Le chantier, quand il s'ouvrira : npm audit fix nu, build de production
revérifié, et l'éventuel passage à pug 3 instruit séparément avec le remplacement du loader.
La note d'origine juge le bump « low-risk » parce que ce sont des devDependencies
isolées du runtime de Tabby. Vrai pour webpack-cli 4→5 (webpack 5 est déjà en
place) ; faux pour TypeScript : Angular 15 — imposé par la peer dependency de
tabby-core — plafonne TS strictement sous 5.0, son compilateur
refusant au-delà (piège #4). typescript@4.9.5
est donc déjà le plafond atteignable ; passer à TS 5.x n'est pas un bump mais une
migration Angular 16+, hors de portée tant que Tabby n'y est pas — même conclusion
atteinte indépendamment côté tabby-better-vault. Le chantier réel :
webpack-cli et les loaders, en période calme, build de production revérifié. À savoir avant de le
juger urgent : l'audit du 2026-08-14 montre que webpack-cli@4 ne tire
aucune vulnérabilité — ce bump est de l'hygiène d'outillage, pas de la
sécurité.
Livré avec la 1.0.3 : CHANGELOG.md à la racine, rétro-rempli de la
0.2.0 à la 1.0.3 depuis les releases GitHub et le journal, et ajouté à files — il
voyage dans le paquet npm. Règle d'entretien qui reste : une entrée par
publication, écrite au moment du bump de version, en anglais comme le README.
Aucun CHANGELOG.md dans le dépôt, alors que les versions s'enchaînent
(1.0.0 → 1.0.1 → 1.0.2). État réel nuancé le 2026-08-14 : les releases GitHub
v0.4.0 → v1.0.2 portent déjà un titre et des notes — le manque est le fichier au dépôt,
seul visible depuis la page npm et le clone, et la couverture des versions antérieures
(0.2.0, 0.3.0). Rétro-remplissage possible depuis les releases et le journal. À faire au plus tard
avec la prochaine publication, pour que la version qui l'introduit le mentionne.
Idée du backlog du 2026-08-08 : redirection X11 transparente, détection des serveurs X locaux,
interrupteur par profil, éventuellement en plugin séparé tabby-better-x11.
Sans objet : Tabby le fait déjà, intégralement. russh expose
requestX11Forwarding() et le flux des canaux x11 entrants ; tabby-ssh
implémente la chaîne complète (src/session/ssh.ts : requête x11-req avec
cookie MIT-MAGIC-COOKIE-1, relais vers la socket X locale par X11Socket,
src/session/x11.ts), et affiche lui-même les liens VcXsrv/Xming quand aucun serveur X
ne répond. L'interrupteur par profil existe déjà (Éditer le profil → onglet
Advanced → « X11 forwarding », options.x11), ainsi qu'un réglage global
« Override X11 display ». Comme notre entrée « Modifier… » ouvre la modale native, c'est atteignable
depuis la sidebar sans une ligne de code. Aucun plugin tabby-*x11* n'existe sur npm —
l'écosystème ne l'a pas fait parce qu'il n'y a rien à faire.
La regex de X11Socket.resolveDisplaySpec() (tabby-ssh/src/session/x11.ts:10)
est /^(.+):(\d+)(?:.(\d+))$/ : le groupe du numéro d'écran n'est pas
optionnel. Vérifié par exécution — localhost:10.0 donne bien
localhost:6010, mais localhost:1, host:10 et
192.168.1.5:0 ne correspondent à rien et retombent en silence sur
localhost:6000. Le champ « Override X11 display » n'obéit donc qu'à la forme longue.
La correction tient en un caractère ((?:\.(\d+))?, point échappé au passage). Si
quelque chose doit sortir de cette idée, c'est cette PR — pas un plugin.
Signalé en amont le 2026-08-08 :
Eugeny/tabby#11576. Bug confirmé présent
dans master amont (14e2d60), aucune issue ouverte ne le couvrait. L'issue
porte le comportement mesuré avant/après sur les deux plates-formes, le diff proposé, et l'effet de
bord à connaître : sur Linux, un localhost:0 explicite passe de la socket Unix au TCP
localhost:6000 — ce que la spec veut dire, mais que -nolisten tcp refuse
sur beaucoup de distributions. La forme nue :0, elle, est inchangée.
Pertinence de fond, pour mémoire : X11 est devenu marginal — serveurs administrés sans bibliothèque X, alternatives web (Cockpit, Proxmox, console du fournisseur), VNC/RDP, X2Go, Wayland qui ne le fait pas fonctionner, et WSLg côté Windows.
Idée du backlog du 2026-08-08 : enregistrer, sauvegarder et rejouer des séquences de frappes,
avec délais et affectation par profil ou workspace. Redondant aux trois quarts :
la fiche livrée s'appelle déjà « Quick Snippets
(Macros de Sidebar) », un snippet est un texte multi-lignes envoyé tel quel dans le pty — donc
déjà une macro multi-commandes —, et l'affectation par profil/dossier avec héritage est plus fine
que ce que l'idée demande. Ce qu'elle ajouterait vraiment est isolé et retenu ailleurs :
étapes, délais et expect.
frontend.input$ capte chaque frappe, y compris à une invite de mot de
passe : les caractères sont absents de la sortie mais intégralement présents dans
l'entrée, dans l'ordre. Ils atterriraient dans config.store.sidebarPlus, en
clair — vérifié sur ce poste, config.yaml ne porte aucune clé
encrypted:, seule la section vault: est chiffrée. Or ce projet a déjà
tranché exactement cet artefact : scripts est purgé d'office à l'export parce qu'« un
couple expect/send est l'endroit où un mot de passe sudo se retrouve en dur », et
Auto-Enter est sorti du périmètre pour le même motif. Un enregistreur
produirait cet artefact automatiquement, sans que l'utilisateur ait choisi de
l'écrire. Ne pas le construire, ici ni ailleurs.
À savoir : tabby-quick-cmds existe et recouvre presque trait pour
trait nos snippets, délais entre commandes compris — c'est le « Macro Manager » moins
l'enregistrement.
Idée du backlog du 2026-08-08 : s'abonner à un workspace par URL ou chemin réseau, rafraîchi
périodiquement, « lecture seule en local, secrets jamais synchronisés ». Le transport est
trivial (aucune CSP dans Tabby, fetch() est le style maison, Node entier dans le
renderer, UNC lisible par fs) — et c'est précisément ce qui rend l'idée
dangereuse : le sujet n'est pas le transport, c'est la réconciliation.
Il faut séparer deux objets que l'idée confond. Un workspace est un overlay
dont le plugin est seul écrivain : verrouillable. Un dossier et ses profils vivent
dans config.store.groups/.profiles, données natives que les
paramètres de Tabby éditent sans passer par le plugin — le verrou n'existerait donc que dans
la sidebar, c'est-à-dire nulle part de fiable : une convention d'affichage vendue comme une
garantie, ce qui est pire que rien. S'y ajoute que le re-parentage change l'id du
dossier (piège #62), donc casse toute clé de rapprochement au premier glisser-déposer, et qu'un
moteur de resynchronisation est fait de la question interdite par le piège #74 (« cet id
existe-t-il encore ? », qui répond non pour des entrées vivantes). L'idée se
dévore elle-même : sa partie sûre (un workspace nu) est inutile — ce ne sont que des
listes d'ids qui ne désignent rien chez le destinataire — et sa partie utile (les profils) n'est
pas sûre.
Sécurité, pour mémoire si le sujet revenait : un abonnement automatique est plus dangereux qu'un collage sur cinq points — l'acte disparaît (personne ne lit la notice), la source devient répétée (bénigne aujourd'hui, hostile dans trois mois), la surface d'attaque devient l'URL (DNS, TLS, dépôt compromis, ACL SMB qui glissent), le rendu est automatique (une icône hostile s'exécute quand l'arbre se peint) et le contexte d'exécution est maximal. C'est en instruisant ce point qu'ont été trouvés les trous de purge du chemin déjà livré — voir Durcir l'import de dossier collé.
Le besoin réel — « mes deux ou trois machines » — est déjà couvert
par le partage de groupe niveau secrets, décrit comme « celle qui se recolle à
l'identique entre ses propres machines ». Ce qui manquerait au plus, sans rien du risque :
importer/exporter depuis un fichier plutôt que le presse-papiers — même
parsePayload(), même modale, même double décompte, seule la source change. L'acte reste
manuel et le contenu reste choisi.
Décision de l'utilisateur à l'usage : « Ouvrir avec » du clic droit fait le travail plus directement, et la table par extension n'apportait rien qui vaille son coût. Le chantier a été construit puis retiré le même jour ; la branche n'a pas été conservée, cette fiche est la trace.
Ce que l'essai a montré, et qui vaut pour quiconque rouvrirait la question : la règle retenue au cadrage — un type inconnu demande plutôt que d'emprunter l'éditeur d'un autre type — est cohérente sur le papier et pénible à l'usage, puisqu'elle pose une question à chaque nouvelle extension rencontrée. L'alternative, un repli silencieux sur l'éditeur par défaut, revient exactement au comportement d'aujourd'hui pour tout ce qui n'a pas été renseigné à la main. Entre les deux, il n'y avait pas de position qui justifie la table.
Aucun reste dans config.yaml — vérifié le 2026-08-03 sur la
configuration de l'utilisateur, une fois son chiffrement retiré : ni clé sftpEditors
orpheline, ni sftpEditorPath vidé, qui porte toujours son éditeur. La migration a bien
tourné en mémoire pendant l'essai, mais rien n'en a survécu sur le disque. Le retrait n'a donc rien
laissé à nettoyer. Ce paragraphe annonçait l'inverse par précaution, avant vérification : c'est la
vérification qui fait foi.
Le design ci-dessous est conservé tel quel, sans être transporté ailleurs.
Demandé par l'utilisateur le 2026-07-30, en constatant à l'usage que l'éditeur
retenu par l'éditeur forcé est global : une seule clé
sidebarPlus.sftpEditorPath, appliquée à tous les fichiers. Un éditeur de texte convient
à un .yml ou à un fichier de conf sans extension, beaucoup moins à un
.png.
sidebarPlus.sftpEditors: { '': 'C:\\...\\Code.exe', '.png': 'C:\\...\\viewer.exe' }.
L'entrée de clé vide sert aussi bien de défaut que d'éditeur des fichiers sans extension.
Extensions normalisées en minuscules à l'écriture comme à la lecture. Nouvelle clé à déclarer
dans les defaults (piège #16) ; migration de l'ancienne sftpEditorPath
vers l'entrée par défaut, sans la perdre.Décision de l'utilisateur, sur deux motifs distincts qu'il vaut mieux ne pas confondre.
L'utilité même n'est pas établie. Le design ci-dessous prévoit d'envoyer Entrée
dès qu'une invite interactive est détectée : valider quand le mot de passe est pré-rempli, passer
l'invite quand il est vide. Or si l'authentification par mot de passe enregistré fonctionne de bout en
bout, le premier cas ne sert à rien — Tabby envoie déjà le mot de passe. Ne resteraient que les
invites qu'il ne traite pas nativement : seconde étape de keyboard-interactive, question
de sudo, facteur supplémentaire. Ce sont précisément les cas où un faux positif enverrait
un Entrée parasite dans un shell de production. Tant qu'aucune invite précise n'est nommée, le
chantier n'a pas de cible, et lui en chercher une par le code serait prendre le risque avant
d'avoir le besoin.
Le rattachement à ce plugin est discuté. L'utilisateur le verrait plutôt du côté
de tabby-better-vault. À noter pour qui reprendra la question : le geste décrit ici est
d'écrire dans un pty distant, pas de lire un secret — ce qui toucherait au coffre-fort est
l'origine du mot de passe, pas l'action. Le rapprochement est donc moins direct qu'il n'en a l'air, et
trancher demande de regarder ce que l'autre plugin fait réellement plutôt que de le supposer :
la décision se prend dans une session consacrée à ce projet-là.
Le design ci-dessous est conservé tel quel et n'a pas été transporté ailleurs — contrairement au Vault Auto-Unlock, dont le périmètre d'accueil était acquis au moment de sa sortie.
Automatisation du login en simulant l'appui sur "Entrée" dès qu'une invite interactive (login/password) est détectée. Si le mot de passe est pré-rempli, cela valide ; s'il est vide, cela passe l'invite.
L'injection de ce toggle près du champ mot de passe implique d'intervenir sur le DOM de la modale
native EditProfileModalComponent. Cette zone étant critique et instable (voir
piège #17 et le
point fragile associé dans AI-CONTEXT.html), le hook
d'injection Angular/DOM devra intégrer un mécanisme de repli (fallback) strict. Si le DOM de Tabby
change lors d'une mise à jour de l'application, le toggle doit se déporter gracieusement dans le
menu contextuel du profil ("Options Sidebar" → "Auto-Enter") ou dans les options globales, sans
provoquer de plantage de l'interface.
tabby-better-vaultLe déverrouillage automatique du coffre-fort Tabby (éviter de ressaisir le mot de passe maître à
chaque démarrage) ne fait pas partie de better-sidebar — ce n'est pas
une fonctionnalité de sidebar, et c'est le point le plus sensible (manipulation d'un secret
utilisateur) envisagé sur cet écosystème de plugins. Devient un plugin indépendant,
tabby-better-vault, publié et versionné séparément — dossier de travail et
gouvernance propres créés le 2026-07-26 :
.AIRules de tabby-better-vault (lien
relatif valable tant que les deux dossiers restent côte à côte dans Développement/).
Design détaillé ci-dessous transporté à l'identique là-bas.
better-sidebar pour consommer des classes de
tabby-core/tabby-settings/tabby-ssh. Pattern non officiel
(effet de bord du chargeur de plugins, pas une API documentée) : le contrat entre les deux
packages serait à notre charge (versioning à gérer). Même discipline de vérification que le reste
du projet : si tabby-better-vault exporte un service, vérifier les DEUX niveaux
(typings ET bundle dist/index.js compilé) avant de s'y fier — voir piège #13
d'AI-CONTEXT.html. Pour le cas concret de l'onglet de réglages, ce pattern générique s'est précisé
en un mécanisme d'élection par token de chaîne (pas de classe TypeScript importée, justement pour
ne pas créer de dépendance npm dure entre les deux plugins) — conception tranchée avec
l'utilisateur, voir ci-dessous.electron.safeStorage (Windows : Gestionnaire d'identifiants/DPAPI ; macOS : Trousseau
d'accès/Keychain avec Touch ID si activé ; Linux : Secret Service API/libsecret via
GNOME Keyring/KWallet). Mécanisme opt-in, injection programmatique du mot de passe déchiffré dans
le VaultService de Tabby avant que la pop-up native de saisie n'apparaisse.= null) dès que l'injection dans le
moteur de Tabby est validée, pour limiter au minimum la fenêtre d'exposition du secret
en mémoire vive.VaultService (ou l'équivalent réel dans le tabby-core installé) expose
bien une méthode d'injection programmatique utilisable depuis un plugin tiers — typings ET bundle
compilé, ne jamais conclure sur un seul des deux (piège #13).better-sidebar et
tabby-better-vault ✅ Livré le 2026-08-07 — validé 25/25 en
conditions réellesSi les deux plugins sont installés ensemble, un seul onglet « Better Tabby »
apparaît dans les réglages Tabby, à la place des deux onglets côte à côte. Vérifié dans le bundle
compilé (node_modules/tabby-settings/dist/index.js) : Tabby ne déduplique
pas deux SettingsTabProvider de même id — un seul plugin à la
fois doit donc déclarer l'onglet unifié, d'où l'élection ci-dessous. Conçu, codé et validé le même
jour (cinq vagues de tests, 25/25, y compris poids inversés) ; la convention vit dans
src/betterPanel.ts et le CLAUDE.md des deux projets, le pendant vault dans
sa branche feat/panneau-unifie.
Détection inter-plugin, par token de chaîne : pas de classe TypeScript importée
d'un plugin par l'autre — ça créerait une dépendance npm dure, alors que les deux plugins doivent
rester installables indépendamment. Chaque plugin « Better * » présent expose, au niveau de son
module racine, un provider Angular sous une clé BetterPanelContribution:<id>
(ex. BetterPanelContribution:sidebar, BetterPanelContribution:vault), de
forme :
interface BetterPanelContribution {
id: string // identifiant stable ('sidebar', 'vault', ...)
title: string // libellé affiché en sous-onglet
hostWeight: number // poids d'élection de l'hôte — SANS RAPPORT avec le `weight` natif
// de SettingsTabProvider (qui ordonne les onglets Tabby de premier
// niveau, déjà utilisé ici avec la valeur 2), à ne pas confondre
componentType: Type<any> // le composant Angular déjà existant de ce plugin, réutilisé tel quel
}
Chaque plugin interroge les tokens des autres membres connus de la famille via
injector.get('BetterPanelContribution:xxx', null) — jamais d'erreur si l'autre n'est
pas installé. La liste des tokens à connaître est codée en dur et documentée dans
le CLAUDE.md de chaque plugin, pas un registre automatique : un futur plugin
« Better X » devra être ajouté à cette liste dans les projets existants pour rejoindre
l'unification.
Élection de l'hôte, poids numérique, le plus léger gagne : chaque plugin déclare
un hostWeight fixe. Au démarrage, un plugin calcule l'ensemble des contributions
présentes (lui-même + celles détectées) et devient hôte seulement s'il porte le
hostWeight le plus faible parmi les présentes (égalité départagée par ordre
alphabétique d'id, cas improbable mais couvert). Valeurs : better-sidebar
= 10, better-vault = 20, espacées de 10 pour insérer un futur membre sans
renuméroter.
Tel que réalisé — deux écarts à la conception d'origine, tous deux tranchés en cours de route :
getComponentType() → null, pas
par le useFactory conditionnel envisagé : le constructeur de
SettingsTabComponent (bundle tabby-settings) filtre lui-même les
providers dont getComponentType() rend une valeur fausse, alors que
SettingsHotkeyProvider parcourt tous les providers sans ce filtre et lit
id/title — une entrée null du multi-provider l'aurait fait
planter. Le provider reste donc toujours enregistré ; conséquence assumée, un hotkey fantôme
« Open settings tab: Better Vault » subsiste côté non-hôte.hostPanel.component — un onglet par plugin, sa propre page comprise, chaque page
montée par NgComponentOutlet avec un injecteur portant le jeton
BetterPanelEmbedded (les pages embarquées lâchent leur content-box).
Les sous-onglets internes d'un plugin restent au niveau du dessous, une page plate s'affiche
telle quelle. Chaque plugin porte son propre panneau hôte : le vault sait héberger aussi, vérifié
en poids inversés. Seul de sa famille, un plugin garde son onglet solo, sans habillage.better-tabby dès que la famille
compte deux membres — tout lien interne passe par l'id élu
(electBetterPanelHost().settingsTabId), et openRequested posé sur sa
propre contribution (objet partagé tel quel via l'injecteur) fait présélectionner son onglet par
l'hôte, quel qu'en soit le porteur — reset-on-read, comme requestedSection.pluginBlacklist sans décharger son module — les services sont
filtrés (enabledServices) mais le token useValue de la contribution
reste visible, donc l'autre plugin se croit toujours non-hôte et plus aucun onglet Better ne
s'affiche. Amélioration possible : faire consulter pluginBlacklist par l'élection.
En attendant, désactiver un plugin de la famille se fait en retirant sa jonction.requestedSection est ignoré si l'onglet paramètres est déjà ouvert
(limite préexistante au chantier) : lu une seule fois au constructeur, jamais sur un composant
déjà construit. Corrigeable par un flux auquel le composant s'abonne, émis après sélection de
l'onglet existant.Le pendant symétrique côté coffre-fort est réalisé dans son propre projet (branche
feat/panneau-unifie, même convention, contrat dupliqué sans import npm) — voir
.AIRules de tabby-better-vault (lien
relatif valable tant que les deux dossiers restent côte à côte dans Développement/).
Explicitement écarté par l'utilisateur : des outils de conversion (.mxtpro/.ini)
existent déjà, non prioritaire. À reconsidérer seulement si ce point de friction remonte
concrètement en usage réel.