# #75 — Éditeur multi-panneaux (Split View) > **Statut :** ✅ Terminé — 100% implémenté + 37 tests E2E (16 split-view.spec.js + 21 split-view-matrix.spec.js, 2026-09-08) > **Effort :** 5-7 jours (réalisé) | **Impact :** 🟡 > **Références :** [Roadmap](../ROADMAP.md) · [Changelog — 2.1.0](../../CHANGELOG.md#210--2026-09-08) - **Fichiers clés :** `frontend/js/pane-manager.js` (1082 loc), `frontend/js/viewer.js` (modifié), `frontend/js/ui.js` (modifié), `frontend/js/dashboard.js` (modifié), `frontend/js/palette.js` (modifié), `frontend/style.css` (modifié), `tests/test_pane_manager.py` (22 tests) - **Description :** Système de panneaux divisés permettant d'afficher plusieurs documents côte à côte ou superposés, à la manière d'un éditeur de code (VS Code). Drag & drop des onglets entre les panneaux, drag-to-split, menu contextuel, raccourcis clavier, palette de commandes. - **Architecture :** - **PaneManager** — orchestre la grille de panneaux (1-4), crée/détruit les instances PaneTabManager - **PaneTabManager (factory `createPaneTabManager(paneId)`)** — chaque panneau a son instance indépendante avec ses propres onglets, cache, état - **Singleton TabManager** — délègue automatiquement vers le PaneTabManager actif quand en mode split ; gère les onglets en single-pane - **Migration automatique** — les onglets migrent singleton→PaneTabManager au premier split, et inversement au collapse - **getContentArea()** — helper global dans viewer.js, priorise `window._activePaneContentArea` (set par PaneTabManager) - **Implémenté :** ## A. PaneManager — Gestionnaire de panneaux - [x] **A1.** Module `PaneManager` + factory `createPaneTabManager` — état indépendant par panneau - [x] **A2.** Création dynamique du DOM — `.pane-grid` avec `.pane-container`, IDs suffixés `-N` - [x] **A3.** Redimensionnement — drag handles, double-clic reset 50/50, contrainte 20-80%, localStorage - [x] **A4.** Indépendance des panneaux — chaque panneau a ses propres onglets, scroll, cache, source view ## B. Refactoring du TabManager - [x] **B1.** Découplage du DOM — `init(tabBar, tabList, contentArea)` par paramètre - [x] **B2.** Factory `createPaneTabManager(paneId)` — instances indépendantes - [x] **B3.** Compatibilité ascendante — pane 0 réutilise les IDs existants, panes 1+ suffixés `-N` - [x] **B4.** Synchronisation state global — `setActivePane()` met à jour `currentVault`/`currentPath` ## C. Actions de division - [x] **C1.** Menu contextuel — « Diviser à droite », « Diviser en bas », « Fermer le panneau » - [x] **C1b.** Sous-menu « Déplacer vers... » — flyout hover listant les autres panneaux - [x] **C2.** Raccourcis clavier — `Ctrl+Alt+\` (split R), `Ctrl+Alt+Shift+\` (split D), `Ctrl+Alt+W` (close pane), `Ctrl+Shift+Alt+W` (close others), `Ctrl+Alt+←/→/↑/↓` (navigate) - [x] **C3.** Boutons dans la barre d'onglets — ⊞→ et ⊞↓ visibles au survol - [x] **C4.** Fermeture panneau — retour single-pane si dernier, focus voisin, onglets perdus (pas déplacés) ## D. Drag & Drop - [x] **D1.** Drag cross-pane — `text/plain` JSON `{paneId, tabId, index}`, indicateurs visuels - [x] **D2.** Drag depuis l'arborescence — format `tree:{vault,path}`, drop sur panneau spécifique - [x] **D3.** Drag-to-split — zone droite 28% → split right, zone basse 28% → split down, overlay bleu ## E. Rendu et performance - [x] **E1.** Rendu indépendant — `window._activePaneContentArea` override pour `renderFile()` - [x] **E2.** Cache de contenu — chaque PaneTabManager a son propre `_tabCache`, pas de refetch - [x] **E3.** Rendu paresseux — contenu masqué via `display:none` sur panneaux inactifs - [x] **E4.** Verrouillage éditeur — même fichier déjà ouvert ailleurs → focus le panneau existant ## F. CSS & Design - [x] **F1-F5.** Grid layout, resize handles avec hover accent, barre onglets par panneau, variables CSS (dark/light), responsive <768px ## G. Persistance et restauration - [x] **G1.** localStorage `obsigate-panes` — layout, panes[{id, width, activeTab, tabs[]}] - [x] **G2.** Restauration au chargement — reconstruction grille + onglets + activation - [x] **G3.** Reset dans la palette de commandes — « 🔄 Réinitialiser les panneaux » ## H. Compatibilité - [x] **H1.** Palette de commandes — « Diviser à droite », « Diviser en bas », « Fermer le panneau », « Panneau suivant/précédent » (catégorie Panneaux) - [x] **H2.** Pop-out inchangé - [x] **H3.** Mermaid scoped au content-area - [x] **H4.** AI Editor en modale (inchangé) - [x] **H5.** Ctrl+S par panneau ## I. Tests - [x] **I1.** 22 tests statiques (structure, CSS, brace balance, régression backend) - [x] **I4.** 307 tests de régression passent - [x] **I2.** Tests d'intégration frontend (JSDOM) — `tests/frontend/pane-manager.test.mjs` (9 tests) - [x] **I3.** Tests E2E Playwright (#58) — 2026-09 : `split-view.spec.js` (16) + `split-view-matrix.spec.js` (21) = 37 tests verts en local + CI ## Reste à faire - [x] **C1b.** Sous-menu « Déplacer vers... » dans le menu contextuel des onglets - [x] **D2.** Drag & drop depuis l'arborescence vers un panneau spécifique - [x] **E3.** Rendu paresseux (mémoire) — contenu masqué via `display:none` sur panneaux inactifs - [x] **E4.** Verrouillage éditeur multi-panneau — même fichier ouvert dans 2 panneaux → focus le panneau existant - [x] **G3.** Bouton reset dans la palette de commandes (🔄 Réinitialiser les panneaux) - [x] **I2.** Tests d'intégration frontend (JSDOM) — `tests/frontend/pane-manager.test.mjs` (9 tests, 100% verts) - [x] **I3.** Tests E2E Playwright — matrice complète ouverture/fermeture couverte (voir split-view-matrix.spec.js) ## Matrice de couverture ouverture/fermeture (validée 2026-09-08, 37/37 verts) | Cas | Surface testée | Spec | |---|---|---| | Ouverture fichier → onglet | clic arbre, double-clic preview vs persistant | split-view | | Split droit | raccourci Ctrl+Alt+\ · bouton ⊞→ · menu contextuel · palette · drag-to-split bord droit | les 5 | | Split bas | raccourci Ctrl+Alt+Shift+\ · bouton ⊞↓ · menu contextuel · drag-to-split bord bas | les 4 | | Déplacer onglet | drag cross-pane · flyout « Déplacer vers... » · drag depuis arbre vers panneau | les 3 | | Navigation | Ctrl+Alt+←/→ (horizontal) · Ctrl+Alt+↑/↓ (vertical) · palette suivant/précédent | les 3 | | Fermeture onglet | bouton X · double-clic · clic molette · Ctrl+W (single) | les 4 | | Fermeture menu ctx | Fermer · Fermer les autres · Fermer à droite · Fermer tout → dashboard | les 4 | | Fermeture panneau | Ctrl+Alt+W (clavier) · menu « Fermer le panneau » · Ctrl+Shift+Alt+W (autres) · auto-collapse panneau vidé (C4) · fermeture pane 0 → voisin survit · onglets du panneau fermé perdus (documenté) | les 5 | | Persistance | layout restauré au reload · reset palette vide localStorage | les 2 | | Edge | plafond 4 panneaux (PANE_MAX) · pas de doublon à la réouverture (E4) | les 2 | Non couvert par E2E (hors périmètre ouverture/fermeture) : poignée de redimensionnement drag/dblclick-reset (A3) — logique vérifiée par les 22 tests statiques + JSDOM.