ObsiViewer/docs/PROMPTS/Nimbus_Interface/ObsiViewer_NewInterface_Nimbus_V2_grok.md

37 KiB
Raw Blame History

🎯 Prompt Windsurf — ObsiViewer Nimbus UI (Desktop + Mobile)

ObsiViewer → UI/UX "Nimbus-like" (simple, dense, rapide)

Rôle & mode : Agis comme Staff Frontend Engineer Angular 20 + UX designer. Raisonnement détaillé autorisé. Tu as les pleins pouvoirs de refactor UI, d'ajout de composants, et de migration CSS vers Tailwind. Conserve la compatibilité de toutes features existantes.

Contrainte majeure : L'interface doit être 100% responsive et compatible à 100% avec les modes Desktop et Mobile (téléphone et tablette). Un bouton toggle dans la navbar permet de basculer entre l'ancienne interface et la nouvelle sans perte d'état. Assure une expérience fluide sur tous les appareils, avec des adaptations spécifiques pour les écrans tactiles (touch targets ≥44x44px), gestures (swipe, pull-to-refresh), et breakpoints optimisés pour téléphones (xs/sm), tablettes (md), et desktops (lg/xl/2xl).


Contexte rapide

  • Projet : ObsiViewer (Angular 20 + Tailwind, Node/Express backend).
  • Objectif : Refondre l'interface selon un design Nimbus Notes / FuseBase Note-like, en intégrant fidèlement la lecture anatomique et UX décrite ci-dessous.
  • Cœurs d'usage : navigation par dossiers, tags, recherche, lecture markdown plein écran, ToC à droite, tri et filtres rapides.
  • Nouveauté : Design adaptatif complet (Desktop/Mobile/Tablet) avec UI toggle persisté.

Description détaillée de l'interface Nimbus (à reproduire fidèlement)

Voici une lecture “anatomique” et UX de linterface Web de Nimbus Notes / FuseBase Note, rédigée comme le ferait un lead designer qui doit la reproduire fidèlement. Cette description guide toute la refonte et doit être intégrée à 100% dans l'implémentation, en veillant à la compatibilité Desktop/Mobile/Tablet (par exemple, colonnes empilées ou onglets sur mobile, drawers tactiles sur tablette).


1) Philosophie produit (ce qui guide toute lUI)

  • Workspace-first : tout vit dans un espace de travail isolé (pages, dossiers, tags, membres). On change despace, on change dunivers de contenu ce nest pas un simple filtre global. (FuseBase)
  • Organisation bi-axiale : dossiers (hiérarchie) + tags (multi-catégories). Les dossiers structurent, les tags donnent le contexte transversal et la recherche rapide. (FuseBase)
  • Recherche dabord, navigation ensuite : la top bar met la recherche au centre, avec des chips de filtres persistants (“All folders / All tags / All pages”) qui rendent létat des filtres visible, éditable et réinitialisable sans quitter le flux.
  • Progressive disclosure : menus condensés, panneaux contextuels (picker dossiers/tags, panneau doutline à droite) qui napparaissent que “on demand” pour ne jamais surcharger lécran.
  • Un contenu, plusieurs façades : la page est éditée dans lapp interne, mais peut être partagée ou publiée dans un Portal externe brandé. Les commandes “Add to portal / Share” sont toujours visibles dans léditeur. (FuseBase)

2) Layout global (3 panneaux + overlays)

A. Barre supérieure (globale)

  • Champ de recherche large, centré, avec icône filtre (entonnoir) qui ouvre les options avancées.
  • Bouton “+ Page” (CTA positif turquoise) : création immédiate dans le contexte courant (workspace + dossier sélectionné).
  • À droite, dans la content toolbar du document : Ask AI, Add to portal, Share, loupe locale, pièces jointes/annotations, toggle plein écran, menu kebab. (Les intitulés et la présence “Ask AI / Portals” sont aussi documentés côté guides.) (FuseBase)

B. Colonne de gauche (navigation primaire)

  • Quick Links puis Folders / Tags / Trash — arborescence compacte avec triangles de disclosure.
  • Création de sous-dossier au survol via un + qui apparaît à droite du dossier (pattern hover-reveal). (FuseBase)
  • Sélecteur dorganisation/workspace et switch de thème dans le footer du rail (menu avatar : “Light theme”, Settings, etc.). Le changement despace est aussi accessible depuis la colonne et le menu avatar. (FuseBase)
  • État : le dossier actif est surligné, le compteur de pages peut apparaître à côté des libellés (donnant un feedback rapide sur la densité).

Traits de design

  • Icônes de dossiers colorées + emojis autorisés dans les noms (lisibilité + repères mnémotechniques).
  • Densité compacte mais respirante (gouttière + padding régulier), contrastes élevés en dark mode.

