16 KiB
Voici le prompt refondu pour inclure la compatibilité mobile/tablet complète tout en conservant la philosophie Nimbus :
🎯 Prompt Windsurf — ObsiViewer Nimbus UI (Desktop + Mobile)
ObsiViewer → UI/UX "Nimbus-like" (responsive, 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 (Desktop ≥1024px / Tablet 768-1023px / Mobile <768px). Un bouton toggle dans la navbar permet de basculer entre l'ancienne interface et la nouvelle sans perte d'état.
📋 Architecture Nimbus Responsive
Desktop (≥1024px) - Layout 3 colonnes
┌─────────────────────────────────────────────────────────┐
│ NAVBAR (Search centré + CTA "+ Page" + Actions droite) │
├─────────────┬─────────────────────┬─────────────────────┤
│ SIDEBAR │ CENTER COLUMN │ NOTE + TOC │
│ Navigation │ • Chips filtres │ • Édition riche │
│ Dossiers/Tags│ • Liste résultats │ • Outline contextuel│
│ Quick Links │ • Virtual scroll │ • Barre actions │
└─────────────┴─────────────────────┴─────────────────────┘
Tablet (768-1023px) - Navigation par onglets
┌────────────────────────────────────┐
│ NAVBAR (Search + Toggle UI) │
├────────────────────────────────────┤
│ [≡] [📄] [🔍] [📋] Tabs bottom │
├────────────────────────────────────┤
│ │
│ CONTENU ACTIF FULL-WIDTH │
│ • Sidebar: Drawer 80vw │
│ • Liste: Cards compactes │
│ • Note: Markdown + ToC inline │
│ │
└────────────────────────────────────┘
Mobile (<768px) - Navigation bottom + Drawers
┌──────────────────────────────────┐
│ NAVBAR compacte (Menu + Search) │
├──────────────────────────────────┤
│ CONTENT AREA │
│ • Sidebar: Drawer full │
│ • Liste: Items optimisés │
│ • Note: Plein écran │
├──────────────────────────────────┤
│ [≡][📄][+][🔍][📋] Bottom Nav │
└──────────────────────────────────┘
🎯 Composants Nimbus Spécifiques à Reproduire
1) Top Bar Nimbus (Responsive)
// Desktop: Search centré + CTA turquoise
<header class="flex items-center justify-between p-4">
<!-- Gauche: Logo/Worspace -->
<div class="w-1/4">Workspace Selector</div>
<!-- Centre: Search large + filtre -->
<div class="flex-1 max-w-2xl">
<div class="relative">
<input type="text" placeholder="Search..." class="w-full pl-10 pr-4 py-2 rounded-lg border">
<span class="absolute left-3 top-2.5">🔍</span>
<button class="absolute right-3 top-2.5">⏷</button>
</div>
</div>
<!-- Droite: CTA + Actions -->
<div class="w-1/4 flex justify-end gap-2">
<button class="bg-cyan-500 text-white px-4 py-2 rounded-lg">+ Page</button>
<button>Ask AI</button>
<button>Share</button>
</div>
</header>
// Mobile: Stacked + icons
<header class="p-3 space-y-2">
<div class="flex justify-between">
<button>☰</button>
<button class="bg-cyan-500 text-white px-3 py-1 rounded">+</button>
</div>
<div class="relative">
<input type="text" placeholder="Search..." class="w-full pl-8 pr-3 py-1 rounded border">
<span class="absolute left-2 top-1.5">🔍</span>
</div>
</header>
2) Chips de Filtres Nimbus (Responsive)
// Desktop: Chips horizontales
<div class="flex gap-2 p-4 border-b">
<div class="chip-filter">
<span>All folders</span>
<span class="badge">+2</span>
<span>▾</span>
</div>
<div class="chip-filter">
<span>All tags</span>
<span class="badge">+1</span>
<span>▾</span>
</div>
<div class="chip-filter">
<span>All pages</span>
<span>▾</span>
</div>
</div>
// Mobile: Chips scrollables horizontalement
<div class="flex gap-1 p-3 border-b overflow-x-auto">
<div class="chip-filter flex-shrink-0">
<span>Folders</span>
<span>▾</span>
</div>
<div class="chip-filter flex-shrink-0">
<span>Tags</span>
<span>▾</span>
</div>
<!-- ... -->
</div>
3) Picker Overlays Nimbus (Responsive)
// Desktop: Overlay centré
<div class="fixed inset-0 bg-black/20 z-50 flex items-start justify-center pt-20">
<div class="bg-white rounded-lg shadow-xl w-96 max-h-96 overflow-hidden">
<!-- Header avec recherche -->
<div class="p-3 border-b">
<input type="text" placeholder="Search folders..." class="w-full p-2 border rounded">
</div>
<!-- Liste avec hiérarchie -->
<div class="overflow-y-auto max-h-64">
<div class="flex items-center p-2 hover:bg-gray-100">
<input type="checkbox" class="mr-2">
<span>📁</span>
<span class="ml-2 flex-1">Workspace</span>
<span>›</span>
</div>
<!-- Sous-dossiers indentés -->
<div class="flex items-center p-2 pl-6 hover:bg-gray-100">
<input type="checkbox" class="mr-2">
<span>📁</span>
<span class="ml-2 flex-1">Projects</span>
<span class="text-xs text-gray-500">15</span>
</div>
</div>
<!-- Footer actions -->
<div class="flex justify-between p-3 border-t">
<button class="text-gray-600">Clear</button>
<button class="bg-cyan-500 text-white px-4 py-1 rounded">Done</button>
</div>
</div>
</div>
// Mobile: Picker full-screen
<div class="fixed inset-0 bg-white z-50">
<div class="p-4 border-b flex justify-between items-center">
<h3 class="font-semibold">Select Folders</h3>
<button class="text-cyan-500">Done</button>
</div>
<div class="p-3 border-b">
<input type="text" placeholder="Search folders..." class="w-full p-2 border rounded">
</div>
<div class="overflow-y-auto h-full pb-20">
<!-- Liste mobile-optimisée -->
</div>
</div>
4) Outline Contextuel Nimbus (Responsive)
// Desktop: Panneau droit fixe
<aside class="fixed right-0 top-14 bottom-0 w-64 border-l bg-gray-50 overflow-y-auto hidden lg:block">
<div class="p-4">
<h4 class="font-semibold mb-3">Outline</h4>
<nav class="space-y-1">
<a class="block py-1 text-sm text-gray-600 hover:text-gray-900"># Introduction</a>
<a class="block py-1 pl-4 text-sm text-gray-600 hover:text-gray-900">## Getting Started</a>
<!-- ... -->
</nav>
</div>
</aside>
// Mobile: Bouton flottant + overlay
<button class="fixed bottom-20 right-4 w-12 h-12 bg-cyan-500 text-white rounded-full shadow-lg lg:hidden"
(click)="showTocMobile = true">
📋
</button>
<div *ngIf="showTocMobile" class="fixed inset-0 bg-white z-50 lg:hidden">
<div class="p-4 border-b flex justify-between">
<h3 class="font-semibold">Table of Contents</h3>
<button (click)="showTocMobile = false">✕</button>
</div>
<div class="p-4 overflow-y-auto h-full">
<!-- Contenu ToC -->
</div>
</div>
🎨 Palette Nimbus & Design Tokens
/* Tokens Nimbus (light/dark) */
:root {
/* Couleurs primaires */
--nimbus-cyan: #06b6d4;
--nimbus-cyan-dark: #0891b2;
/* Gris scale */
--nimbus-gray-50: #f9fafb;
--nimbus-gray-100: #f3f4f6;
--nimbus-gray-800: #1f2937;
--nimbus-gray-900: #111827;
/* Espacements */
--nimbus-space-1: 0.25rem;
--nimbus-space-2: 0.5rem;
--nimbus-space-3: 0.75rem;
/* Typographie */
--nimbus-text-sm: 0.875rem;
--nimbus-text-base: 1rem;
}
.dark {
--nimbus-bg: var(--nimbus-gray-900);
--nimbus-surface: var(--nimbus-gray-800);
}
📱 Gestes Mobiles Spécifiques Nimbus
Swipe Navigation
@Directive({
selector: '[appSwipeNav]',
standalone: true
})
export class SwipeNavDirective {
private startX = 0;
private startY = 0;
@HostListener('touchstart', ['$event'])
onTouchStart(e: TouchEvent) {
this.startX = e.touches[0].clientX;
this.startY = e.touches[0].clientY;
}
@HostListener('touchend', ['$event'])
onTouchEnd(e: TouchEvent) {
const endX = e.changedTouches[0].clientX;
const endY = e.changedTouches[0].clientY;
const diffX = this.startX - endX;
const diffY = this.startY - endY;
// Seulement swipe horizontal significatif
if (Math.abs(diffX) > 50 && Math.abs(diffY) < 30) {
if (diffX > 0) {
this.swipeLeft.emit(); // → Tab suivant
} else {
this.swipeRight.emit(); // ← Tab précédent
}
}
}
@Output() swipeLeft = new EventEmitter<void>();
@Output() swipeRight = new EventEmitter<void>();
}
Hover-reveal Adaptatif (Desktop seulement)
// Seulement sur desktop
<div class="group relative">
<div class="flex items-center">
<span>📁 Projects</span>
<button class="opacity-0 group-hover:opacity-100 transition-opacity lg:block hidden ml-2">
+
</button>
</div>
</div>
// Mobile: Toujours visible ou menu contextuel
<div class="flex items-center justify-between lg:justify-start">
<span>📁 Projects</span>
<button class="lg:hidden ml-2">+</button>
</div>
🔧 Service UI Mode & Responsive State
@Injectable({ providedIn: 'root' })
export class UiModeService {
// Signal pour le mode Nimbus vs Legacy
isNimbusMode = signal<boolean>(this.loadUIMode());
// Signal pour le breakpoint actif
currentBreakpoint = signal<'mobile' | 'tablet' | 'desktop'>('desktop');
constructor(private breakpointObserver: BreakpointObserver) {
// Surveillance des breakpoints
this.breakpointObserver.observe([
'(max-width: 767px)',
'(min-width: 768px) and (max-width: 1023px)',
'(min-width: 1024px)'
]).subscribe(result => {
const breakpoints = result.breakpoints;
if (breakpoints['(max-width: 767px)']) {
this.currentBreakpoint.set('mobile');
} else if (breakpoints['(min-width: 768px) and (max-width: 1023px)']) {
this.currentBreakpoint.set('tablet');
} else {
this.currentBreakpoint.set('desktop');
}
});
}
toggleUIMode() {
const newMode = !this.isNimbusMode();
this.isNimbusMode.set(newMode);
localStorage.setItem('obsiviewer-ui-mode', newMode ? 'nimbus' : 'legacy');
}
private loadUIMode(): boolean {
return localStorage?.getItem('obsiviewer-ui-mode') !== 'legacy';
}
}
🚀 Composants Spécifiques Nimbus
1) Bookmark Card (Responsive)
// Desktop: Carte horizontale
<div class="border rounded-lg p-3 hover:shadow-md transition-shadow">
<div class="flex gap-3">
<img src="thumbnail.jpg" class="w-20 h-20 rounded object-cover flex-shrink-0">
<div class="flex-1 min-w-0">
<h4 class="font-semibold truncate">Article Title</h4>
<p class="text-sm text-gray-600 line-clamp-2">Description of the bookmarked content...</p>
<div class="flex items-center gap-2 mt-1">
<span class="text-xs text-gray-500">example.com</span>
<span class="text-xs text-gray-500">2 days ago</span>
</div>
</div>
</div>
</div>
// Mobile: Carte verticale
<div class="border rounded-lg p-3 hover:shadow-md transition-shadow">
<img src="thumbnail.jpg" class="w-full h-32 rounded object-cover mb-2">
<h4 class="font-semibold truncate">Article Title</h4>
<p class="text-sm text-gray-600 line-clamp-2">Description...</p>
<div class="flex justify-between items-center mt-2">
<span class="text-xs text-gray-500">example.com</span>
<span class="text-xs text-gray-500">2 days ago</span>
</div>
</div>
2) Result List Item Nimbus
// Desktop: Ligne compacte
<div class="flex items-center gap-3 p-3 border-b hover:bg-gray-50 cursor-pointer">
<div class="w-6 h-6 rounded bg-blue-100 flex items-center justify-center text-sm">📄</div>
<div class="flex-1 min-w-0">
<h4 class="font-medium truncate">Document Title</h4>
<p class="text-sm text-gray-600 truncate">First few lines of content as preview...</p>
</div>
<div class="flex items-center gap-3 text-sm text-gray-500">
<span>2h ago</span>
<div class="flex gap-1">
<span class="bg-gray-100 px-2 py-0.5 rounded text-xs">work</span>
</div>
</div>
</div>
// Mobile: Stack vertical
<div class="p-3 border-b hover:bg-gray-50 cursor-pointer">
<div class="flex items-start gap-2">
<div class="w-6 h-6 rounded bg-blue-100 flex items-center justify-center text-sm mt-0.5">📄</div>
<div class="flex-1 min-w-0">
<h4 class="font-medium">Document Title</h4>
<p class="text-sm text-gray-600 line-clamp-2">First few lines of content as preview...</p>
<div class="flex items-center justify-between mt-1">
<span class="text-xs text-gray-500">2h ago</span>
<div class="flex gap-1">
<span class="bg-gray-100 px-2 py-0.5 rounded text-xs">work</span>
</div>
</div>
</div>
</div>
</div>
📋 Critères d'Acceptation Nimbus
Desktop (≥1024px)
- Layout 3 colonnes fidèle à Nimbus
- Top bar avec search centré + CTA "+ Page" turquoise
- Chips filtres "All folders ▾", "All tags ▾", "All pages ▾" avec badges
- Pickers overlays avec recherche locale + hiérarchie
- Outline contextuel panneau droit fixe
- Hover-reveal création sous-dossiers
- Workspace switcher dans sidebar
Tablet (768-1023px)
- Navigation par onglets [Sidebar] [List] [Page] [ToC]
- Drawer sidebar 80vw avec arborescence
- Chips scrollables horizontalement
- Pickers full-screen avec Done/Clear
- ToC inline collapsible
Mobile (<768px)
- Bottom navigation 5 onglets [Menu] [List] [+] [Search] [ToC]
- Drawers full-width pour sidebar/pickers
- Search bar compacte dans header
- Touch targets ≥ 44px
- Swipe gestures changement onglets
- Pull-to-refresh liste résultats
Fidélité Nimbus
- Philosophie workspace-first avec sélecteur d'espace
- Organisation bi-axiale dossiers + tags
- Recherche d'abord avec filtres persistants
- Progressive disclosure (menus contextuels)
- Bookmarks avec cartes enrichies
- Boutons Share/Add to portal visibles
Performance & Accessibilité
- Virtual scroll 1000+ items fluide
- Lighthouse Mobile ≥ 85 perf, ≥ 95 a11y
- Keyboard navigation complète
- WCAG 2.1 AA tous breakpoints
- Toggle UI persisté sans perte état
🎯 Livrables Exacts
- Toggle UI Nimbus/Legacy avec persistence
- Layout 3 colonnes desktop fidèle Nimbus
- Navigation responsive tablette/mobile
- Composants Nimbus : Chips, Pickers, Outline, Bookmark cards
- Gestes mobiles : swipe, pull-to-refresh
- Thème clair/sombre Nimbus-like
- Documentation responsive + patterns Nimbus
État initial : Toggle sur "Nimbus" par défaut, legacy UI intacte Testing : 3 breakpoints + gestures + accessibility Performance : 60fps virtual scroll, lazy loading images
Exécute maintenant cette implémentation en respectant scrupuleusement les patterns UI/UX de Nimbus Notes tout en garantissant la compatibilité 100% responsive mobile/tablet/desktop.