# TODO # ObsiViewer Nimbus — Performance Roadmap & Todo List (Angular 20 + Tailwind 3.4) Cette roadmap est une liste d’actions priorisées pour optimiser les performances côté client de l’interface Nimbus, en respectant strictement: - Angular 20 (signals, @for/@if, @defer, standalone, zoneless) - TailwindCSS 3.4 (purge actuelle conservée) - UI/UX Nimbus existante (aucun changement visuel ou de comportement attendu) - Modes Desktop et Mobile Chaque tâche contient: Objectif mesurable, Critères d’acceptation, Emplacements de code, Risques/Dépendances et un Prompt prêt à l’emploi pour exécuter la tâche à 100% (desktop + mobile). --- ## ✅ Phase 0 — Quick Wins (ROI élevé, faible risque) ### 0.1 Retirer l’import global Excalidraw du bundle initial - Objectif: réduire la taille du bundle initial et le TTI en excluant `@excalidraw/excalidraw` du chunk principal. - Critères d’acceptation: - L’import global n’apparaît plus dans `index.tsx`. - Le Drawings Editor reste fonctionnel via import dynamique (`web-components/excalidraw/define`). - Build prod montre une baisse du poids du bundle initial (source-map-explorer). - Emplacements: `index.tsx`, `src/app/features/drawings/drawings-editor.component.ts`, `web-components/excalidraw/define.ts`. - Risques/Dépendances: s’assurer que les usages Excalidraw restent lazy et conditionnels. - Prompt: ``` Tu es un pair-programmer Angular 20. Objectif: retirer tout import global d'Excalidraw du bundle initial sans casser le Drawings Editor. Étapes: 1) Ouvre `index.tsx` et supprime toute ligne `import '@excalidraw/excalidraw'`. 2) Vérifie que `src/app/features/drawings/drawings-editor.component.ts` importe `../../../../web-components/excalidraw/define` via `await import(...)`. 3) Lance un build prod avec stats et analyse les bundles (source-map-explorer) pour confirmer la réduction du main chunk. 4) Teste Desktop et Mobile: ouverture d’un fichier .excalidraw.md dans l’éditeur (réactivité, chargement lazy OK). 5) Ne change pas l’UI/UX. Respecte Angular 20 et Tailwind 3.4. ``` ### ✅ 0.2 Virtualiser la liste centrale Nimbus avec `PaginatedNotesList` - Objectif: maintenir ≥55 FPS et mémoire <200MB avec 5k+ notes. - Critères d’acceptation: - Le centre affiche `PaginatedNotesListComponent` (CDK virtual scroll + pagination). - Scroll fluide Desktop/Mobile, FPS ≥55 sur 5k items. - Émissions/inputs existants (openNote, queryChange, selectedId, quickLink) préservés. - Emplacements: `src/app/layout/app-shell-nimbus/app-shell-nimbus.component.ts` (zones de liste Desktop/Tablet/Mobile). - Risques/Dépendances: compatibilité filtres/search/URL state; conserver événements. - Prompt: ``` Tu es un expert Angular 20/CDK Virtual Scroll. Remplace la liste centrale dans `AppShellNimbus` par `PaginatedNotesListComponent` sans changer l’UX. 1) Identifie les trois zones de rendu de la liste (desktop/tablet/mobile) dans `app-shell-nimbus.component.ts`. 2) Remplace le composant de liste actuel par `` en câblant: - Inputs: folderFilter, tagFilter, quickLinkFilter, query, selectedId - Outputs: openNote, queryChange, clearQuickLinkFilter 3) Vérifie Desktop/Mobile: scroll fluide, sélection, recherche, quick links. 4) Mesure FPS (DevTools) sur 5k notes; objectif ≥55 FPS. 5) Pas de modification visuelle. Respecte Angular 20/Tailwind 3.4. ``` ### 0.3 Déférer les viewers lourds (PDF/Video/Excalidraw/Code) via `@defer` - Objectif: réduire LCP et TTI en chargeant à la demande les viewers lourds. - Critères d’acceptation: - `@defer` entourant le SmartFileViewer (non-markdown) avec placeholder. - Le chunk viewer se charge uniquement quand visible (on viewport/interactions). - Pas de régression Desktop/Mobile. - Emplacements: `src/components/tags-view/note-viewer/note-viewer.component.ts` (ou template), `src/components/smart-file-viewer/`. - Risques/Dépendances: placeholders légers; événements image/video conservés. - Prompt: ``` Tu es un spécialiste Angular 20. Déferre l’affichage des viewers lourds. 1) Dans le template NoteViewer (ou SmartFileViewer), entoure le rendu non-markdown avec `@defer (on viewport)` et un placeholder léger. 2) Garde le rendu markdown immédiat. 3) Vérifie Desktop/Mobile: ouverture d’un PDF/Excalidraw/Video/Code charge un chunk async seulement à l’affichage. 4) Mesure LCP/TTI avant/après (Lighthouse Mobile). Objectif: LCP ≤ 2.5s, TTI ≤ 3.0s. 5) Aucun changement d’UI. Respecte Angular 20/Tailwind 3.4. ``` ### 0.4 Adopter `NgOptimizedImage` pour les images - Objectif: améliorer LCP et éliminer CLS en fournissant dimensions/sizes. - Critères d’acceptation: - `NgOptimizedImage` importé et `ngSrc` utilisé dans `image-viewer`. - Dimensions (width/height) ou stratégie calculée, `sizes` renseigné. - LCP image ↓ et CLS ≈ 0. - Emplacements: `src/app/features/note-view/components/image-viewer/image-viewer.component.ts` (+ contenu markdown rendu si applicable). - Risques/Dépendances: fournir dimensions raisonnables; ne pas dégrader mise en page. - Prompt: ``` Tu es un dev Angular 20. Optimise les images avec NgOptimizedImage. 1) Dans `image-viewer.component.ts`, ajoute `NgOptimizedImage` aux imports standalone. 2) Remplace `src` par `ngSrc` et fournis `width`, `height`, `sizes`. 3) Vérifie Desktop/Mobile: absence de sauts de layout (CLS), affichage net, LCP image améliorée. 4) Garde les événements/émissions inchangés. Pas de changement visuel. ``` ### 0.5 Convertir les `*ngFor` restants en `@for` avec `track` - Objectif: réduire le coût de diffing et stabiliser l’identité des items. - Critères d’acceptation: - Listes dynamiques dans Nimbus utilisent `@for (...; track ...)`. - Aucune régression d’interaction (sélection tags/dossiers/etc.). - Emplacements: `app-shell-nimbus.component.ts` (ex. flyout tags/dossiers) et autres listes restantes. - Risques/Dépendances: choisir une clé stable (`id`, `name`). - Prompt: ``` Tu es un expert Angular 20. Modernise les boucles. 1) Recherc he `*ngFor` dans les templates Nimbus et remplace-les par `@for`. 2) Ajoute un `track` stable (id, name) pour chaque liste. 3) Teste Desktop/Mobile: navigation, clics tags/dossiers, pas de re-rendu excessif. ``` --- ## Phase 1 — Correctifs cœur ### 1.1 Throttling/Debouncing des entrées + écouteurs passifs - Objectif: TBT ≤ 150ms, INP ≤ 200ms durant saisie/recherche/scroll. - Critères d’acceptation: - Flux de recherche: debounce/throttle ~16–100ms. - Écouteurs scroll/wheel/resize en `{ passive: true }` si custom. - Profiler: long tasks ↓ et input latency ↓ sur Desktop/Mobile. - Emplacements: composants de recherche/liste; directives éventuelles d’événements. - Prompt: ``` Tu es un dev perf Angular. Lisse les entrées. 1) Ajoute debounce/throttle (RxJS) aux flux de recherche et scroll. 2) Convertis tout `addEventListener` custom en `{ passive: true }` si pertinent. 3) Mesure INP/TBT (Lighthouse Mobile). Objectifs: INP ≤ 200ms, TBT ≤ 150ms. ``` ### 1.2 Déférer panneaux lourds (paramètres/tests/about) - Objectif: réduire bundle initial et coût de rendu. - Critères d’acceptation: - Panneaux secondaires rendus via `@defer (on interaction)` + placeholder. - Aucun changement d’UX; charge uniquement à l’ouverture. - Emplacements: `app-shell-nimbus.component.ts` (panneaux/overlays non essentiels). - Prompt: ``` Tu es un expert Angular 20. Déferre les panneaux non essentiels. 1) Ajoute `@defer (on interaction)` autour des panneaux paramètres/tests/about. 2) Placeholder léger et accessibilité préservée. 3) Vérifie Desktop/Mobile: ouverture fluide, chunks chargés à la demande. ``` ### 1.3 Vérifier import conditionnel des libs lourdes - Objectif: `@excalidraw/excalidraw`, PDF et autres libs uniquement chargés si viewer requis. - Critères d’acceptation: aucun import global résiduel; lazy import confirmé (network waterfall). - Emplacements: `smart-file-viewer`, `drawings-editor`, pdf viewer. - Prompt: ``` Tu es un dev Angular. Vérifie et force les imports conditionnels. 1) Inspecte smart-file-viewer/drawings-editor/pdf-viewer: pas d’import global. 2) Les imports doivent être dynamiques/conditionnels à l’usage. 3) Confirme au profiler: chunks chargés seulement si nécessaires. ``` --- ## Phase 2 — Refactors profonds ### 2.1 Slices de store par signals (Sélection/Filtre/Recherche) - Objectif: diminuer les re-rendus et effets transverses. - Critères d’acceptation: - Séparation claire des signals; reactivité localisée; pas de boucle. - UX identique; URL state toujours synchronisé. - Emplacements: `AppComponent`, `AppShellNimbusLayoutComponent`, services d’état/URL. - Prompt: ``` Tu es un architecte Angular 20. Isoles les états en slices par signals. 1) Identifie les states très utilisés (sélection, filtres, recherche). 2) Crée des slices/cohorts de signals avec computed/effects. 3) Assure la synchro URL <-> UI intacte. Tests Desktop/Mobile. ``` ### 2.2 content-visibility/contain pour grands conteneurs - Objectif: réduire le coût de layout/paint hors écran. - Critères d’acceptation: scrolling et navigation plus fluides; aucune régression visuelle. - Emplacements: grands conteneurs de liste/notes (CSS global ou scss locaux). - Prompt: ``` Tu es un expert CSS perf. Ajoute `content-visibility: auto` et `contain-intrinsic-size` sur les grands conteneurs offscreen. 1) Cible les conteneurs principaux liste/viewer. 2) Vérifie Desktop/Mobile: aucun artefact; paint/layout réduits (Performance panel). ``` ### 2.3 Pipeline images responsive (thumb→medium→full) - Objectif: améliorer le temps de rendu perçu des assets visuels. - Critères d’acceptation: affichage progressif sans saut, qualité finale correcte. - Emplacements: chargement d’images (viewer et markdown rendu). - Prompt: ``` Tu es un dev front. Mets en place un pipeline responsive images. 1) Servez d’abord une miniature, puis une image medium, puis la full. 2) Adapte `sizes/srcset` (NgOptimizedImage) pour Desktop/Mobile. 3) Mesure LCP perçu et absence de CLS. ``` --- ## Phase 3 — Plateforme / Build ### 3.1 Budgets Angular (initial, anyComponentStyle, css) - Objectif: prévenir les régressions de poids. - Critères d’acceptation: budgets ajoutés à `angular.json`; build échoue si dépassement. - Emplacements: `angular.json`. - Prompt: ``` Tu es un mainteneur Angular. Ajoute des budgets stricts. 1) Ajoute budgets pour initial (warn 1400kb / error 1800kb), anyComponentStyle, css. 2) Lance un build prod pour valider. 3) Documente dans README/CI les seuils. ``` ### 3.2 Scripts d’analyse + (optionnel) Lighthouse CI gate - [] done - Objectif: visibilité continue et garde-fous automatiques. - Critères d’acceptation: scripts npm `build:stats` et `analyze:bundle`; pipeline peut exécuter Lighthouse avec seuils mobiles. - Emplacements: `package.json`, CI. - Prompt: ``` Tu es un dev outillage. Ajoute scripts d’analyse et un job Lighthouse CI (optionnel). 1) `build:stats` (ng build --stats-json) et `analyze:bundle` (source-map-explorer) dans package.json. 2) Pipeline: exécute Lighthouse Mobile/Desktop et échoue si LCP/TTI/CLS/TBT hors seuils. 3) Ne modifie pas le code applicatif. ``` --- ## Mesure & Validation - Baseline puis post-implémentation: Lighthouse Mobile (4x CPU, 1.5 Mbps) et Desktop. - Objectifs cibles: - LCP ≤ 2.5s (Mobile), TTI ≤ 3.0s, CLS ≤ 0.02, TBT ≤ 150ms - Liste 5k items: ≥55 FPS, mémoire <200MB - Bundle initial: -40% vs baseline si possible - Scripts utiles: - `npm run build:stats` - `npx source-map-explorer "dist/**/*.js"` --- ## Checklist globale (progression) - [x] 0.1 Retirer import global Excalidraw - [x] 0.2 Virtualiser liste centrale Nimbus (PaginatedNotesList) - [ ] 0.3 Déférer viewers lourds (@defer) - [ ] 0.4 NgOptimizedImage pour images - [ ] 0.5 Convertir *ngFor → @for avec track - [ ] 1.1 Throttle/Debounce + passive listeners - [ ] 1.2 Déférer panneaux non essentiels - [ ] 1.3 Imports conditionnels libs lourdes - [ ] 2.1 Slices signals (Sélection/Filtre/Recherche) - [ ] 2.2 content-visibility/contain pour conteneurs - [ ] 2.3 Pipeline images responsive - [ ] 3.1 Budgets Angular - [ ] 3.2 Scripts analyse + Lighthouse CI (opt.)