C. Colonne centrale (index / résultats)

  • En-tête dindex : chips All folders ▾, All tags ▾, All pages ▾. Chacune ouvre un picker flottant (overlay) :

    • Picker dossiers : recherche interne, sélection multi, sous-dossiers avec chevron/, actions Clear / Done, badges +1, +15 reflétant le nombre de sélections.
    • Picker tags : liste alphabétique, checkmarks pour les tags actifs, Clear / Done en bas.
  • En dessous, liste des pages : tuiles en une ligne avec titre, date, snippet (extrait), petites icônes (ex. carte bookmark pour les liens sauvegardés). Le hover change légèrement le fond ; lélément sélectionné est surligné (bleu).

  • Message “Found pages: N” + lien Clear lorsque des filtres sont actifs (feed-back lisible de létat de recherche).

Subtilités dusage

  • Les chips de filtre restent visibles pendant la navigation (ancrage cognitif).
  • Chaque picker possède sa barre de recherche locale, ce qui réduit la charge mentale sur de gros jeux de tags/dossiers.
  • Les chips affichent des badges de comptage (+1, +2) et un caret pour signifier quil sagit dun menu stateful, pas dun simple label.

D. Panneau de droite (lecteur/éditeur)

  • Titre de page très lisible, méta-ligne en dessous (ex. Add tags), mini-icônes de statut.

  • Contenu riche : titres, listes, inline code, code fences, callouts, objets embarqués, cartes “Bookmark” (prévisualisation dURL avec titre/description/vignette). (FuseBase)

  • Toolbar du document (droite) :

    • Ask AI (génération, résumé, etc.) ; Add to portal (publier la page dans une arborescence publique/privée) ; Share (lien partagé, droits). (FuseBase)
    • Loupe/“find in page”, attachements, rappel/notification si activés, plein écran avec raccourci (tooltip).
  • Panneau latéral contextuel (outline) : petit bouton hamburger rond “dans la page” (à mi-hauteur, côté droit) qui déplie un rail doutline listant les headings H1H6 ; clic = scroll-to. (Très utile sur documents longs, progressive disclosure).

Édition & Portals (workflow)

  • Lédition se fait directement côté web, et lon peut pousser vers un Portal sans passer par un autre back-office : “Add to portal” + drag & drop vers la structure du Portal (depuis le rail). (FuseBase)

E. Overlays & états transitoires

  • Pickers (Folders/Tags) : overlays centrés au-dessus de la colonne des résultats, avec Clear/Done persistants en pied.
  • Dark / Light : toggles dans le menu avatar ; la typographie/balances de gris sont recalibrées (mode light très clair, mode dark très contrasté) pour garder le même information scent.
  • Notifications/Tooltips : discrets, ancrés aux contrôles (ex. plein écran).

3) Micro-interactions & patterns récurrents

  • Hover-reveal : le “+” pour créer un sous-dossier napparaît quau survol → garde larborescence propre. (FuseBase)
  • Pills / Chips avec caret + badge = stateful filter (on comprend dun coup dœil ce qui est filtré).
  • Sélection persistante dans la liste : lélément actif garde un fond et une bordure turquoise ; on ne “perd” pas la page lue.
  • Clear partout : dans les pickers et au niveau global la stratégie de reset rapide est omniprésente (réduction du coût dexploration).
  • Hiérarchie visuelle stricte : H1H3 très différenciés, marges généreuses, line-height confortable (lecture longue).

4) Contenus & types spéciaux

  • Bookmarks : carte enrichie (titre + description + vignette) → meilleure scanability des liens sauvegardés que du simple texte. (FuseBase)
  • Tables/Databases : vues avec tri, filtres, commentaires cellule, panneau de calcul (présents dans lécosystème guides). (FuseBase)
  • Web Clipper : amène des pages propres (sans pubs), cohérent avec la carte Bookmark ; logique de capture → enrichissement → partage. (FuseBase)

5) Accessibilité & lisibilité

  • Contrastes sûrs (notamment en dark), tailles de cible suffisantes sur la top bar et les chips.
  • Iconographie descriptive (dossier, tag, favoris, web card…) + emojis pour ancrage visuel rapide.
  • Réduction du cognitive load : les options rarement utilisées sont “cachées” (kebab, hover-reveal), les actions courantes sont toujours visibles (Search, +Page, filtres).

6) Ce que lUX “raconte” (le mode demploi implicite)

  1. Trouver : commencez par Search et affinez avec All folders / All tags.
  2. Parcourir : si vous êtes “dans” un dossier, lindex au centre sert de tableau de bord (dates + snippets).
  3. Éditer / Enrichir : dans le panneau de droite, vous rédigez, taggez, joignez, bookmarquez.
  4. Diffuser : Share pour du partage direct, Add to portal pour publier dans un espace externe gouverné (droits, branding). (FuseBase)

7) Détails visibles dans vos captures (à reproduire à lidentique)

  • Top bar : champ de recherche + icône filtre + + Page (CTA turquoise).
  • Trio de chips : “All folders ▾” (multi-sélection hiérarchique), “All tags ▾” (liste à coches), “All pages ▾”.
  • Liste : ligne compacte, hover, selected state bleu, date en gris, snippet tronqué, petite icône (ex. “shopping cart” pour bookmark e-commerce).
  • Panneau doutline (badge hamburger circulaire flottant à droite du contenu) → rail doutline repliable.
  • Menu avatar (footer gauche) : Light theme, Settings, Upgrade, logout, etc.
  • Dark/Light : la skin claire conserve larchitecture et les espacements, en ne changeant que la palette et les ombres.

TL;DR — Nimbus en 8 principes à retenir (pour inspirer un redesign)

  1. Trois panneaux stables (nav / index / contenu) + overlays transitoires.
  2. Filtres persistants sous forme de chips toujours visibles.
  3. Pickers riches (recherche locale, checkmarks, Clear/Done).
  4. Outline contextuel à droite, déclenché dans la page.
  5. Action de création unique, toujours au même endroit (+ Page).
  6. Workspaces strictement séparés ; Portals pour lexterne. (FuseBase)
  7. Bi-axe dossiers/tags (structure + sens transversal). (FuseBase)
  8. Share/Add to portal omniprésents : écrire → publier est un continuum. (FuseBase)

Si tu veux, je peux te livrer juste après un spec UI prêt à coder (grid, tailles, tokens couleur, comportements exacts des pickers) ou un prompt Windsurf pour cloner ces patterns dans ObsiViewer.


🎯 Objectif final (résumé)

Desktop (≥1024px)

Refondre l'interface ObsiViewer en 3 colonnes respectant fidèlement la description Nimbus :

  1. Sidebar gauche (Quick Links, Dossiers arborescents, Tags) — Redimensionnable.
  2. Colonne centrale Liste des pages (recherche, filtres dossiers/tags, tris, résultats virtualisés).
  3. Vue de page à droite (lecture markdown, barre d'actions, panneau sommaire/ToC docké à l'extrême droite).

Le tout compact, performant, thème clair/sombre, navigation au clavier, états persistés localement.

Mobile/Tablet (<1024px)

Une navigation par onglets/drawer intelligente, adaptée à la philosophie Nimbus (progressive disclosure, chips persistants, overlays tactiles) :

  • Tab 1 : Sidebar (dossiers, tags, recherche) — Panneau full-width ou drawer collapsible.
  • Tab 2 : Liste (résultats de recherche) — Full-width scrollable.
  • Tab 3 : Page (markdown) — Full-width avec ToC inline collapsible ou drawer.

Gestures : Swipe horizontal pour navigation onglets, pull-to-refresh, tap = open item. Assure que les chips de filtres et pickers sont optimisés pour touch (tailles augmentées, overlays pleine largeur sur mobile).


📋 Architecture Feature Flag & Toggle

1) Toggle UI dans la NavBar

Ajouter un bouton toggle dans src/app/layout/app-navbar/app-navbar.component.ts :

<!-- app-navbar.component.html (snippet) -->
<div class="flex items-center gap-2">
  <!-- Autres boutons -->
  <button 
    (click)="toggleUIMode()"
    [attr.aria-label]="'Toggle ' + (isNimbusMode$ | async ? 'legacy' : 'nimbus') + ' UI'"
    class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-800">
    <span *ngIf="(isNimbusMode$ | async)">✨ Nimbus</span>
    <span *ngIf="!(isNimbusMode$ | async)">🔧 Legacy</span>
  </button>
</div>

2) Service de gestion du mode UI

Créer src/app/shared/services/ui-mode.service.ts :

import { Injectable, signal } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class UiModeService {
  // Signal pour réactivité fine-grained
  isNimbusMode = signal<boolean>(this.loadUIMode());

  constructor() {}

  toggleUIMode() {
    const newMode = !this.isNimbusMode();
    this.isNimbusMode.set(newMode);
    localStorage.setItem('obsiviewer-ui-mode', newMode ? 'nimbus' : 'legacy');
  }

  private loadUIMode(): boolean {
    if (typeof localStorage === 'undefined') return false;
    const saved = localStorage.getItem('obsiviewer-ui-mode');
    return saved ? saved === 'nimbus' : true; // Nimbus par défaut
  }
}

3) Layout wrapper avec feature flag

Créer src/app/layout/app-shell-adaptive/app-shell-adaptive.component.ts :

import { Component, inject } from '@angular/core';
import { UiModeService } from '@app/shared/services/ui-mode.service';
import { AppShellNimbusLayoutComponent } from '../app-shell-nimbus/app-shell-nimbus.component';
import { AppShellLegacyLayoutComponent } from '../app-shell-legacy/app-shell-legacy.component';

@Component({
  selector: 'app-shell-adaptive',
  template: `
    @if (uiMode.isNimbusMode()) {
      <app-shell-nimbus-layout></app-shell-nimbus-layout>
    } @else {
      <app-shell-legacy-layout></app-shell-legacy-layout>
    }
  `,
  standalone: true,
  imports: [AppShellNimbusLayoutComponent, AppShellLegacyLayoutComponent],
})
export class AppShellAdaptiveComponent {
  uiMode = inject(UiModeService);
}

🎨 Responsive Design Strategy

Breakpoints Tailwind (standard)

// tailwind.config.js
module.exports = {
  theme: {
    screens: {
      'xs': '320px',   // iPhone SE
      'sm': '640px',   // Petites tablettes
      'md': '768px',   // iPad, tablettes
      'lg': '1024px',  // Desktop compact
      'xl': '1280px',  // Desktop standard
      '2xl': '1536px', // Larges écrans
    },
  },
};

Mobile First Approach

Développer pour mobile d'abord, puis enrichir pour desktop. Assure que tous les éléments Nimbus (chips, pickers, outline) sont tactiles et responsifs : par exemple, chips scrollables horizontalement sur mobile, pickers en modales pleine largeur sur téléphone/tablette.


📱 Layouts Responsifs

Desktop Layout (≥1024px)

┌─────────────────────────────────────────────────────────┐
│ NAVBAR (Dark, fixed, h-14)                              │
├────────────────┬──────────────────┬──────────────────────┤
│                │                  │                      │
│  SIDEBAR       │   RESULT LIST    │   NOTE VIEW + TOC    │
│  (240-440px)   │   (virtualized)  │   (Resizable)        │
│  Resizable     │                  │                      │
│                │                  │                      │
│  - Quick       │  - Search bar    │  - Markdown          │
│    Links       │  - Filters       │  - ToC drawer        │
│  - Folders     │  - Items (80px)  │  - Actions bar       │
│  - Tags        │  - Pagination    │                      │
│                │                  │                      │
└────────────────┴──────────────────┴──────────────────────┘

Tablet Layout (768px ≤ width < 1024px)

┌──────────────────────────────────────┐
│ NAVBAR + Toggle (fixed, h-14)        │
├──────────────────────────────────────┤
│  TAB NAVIGATION (fixed, bottom)      │
│  [Sidebar] [List] [Page] [ToC]       │
├──────────────────────────────────────┤
│                                      │
│  ACTIVE TAB CONTENT (scrollable)     │
│  - Drawer si besoin                  │
│  - Full-width panels                 │
│                                      │
└──────────────────────────────────────┘

Mobile Layout (<768px)

┌──────────────────────────────────┐
│ NAVBAR (compact, h-12)           │
│ [Menu] [Search] [Toggle]         │
├──────────────────────────────────┤
│                                  │
│  TAB/DRAWER NAVIGATION           │
│  [≡] [🔍] [📄] [📋]             │
│                                  │
│  CONTENT AREA (Full-width)       │
│  - Drawer sidebar (80vw left)    │
│  - Swipeable list (list tab)     │
│  - Markdown full-screen (page)   │
│  - Inline ToC (toggle button)    │
│                                  │
├──────────────────────────────────┤
│ Bottom Navigation (sticky)       │
│ Tab buttons (4 icônes)           │
└──────────────────────────────────┘

🎬 Composants Nimbus Responsifs

Desktop/Mobile Variants

Chaque composant doit avoir des variants responsifs intégrant les patterns Nimbus (hover-reveal sur desktop, tap-reveal sur mobile ; chips avec badges tactiles) :

app-left-sidebar/
├── app-left-sidebar.component.ts          # Logique partagée
├── app-left-sidebar.desktop.component.ts  # ≥1024px (fixed, resizable)
└── app-left-sidebar.mobile.component.ts   # <1024px (drawer)

app-center-list/
├── app-center-list.component.ts
├── app-center-list.desktop.component.ts   # ≥1024px (2 colonnes)
└── app-center-list.mobile.component.ts    # <1024px (full-width)

app-note-view/
├── app-note-view.component.ts
├── app-note-view.desktop.component.ts     # ≥1024px (3 colonnes + ToC)
└── app-note-view.mobile.component.ts      # <1024px (full-width + ToC inline)

app-toc-drawer/
├── app-toc-drawer.component.ts
├── app-toc-drawer.desktop.component.ts    # ≥1024px (Fixed right)
└── app-toc-drawer.mobile.component.ts     # <1024px (Collapsible, inline)

Détection et Injection

// app-left-sidebar.component.ts
import { Component, inject } from '@angular/core';
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';

@Component({
  selector: 'app-left-sidebar',
  standalone: true,
  template: `
    @if (isDesktop$ | async) {
      <ng-container *ngComponentOutlet="DesktopSidebarComponent"></ng-container>
    } @else {
      <ng-container *ngComponentOutlet="MobileSidebarDrawerComponent"></ng-container>
    }
  `,
})
export class AppLeftSidebarComponent {
  private breakpoint = inject(BreakpointObserver);
  isDesktop$ = this.breakpoint.observe(Breakpoints.Large).pipe(
    map(result => result.matches)
  );
}

📱 Navigation Mobile Avancée

Tab/Drawer Navigation

// src/shared/services/mobile-nav.service.ts
@Injectable({ providedIn: 'root' })
export class MobileNavService {
  activeTab = signal<'sidebar' | 'list' | 'page' | 'toc'>('list');
  
  setTab(tab: 'sidebar' | 'list' | 'page' | 'toc') {
    this.activeTab.set(tab);
    // Persist if needed
  }
}

// Usage in component
<app-bottom-nav [activeTab]="mobileNav.activeTab()" 
                (tabChange)="mobileNav.setTab($event)">
</app-bottom-nav>

Swipe Navigation (Gestures)

// Directive pour détection de swipe
import { Directive, Output, EventEmitter, HostListener } from '@angular/core';

@Directive({
  selector: '[appSwipeNav]',
  standalone: true,
})
export class SwipeNavDirective {
  @Output() swipeLeft = new EventEmitter<void>();
  @Output() swipeRight = new EventEmitter<void>();

  private startX = 0;

  @HostListener('touchstart', ['$event'])
  onTouchStart(e: TouchEvent) {
    this.startX = e.touches[0].clientX;
  }

  @HostListener('touchend', ['$event'])
  onTouchEnd(e: TouchEvent) {
    const endX = e.changedTouches[0].clientX;
    const diff = this.startX - endX;
    
    if (Math.abs(diff) > 50) { // Seuil minimum
      if (diff > 0) this.swipeLeft.emit();
      else this.swipeRight.emit();
    }
  }
}

🎨 Composants Spécifiques (Mobile-First)

1) Bottom Navigation (Mobile)

<!-- app-bottom-nav.component.html -->
<nav class="fixed bottom-0 left-0 right-0 bg-white dark:bg-gray-900 border-t 
            border-gray-200 dark:border-gray-800 h-16 flex justify-around md:hidden">
  <button *ngFor="let tab of tabs"
          (click)="selectTab(tab.id)"
          [class.active]="activeTab === tab.id"
          class="flex-1 flex flex-col items-center justify-center gap-1 
                 text-xs hover:bg-gray-50 dark:hover:bg-gray-800">
    <span class="text-lg">{{ tab.icon }}</span>
    <span>{{ tab.label }}</span>
  </button>
</nav>

2) Drawer Sidebar (Mobile)

<!-- app-left-sidebar.mobile.component.html -->
<aside class="fixed left-0 top-0 bottom-0 w-80vw max-w-xs 
              bg-white dark:bg-gray-900 shadow-lg z-50
              transform transition-transform duration-300"
       [class.-translate-x-full]="!isOpen">
  <!-- Contenu sidebar -->
  <button (click)="close()" class="absolute top-4 right-4"></button>
</aside>

<!-- Backdrop -->
<div *ngIf="isOpen" 
     (click)="close()"
     class="fixed inset-0 bg-black/50 z-40 md:hidden"></div>

3) Search Bar Compact (Mobile)

<!-- app-search-bar.mobile.component.html -->
<div class="sticky top-0 bg-white dark:bg-gray-900 p-2 shadow-sm z-10">
  <div class="flex gap-2">
    <!-- Menu toggle -->
    <button (click)="toggleSidebar()" 
            class="p-2 rounded hover:bg-gray-100"></button>
    
    <!-- Search input (full-width on mobile) -->
    <input type="text" 
           placeholder="Search..." 
           class="flex-1 px-3 py-2 rounded border dark:border-gray-700">
    
    <!-- Filters button (mobile: popover instead of dropdown) -->
    <button (click)="openFilters()" 
            class="p-2 rounded hover:bg-gray-100">⚙️</button>
  </div>
  
  <!-- Active badges (scrollable horizontally) -->
  <div class="flex gap-1 mt-2 overflow-x-auto">
    <span *ngFor="let badge of activeBadges" 
          class="badge badge-sm">
      {{ badge }} ✕
    </span>
  </div>
</div>

4) Result List Item (Mobile-Optimized)

<!-- app-result-list-item.component.html -->
<div class="p-3 border-b hover:bg-gray-50 dark:hover:bg-gray-800 cursor-pointer">
  <!-- Title -->
  <h3 class="font-semibold text-sm sm:text-base truncate">
    {{ item.title }}
  </h3>
  
  <!-- Date + Tags (stacked on mobile) -->
  <div class="flex flex-col sm:flex-row sm:items-center gap-1 mt-1 text-xs text-gray-600">
    <span>{{ item.modified | date:'short' }}</span>
    <div class="flex gap-1 flex-wrap">
      <span *ngFor="let tag of item.tags" 
            class="badge badge-sm">{{ tag }}</span>
    </div>
  </div>
  
  <!-- Excerpt (truncated) -->
  <p class="mt-2 text-xs text-gray-600 line-clamp-2">
    {{ item.excerpt }}
  </p>
</div>

5) Markdown Viewer (Mobile-Responsive)

<!-- app-markdown-viewer.component.html -->
<article class="prose dark:prose-invert max-w-none 
                 prose-sm sm:prose-base 
                 prose-img:max-w-full prose-img:h-auto
                 px-3 sm:px-6 py-4 sm:py-8">
  <!-- Markdown content -->
  <div [innerHTML]="markdownHTML"></div>
  
  <!-- ToC (mobile: inline toggle) -->
  <button *ngIf="headings.length > 0"
          (click)="showToC = !showToC"
          class="lg:hidden fixed bottom-20 right-4 p-3 rounded-full bg-blue-500 text-white shadow-lg">
    📋
  </button>
  
  <nav *ngIf="showToC" class="lg:hidden fixed inset-0 bg-white dark:bg-gray-900 
                              z-40 overflow-y-auto p-4">
    <!-- ToC content -->
  </nav>
</article>

6) ToC Drawer (Desktop Fixed, Mobile Inline)

// app-toc-drawer.component.ts
@Component({
  selector: 'app-toc-drawer',
  template: `
    <!-- Desktop: Fixed right panel (≥1024px) -->
    <aside class="hidden lg:flex fixed right-0 top-14 bottom-0 w-64 
                  bg-gray-50 dark:bg-gray-800 border-l 
                  border-gray-200 dark:border-gray-700
                  flex-col overflow-y-auto">
      <app-toc-content [headings]="headings"></app-toc-content>
    </aside>
    
    <!-- Mobile: Collapsible inline (< 1024px) -->
    <div *ngIf="(isMobile$ | async)" 
         [@slideDown]="showTocMobile ? 'in' : 'out'"
         class="bg-gray-50 dark:bg-gray-800 border-t p-3 max-h-96 overflow-y-auto">
      <app-toc-content [headings]="headings"></app-toc-content>
    </div>
  `,
  animations: [
    trigger('slideDown', [
      state('in', style({ height: '*' })),
      state('out', style({ height: '0px' })),
      transition('in <=> out', animate('200ms ease-in-out')),
    ]),
  ],
  standalone: true,
})
export class AppTocDrawerComponent {
  @Input() headings: Heading[] = [];
  showTocMobile = false;
  isMobile$ = this.breakpoint.observe('(max-width: 1023px)').pipe(
    map(r => r.matches)
  );
}

🎯 Livrables Attendus

Code

  1. Toggle UI : UiModeService, bouton navbar, AppShellAdaptiveComponent
  2. Responsive Wrappers : Variants pour chaque composant (Desktop/Mobile), intégrant les patterns Nimbus
  3. Mobile Components : Bottom nav, drawer sidebar, inline ToC, mobile search
  4. Gesture Handling : Swipe navigation directive, touch-friendly interactions
  5. Breakpoint Utilities : Service CDK layout, reactive signals

Styling

  1. Tailwind Config : Breakpoints personnalisés, tokens clair/sombre
  2. Mobile-First CSS : Base mobile, enrichissements desktop via md:, lg:
  3. Touch-Friendly : Boutons ≥44x44px, padding adéquat, hover states remplacés par tap sur mobile

Documentation

  1. README_UI.md : Schémas responsive, breakpoints, guide toggle, intégration Nimbus
  2. MOBILE_GUIDE.md : Navigation gestures, bottom nav flow, drawer patterns
  3. RESPONSIVE_CHECKLIST.md : Tests par breakpoint, checklist A11y mobile

Tests

  1. E2E : Toggle persistence, layout switch, gesture navigation
  2. Visual Regression : Screenshots desktop/tablet/mobile
  3. Accessibility : Touch targets, ARIA labels, keyboard nav (Tab key)

Performance & Mobile Optimizations

Critical Optimizations

  • Lazy-load images : loading="lazy", responsive srcset
  • Virtual scroll : CDK virtual scroll adapté mobile (item height ≈ 7080px)
  • Debounce search : 300ms sur mobile, 150ms sur desktop
  • Avoid layout shift : Aspect ratios, skeleton loaders
  • Network awareness : navigator.connection.effectiveType pour adapt qualité
  • Battery saver : Réduire animations, throttle updates en mode saver

Lighthouse Mobile Targets

  • Performance ≥ 85
  • Accessibility ≥ 95
  • Best Practices ≥ 90

🎮 Raccourcis Clavier & Gestures

Desktop

  • Ctrl/Cmd + K : Palette commandes
  • Ctrl/Cmd + F : Focus recherche
  • [ ] : Replier/ouvrir ToC
  • Alt + ←/→ : Navigation historique

Mobile/Tablet

  • Tap : Ouvrir note/item
  • Swipe left/right : Changer onglet (list → page → sidebar)
  • Long-press : Menu contextuel (favoris, open in new tab)
  • Pull-to-refresh : Rafraîchir liste (optionnel)
  • Double-tap : Zoom ToC (mobile)

📋 Critères d'Acceptation

Desktop (≥1024px)

  • Layout 3 colonnes (sidebar fixe/resizable, liste, page+ToC), fidèle à Nimbus
  • Changement dossier/tag/tri reflété en URL
  • 1000+ items fluide (60fps virtual scroll)
  • ToC synchronisé + repliable
  • Tous les flux clavier-seuls possibles

Tablet (7681023px)

  • Navigation par onglets (Sidebar / List / Page)
  • Drawer sidebar (80vw, swipeable)
  • Bottom navigation sticky (4 icônes)
  • Contenu full-width par onglet
  • ToC inline collapsible

Mobile (<768px)

  • Drawer sidebar (80vw max)
  • Bottom nav (4 onglets)
  • Search bar compact (menu + search + filters)
  • List items optimisés (titre + date + excerpt)
  • Markdown full-screen
  • ToC overlay ou inline toggle
  • Touch targets ≥ 44x44px

Feature Flag

  • Toggle UI visible dans navbar
  • État persisté (localStorage)
  • Pas de perte d'état lors du switch
  • Legacy UI reste intacte

Accessibility

  • WCAG 2.1 AA sur tous les breakpoints
  • Keyboard navigation complète (Tab, Arrow, Enter)
  • ARIA labels pour navigation tactile
  • Focus visible partout
  • Zoom ≤ 200% sans horizontal scroll

Performance

  • TTI < 2.5s cold start
  • Scroll 60fps sur 1000+ items
  • Lighthouse Mobile ≥ 85 perf, ≥ 95 a11y
  • ImageOptimizations (lazy, srcset, format next-gen)

🗂️ Arborescence Fichiers

src/app/
├── layout/
│   ├── app-shell-adaptive/
│   │   └── app-shell-adaptive.component.ts      # Feature flag wrapper
│   ├── app-shell-nimbus/
│   │   ├── app-shell-nimbus.component.ts        # 3 colonnes (desktop)
│   │   ├── app-shell-nimbus.desktop.component.ts
│   │   └── app-shell-nimbus.mobile.component.ts
│   └── app-navbar/
│       ├── app-navbar.component.ts
│       └── [Bouton toggle UI intégré]
│
├── features/
│   ├── sidebar/
│   │   ├── app-left-sidebar.component.ts
│   │   ├── app-left-sidebar.desktop.component.ts
│   │   └── app-left-sidebar.mobile.component.ts
│   │
│   ├── search-bar/
│   │   ├── app-search-bar.component.ts
│   │   ├── app-search-bar.desktop.component.ts
│   │   └── app-search-bar.mobile.component.ts
│   │
│   ├── result-list/
│   │   ├── app-result-list.component.ts
│   │   ├── app-result-list.desktop.component.ts
│   │   ├── app-result-list.mobile.component.ts
│   │   └── app-result-list-item.component.ts
│   │
│   ├── note-view/
│   │   ├── app-note-view.component.ts
│   │   ├── app-note-view.desktop.component.ts
│   │   └── app-note-view.mobile.component.ts
│   │
│   ├── toc-drawer/
│   │   ├── app-toc-drawer.component.ts
│   │   └── app-toc-content.component.ts
│   │
│   └── bottom-nav/ [NEW]
│       ├── app-bottom-nav.component.ts
│       └── app-bottom-nav.component.html
│
├── shared/
│   ├── services/
│   │   ├── ui-mode.service.ts                   # [NEW] Toggle management
│   │   ├── mobile-nav.service.ts                # [NEW] Tab/drawer state
│   │   └── breakpoint.service.ts                # [NEW] Responsive helper
│   │
│   ├── directives/
│   │   └── swipe-nav.directive.ts               # [NEW] Gesture detection
│   │
│   └── components/
│       └── resizable-handle/
│
└── styles/
    ├── tokens.css                               # Tailwind tokens
    ├── responsive.css                           # Breakpoint utilities
    └── mobile.css                               # Mobile-specific (touches, etc.)

📅 Plan d'Implémentation (ordre conseillé)

  1. Feature Flag Infrastructure (1-2j)

    • UiModeService + localStorage persistence
    • AppShellAdaptiveComponent wrapper
    • Toggle button dans navbar
  2. Responsive Shell & Breakpoints (2-3j)

    • Desktop layout 3 colonnes (>=1024px)
    • Tailwind breakpoints & tokens
    • Resizable sidebar logic
  3. Mobile Navigation & Bottom Nav (2-3j)

    • BottomNavComponent (4 onglets)
    • MobileNavService (state management)
    • Tab/drawer routing
  4. Mobile Sidebar Drawer (1-2j)

    • Drawer animé (translate, backdrop)
    • Swipe dismiss directive
    • Z-index management
  5. Responsive Components (3-4j)

    • Search bar variants (desktop/mobile)
    • Result list item responsive
    • Markdown viewer mobile optimizations
  6. ToC Drawer Adaptive (1-2j)

    • Fixed right panel (desktop)
    • Inline toggle (mobile)
    • Animations smooth
  7. Gestures & Touch (1-2j)

    • Swipe nav directive
    • Long-press menu
    • Pull-to-refresh (optionnel)
  8. Accessibility & Testing (2-3j)

    • WCAG 2.1 AA audit
    • Keyboard nav (Tab, Arrow)
    • E2E tests (toggle, breakpoints, gestures)
    • Visual regression (3 breakpoints)

Total estimé : 1321 jours (équipe 1 FE engineer)


🚀 Scripts NPM

# Dev complet (Nimbus activé par défaut)
npm run dev

# Build production
npm run build

# Tests responsifs (plusieurs breakpoints)
npm run test:responsive

# Lighthouse audit mobile
npm run audit:lighthouse:mobile

# Feature flag (override)
NIMBUS_UI=false npm run dev        # Force legacy UI

Checklist Livraison

  • Toggle UI visible, fonctionnel, persisté
  • Desktop (≥1024px) : 3 colonnes, interactions fluides
  • Tablet (7681023px) : Onglets + drawer, full-width contenu
  • Mobile (<768px) : Bottom nav, drawer, touch-friendly
  • Tous les flux clavier-seuls réalisables
  • Lighthouse mobile ≥ 85 perf, ≥ 95 a11y
  • Virtual scroll 60fps sur 1000+ items
  • Tests E2E (toggle, breakpoints, gestures)
  • Documentation complète (README_UI.md, MOBILE_GUIDE.md, RESPONSIVE_CHECKLIST.md)
  • Zéro régression : legacy UI inchangée, Wikilinks, bookmarks, graph intacts
  • Screenshots before/after 3 breakpoints
  • Fidélité 100% à la description Nimbus (chips, pickers, outline, etc.)

📖 Documentation à Produire

  1. README_UI.md : Overview, architecture, screenshots 3 breakpoints
  2. MOBILE_GUIDE.md : Navigation onglets, gestures, drawer patterns
  3. RESPONSIVE_CHECKLIST.md : Tests par breakpoint, device testing
  4. DEPLOYMENT.md : Feature flag pour bascule progressive
  5. ARCHITECTURE_DIAGRAM.md : Schémas adaptatifs (Mermaid)

💡 Notes Importantes

  • Mobile First : Développer pour mobile en premier, puis enrichir desktop.
  • Persistent State : Le toggle UI et les filtres actifs doivent persister via localStorage (sans browser storage, utiliser sessionStorage ou service state).
  • Zero Regression : L'ancienne interface reste intacte et fonctionnelle.
  • Performance : Virtual scroll adapté mobile (40+ items à l'écran), lazy-load images.
  • Accessibility : 44x44px touch targets minimum, ARIA labels complets, keyboard nav.
  • Testing : Visual regression sur breakpoints clés (375px / 768px / 1440px).
  • Intégration Nimbus : Tous les éléments de la description (philosophie, layouts, micro-interactions) doivent être reproduits fidèlement, avec adaptations responsives pour mobile/tablette (ex. hover-reveal → tap-reveal).

Exécute maintenant ce plan : crée les composants, adapte les routes/états, ajoute les styles Tailwind responsifs, branche la recherche et livre le MR conforme aux critères ci-dessus avec toggle UI et compatibilité 100% Desktop/Mobile.