feat: Complete internationalization and code cleanup

## Translation Files
- Add 11 new language files (es, de, pt, ru, zh, ja, ko, ar, hi, nl, pl)
- Add 100+ missing translation keys across all 15 languages
- New sections: notebook, pagination, ai.batchOrganization, ai.autoLabels
- Update nav section with workspace, quickAccess, myLibrary keys

## Component Updates
- Update 15+ components to use translation keys instead of hardcoded text
- Components: notebook dialogs, sidebar, header, note-input, ghost-tags, etc.
- Replace 80+ hardcoded English/French strings with t() calls
- Ensure consistent UI across all supported languages

## Code Quality
- Remove 77+ console.log statements from codebase
- Clean up API routes, components, hooks, and services
- Keep only essential error handling (no debugging logs)

## UI/UX Improvements
- Update Keep logo to yellow post-it style (from-yellow-400 to-amber-500)
- Change selection colors to #FEF3C6 (notebooks) and #EFB162 (nav items)
- Make "+" button permanently visible in notebooks section
- Fix grammar and syntax errors in multiple components

## Bug Fixes
- Fix JSON syntax errors in it.json, nl.json, pl.json, zh.json
- Fix syntax errors in notebook-suggestion-toast.tsx
- Fix syntax errors in use-auto-tagging.ts
- Fix syntax errors in paragraph-refactor.service.ts
- Fix duplicate "fusion" section in nl.json

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

Ou une version plus courte si vous préférez :

feat(i18n): Add 15 languages, remove logs, update UI components

- Create 11 new translation files (es, de, pt, ru, zh, ja, ko, ar, hi, nl, pl)
- Add 100+ translation keys: notebook, pagination, AI features
- Update 15+ components to use translations (80+ strings)
- Remove 77+ console.log statements from codebase
- Fix JSON syntax errors in 4 translation files
- Fix component syntax errors (toast, hooks, services)
- Update logo to yellow post-it style
- Change selection colors (#FEF3C6, #EFB162)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-11 22:26:13 +01:00
parent fc2c40249e
commit 7fb486c9a4
183 changed files with 48288 additions and 1290 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -31,7 +31,7 @@ workflow_status:
# Phase 2: Planning
prd: _bmad-output/planning-artifacts/prd.md
create-ux-design: conditional
create-ux-design: _bmad-output/planning-artifacts/ux-design-specification.md
# Phase 3: Solutioning
create-architecture: required
@@ -39,5 +39,26 @@ workflow_status:
test-design: optional
implementation-readiness: _bmad-output/planning-artifacts/implementation-readiness-report-2026-01-09.md
# PROJECT-SPECIFIC STATUS
# ======================
# Notebooks & Labels Contextuels Project (2026-01-11)
notebooks_contextual_labels:
prd: _bmad-output/planning-artifacts/notebooks-contextual-labels-prd.md
ux_design: _bmad-output/excalidraw-diagrams/notebooks-wireframes.md
architecture: _bmad-output/planning-artifacts/notebooks-contextual-labels-architecture.md
architecture_status: VALIDATED
architecture_validated_date: "2026-01-11"
tech_specs: _bmad-output/planning-artifacts/notebooks-tech-specs.md
tech_specs_status: COMPLETE
tech_specs_created_date: "2026-01-11"
epics_stories: _bmad-output/planning-artifacts/notebooks-epics-stories.md
epics_status: COMPLETE
epics_created_date: "2026-01-11"
total_epics: 6
total_stories: 34
total_points: 97
next_phase: "sprint-planning"
# Phase 4: Implementation
sprint-planning: required

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,348 @@
# Memory Echo - UX Improvements Backlog
**Date:** 2025-01-11
**Author:** Sally (UX Designer Agent)
**Project:** Keep - Memory Echo Feature
---
## 🎯 Problem Statement
**User:** Ramez has 22+ similar notes and needs better tools to manage semantic connections.
**Current State:**
- Temporary modal showing 2 connected notes side-by-side
- Notifications when new connections detected
- Basic feedback (thumbs up/down)
- Fusion feature exists but needs better integration
**User Pain Points:**
1. *"Once we see 2-3 identical notes, how do we put them side-by-side?"* - Better management of similar notes
2. *"Can we have a merge button?"* - Intelligent fusion of similar notes
3. *"Can we put them side-by-side on a sketch?"* - Mind-map / graph view of connections
**Constraints:**
- Must remain intuitive and not clutter the UI
- Must integrate cleanly with existing Masonry grid
- Must handle scale (potentially 22+ similar notes)
- Must not overwhelm the user
---
## 💡 UX Proposals
### 1⃣ Better Connection Display & Management
#### **Proposal: Persistent Slide-over Panel**
**Location:** Navigation bar with badge counter
```
[Notes] [Archive] [🔗 Connexions (23)] ← Badge shows total notes with connections
```
**Interaction:**
- Click badge → Slide-over panel opens from right
- Shows hierarchical list of all connections grouped by similarity
- Click on connection → Scroll to & highlight that note in grid
- Hover over note in grid → Highlight connections in slide-over
**UI Layout:**
```
┌─────────────────────────────────────────────────────┐
│ [Notes] [Archive] [🔗 Connexions (23)] │
├─────────────────────────────────────────────────────┤
│ Grille Masonry existante │
│ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │ Note │ │ Note │ │ Note │ │
│ │ 1 │ │ 2 │ │ 3 │ │
│ └──────┘ └──────┘ └──────┘ │
│ │
│ ┌─────────────────────────────────┐ ← Toggle │
│ │ 🔗 Connexions (Slide-over) │ (right side) │
│ │ ├─ Note A (3 connexions) │ │
│ │ │ ├─ Note B (85%) │ │
│ │ │ └─ Note C (72%) │ │
│ │ └─ Note D (12 connexions) │ │
│ │ ├─ Note E (91%) │ │
│ │ └─ ... │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
```
**Features:**
- **Filter controls:** "Show only notes with 5+ connections", "Similarity 80%+"
- **Group by similarity:** Cluster similar notes
- **Search:** Search through connections
- **Collapse/Expand:** Manage large lists
- **Quick actions:** Checkbox multiple notes → "Compare selected" / "Merge selected"
**Why It Works:**
- ✅ Non-intrusive: Doesn't hide the grid
- ✅ Overview: See all connections at once
- ✅ Navigation: Quick access to any connection
- ✅ Scalable: Handles 50+ connections
---
### 2⃣ Intelligent Note Fusion
#### **Proposal: Grouped Actions + Smart Merge**
**A. In Slide-over Panel:**
```
┌──────────────────────────────────────┐
│ 🔗 Group of similar notes │
│ ┌─────────────────────────────────┐ │
│ │ ☑ Note A - "Machine Learning" │ │
│ │ ☑ Note B - "ML basics" │ │
│ │ ☑ Note C - "Intro ML" │ │
│ │ │ │
│ │ [🔀 Merge 3 notes] │ │ ← Primary button
│ └─────────────────────────────────┘ │
└──────────────────────────────────────┘
```
**B. Existing Fusion Modal (Already Implemented!)**
Current modal features:
- Preview AI-generated fusion
- Select which notes to merge
- Custom prompt
- Options (archive originals, keep tags, etc.)
**C. New Feature: "Quick Merge"**
For very similar notes (90%+ similarity):
```
[⚡ Quick Merge] → Automatically archives originals
→ Creates fused note
→ Adds "Fused" badge to originals with link to new note
```
**Workflow:**
```
1. User opens slide-over
2. Sees group of 5 similar notes
3. Option A: Check all 5 → Click "Merge" → Opens custom modal
Option B: Click "⚡ Quick Merge" → Instant merge with smart defaults
4. New note created with "Fused" badge
5. Original notes archived with link to fused note
```
**Why It Works:**
- ✅ Scale: Handle 22+ notes without selecting one-by-one
- ✅ Control: Quick merge for obvious duplicates, custom for nuanced cases
- ✅ Visual feedback: "Fused" badge traces history
- ✅ Reversible: Archive keeps originals accessible
---
### 3⃣ Mind-Map / Graph View
#### **Proposal: Toggle Graph View**
**New Navigation Button:**
```
[Notes] [Archive] [🕸️ Graph] ← New view
```
**Graph View UI:**
```
┌─────────────────────────────────────────────────────┐
│ 🔙 Back to Grid 🔍 Zoom 🎨 Clusters │
├─────────────────────────────────────────────────────┤
│ │
│ [Note A]────────────[Note B] │
│ │ \ / │
│ 85% 72% 91% │
│ │ \ / │
│ [Note C]────[Note D]────[Note E] │
│ │
│ 💡 Cluster "Machine Learning" (5 notes) │
│ │ │
│ [Note F]────────[Note G] │
│ │ │
│ 💡 Cluster "React" (3 notes) │
│ │
└─────────────────────────────────────────────────────┘
Legend:
─ Thick line = 80%+ similarity (highly connected)
─ Thin line = 50-79% similarity
─ 💡 = Auto-clustered by theme (AI)
```
**Features:**
- **Drag & drop:** Reposition notes manually
- **Click note:** Opens modal with:
- Full note content
- Connections with percentages
- Actions: "Merge with selected", "View in grid"
- **Auto-clusters:** AI groups similar thematically (ML, React, etc.)
- **Filters:** "Show only 70%+ connections", "Hide archived"
- **Zoom & pan:** Navigate large graphs
- **Export:** Save graph as image or JSON
**Why It Works:**
- ✅ Immediate visual: See everything at once
- ✅ Scalable: Handles 50+ connections
- ✅ Actionable: Click → Compare → Merge
- ✅ Discovery: Clusters reveal patterns
- ✅ Exploration: Serendipitous connections
**Tech Stack Recommendations:**
- **React Flow** (https://reactflow.dev/) - React-native, excellent performance
- **D3.js** (https://d3js.org/) - Powerful but steeper learning curve
- **Cytoscape.js** (https://js.cytoscape.org/) - Specialized for graphs
---
## 📋 Implementation Phases
### Phase 1 - Quick Win (1-2 days)
**Features:**
- [ ] Badge "🔗 Connexions (X)" in navigation
- [ ] Slide-over panel with connection list
- [ ] Checkbox selection + "Merge" button (uses existing modal)
- [ ] Filter controls (similarity threshold, count)
**Files to Create/Modify:**
- `components/connections-slide-over.tsx` (NEW)
- `components/connections-nav-badge.tsx` (NEW)
- Modify navigation to include badge
- Integrate with existing `/api/ai/echo/connections` endpoint
**Effort:** Low
**Impact:** High
**Risk:** Low
---
### Phase 2 - Graph View (3-5 days)
**Features:**
- [ ] Toggle "🕸️ Graph" view
- [ ] Basic graph visualization (React Flow)
- [ ] Click interactions (open modal, highlight connections)
- [ ] Zoom & pan
- [ ] Basic filters
**Files to Create/Modify:**
- `app/(main)/connections/page.tsx` (NEW - graph view page)
- `components/connections-graph.tsx` (NEW)
- Install `reactflow` package
- Navigation update
**Effort:** Medium
**Impact:** High
**Risk:** Medium (learning curve for React Flow)
---
### Phase 3 - Advanced Features (5-7 days)
**Features:**
- [ ] Auto-clustering by theme (AI)
- [ ] "Quick Merge" for 90%+ similar notes
- [ ] Export graph (image/JSON)
- [ ] Advanced filters (date range, labels)
- [ ] Graph layouts (force, hierarchical, circular)
**Files to Create/Modify:**
- `/api/ai/echo/clusters` (NEW)
- `components/quick-merge-button.tsx` (NEW)
- Enhanced graph component with layouts
- Export functionality
**Effort:** High
**Impact:** Medium
**Risk:** Medium
---
## 🎨 UI/UX Considerations
### Color Scheme
- **Connections Badge:** Amber (already used)
- **Fused Badge:** Purple (already used)
- **Graph Nodes:** Color by cluster/theme
- **Graph Edges:** Gradient by similarity (green = high, yellow = medium, gray = low)
### Responsive Design
- **Mobile:** Slide-over becomes bottom sheet
- **Tablet:** Slide-over 50% width
- **Desktop:** Slide-over 400px fixed width
- **Graph:** Touch interactions for mobile
### Accessibility
- Keyboard navigation for all actions
- Screen reader support for graph view
- High contrast mode support
- Focus indicators
### Performance
- Lazy load connection list (pagination)
- Virtual scroll for large lists
- Debounce graph interactions
- Cache graph layout
---
## 📊 Success Metrics
**User Engagement:**
- % of users opening connections panel
- Average connections viewed per session
- Graph view adoption rate
**Feature Usage:**
- Number of merges per week
- % of quick merges vs custom merges
- Most used similarity threshold
**User Satisfaction:**
- Feedback on graph view usability
- Time to merge similar notes
- Reduction in duplicate notes over time
---
## 🚨 Open Questions
1. **Default similarity threshold:** What should be the default? (Proposed: 70%)
2. **Max connections to display:** Should we cap the list? (Proposed: 50, with pagination)
3. **Auto-archival:** Should "Quick Merge" auto-archive or ask user? (Proposed: Auto-archive with undo)
4. **Graph layout:** Which layout should be default? (Proposed: Force-directed)
5. **Cluster naming:** AI-generated or user-editable? (Proposed: AI-generated with edit option)
---
## 📝 Notes
- All translations already exist in `locales/fr.json` and `locales/en.json`
- Fusion modal already implemented and working
- Connections API endpoint already exists: `/api/ai/echo/connections`
- Badge components already created: `ConnectionsBadge`, `FusionBadge` (inline)
- Current UI issue fixed: Badges now at top, labels after content, owner indicator visible
---
## 🔗 Related Files
- `components/connections-badge.tsx` - Badge component
- `components/connections-overlay.tsx` - Overlay component
- `components/fusion-modal.tsx` - Fusion modal
- `components/note-card.tsx` - Note card with badges
- `app/api/ai/echo/connections/route.ts` - Connections API
- `app/api/ai/echo/fusion/route.ts` - Fusion API
- `locales/fr.json` - French translations
- `locales/en.json` - English translations
---
**Status:** 📋 Ready for Implementation
**Priority:** Phase 1 > Phase 2 > Phase 3
**Next Steps:** Review with Ramez, prioritize features, begin Phase 1 implementation

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,990 @@
# Product Requirements Document (PRD)
## Notebooks & Labels Contextuels avec IA
**Project:** Keep (Memento Phase 1 MVP AI)
**Date:** 2026-01-11
**Author:** Sally (UX Designer) + Ramez (Product Owner)
**Status:** Draft - Ready for Architecture
**Priority:** High - Core Feature Reorganization
---
## 📋 Executive Summary
### Vision
Transformer l'organisation de Keep d'un système de tags plat en une structure de **Notebooks avec Labels Contextuels**, où chaque notebook a sa propre taxonomie de labels, permettant une organisation plus naturelle et contextuelle.
### Objectifs Principaux
1. ✅ Introduire les **Notebooks** comme organisation principale
2. ✅ Rendre les **Labels contextuels** à chaque notebook
3. ✅ Créer une **Inbox** ("Notes générales") pour les notes non organisées
4. ✅ Intégrer l'**IA** intelligemment dans cette nouvelle structure
5. ✅ Permettre une **migration douce** depuis le système actuel
---
## 🎯 User Stories
### Primary Users
- **Ramez (Power User):** Utilise Keep quotidiennement pour organiser voyage, travail, vie perso
- **Professionnel:** Gère des projets avec des contextes différents
- **Voyageur:** Organise ses préparatifs de voyage avec des notes spécifiques
### User Journey Exemple
#### Scénario 1: Création de note dans Notebook
```
1. Ramez ouvre Keep, navigue vers Notebook "Voyage"
2. Il voit les labels contextuels: #hôtels, #vols, #restos
3. Il clique "Nouvelle note"
4. La note est automatiquement assignée au Notebook "Voyage"
5. Il peut tagger avec #hôtels (disponible car dans le bon contexte)
```
#### Scénario 2: Note rapide dans Inbox
```
1. Ramez a une idée rapide, ouvre Keep (page d'accueil)
2. Il tape son idée et sauve
3. La note va dans "Notes générales" (Inbox)
4. Plus tard, il la déplace vers "Notebook Perso"
5. Les labels de "Perso" deviennent disponibles
```
#### Scénario 3: Organisation IA-assistée
```
1. Ramez a 15 notes dans "Notes générales"
2. Il clique "Organiser avec l'IA"
3. L'IA analyse les notes et propose:
- "3 notes pour Notebook Voyage"
- "5 notes pour Notebook Travail"
- "7 notes pour Notebook Perso"
4. Ramez valide les suggestions
5. Les notes sont déplacées automatiquement
```
---
## 🏗️ Structure de l'Organisation
### Hiérarchie
```
KEEP
├─ 📥 Notes générales (Inbox)
│ └─ Notes sans notebook assigné
│ └─ PAS de labels (zone temporaire)
├─ 📚 Notebooks (ordonnés manuellement)
│ ├─ ✈️ Voyage
│ │ ├─ Labels: #hôtels, #vols, #restos, #à_visiter
│ │ └─ Notes assignées à "Voyage"
│ │
│ ├─ 💼 Travail
│ │ ├─ Labels: #réunions, #projets, #urgent, #à_faire
│ │ └─ Notes assignées à "Travail"
│ │
│ └─ 📖 Perso
│ ├─ Labels: #idées, #rêves, #objectifs, #réflexions
│ └─ Notes assignées à "Perso"
└─ [+] Nouveau Notebook
```
### Règles Métier
#### R1: Appartenance des Notes
- **Une note appartient à UN seul notebook** (ou aucune)
- Les notes dans "Notes générales" n'appartiennent à aucun notebook
- Une note ne peut être dans plusieurs notebooks simultanément
#### R2: Labels Contextuels
- Chaque notebook a ses propres labels (100% isolés)
- Les labels sont créés/supprimés au niveau notebook
- Les notes dans "Notes générales" n'ont pas accès aux labels
#### R3: Ordre des Notebooks
- Les notebooks sont ordonnés manuellement (drag & drop)
- L'ordre est personnalisé par utilisateur
- Drag & drop dans la sidebar pour réorganiser
#### R4: Vue "Notes générales"
- Affiche SEULEMENT les notes sans notebook
- PAS de vue "Toutes les notes" (tous notebooks confondus)
- C'est une zone temporaire d'organisation
---
## 🎨 UX/UI Specifications
### 1. Navigation - Sidebar
```
┌─────────────────────────────────────┐
│ KEEP LOGO │
├─────────────────────────────────────┤
│ 🔍 Search │
├─────────────────────────────────────┤
│ 📚 NOTEBOOKS │
│ ┌───────────────────────────────┐ │
│ │ 📥 Notes générales (12) │ │ ← Compteur de notes
│ │ │ │
│ │ ✈️ Voyage (8) │ │ ← Notebook actif = highlight
│ │ ┌─ 🏷️ Labels contextuels │ │
│ │ │ • #hôtels (3) │ │ ← Labels seulement si actif
│ │ │ • #vols (2) │ │
│ │ │ • #restos (3) │ │
│ │ │ [+ Nouveau label] │ │
│ │ └─────────────────────────────┘ │
│ │ │ │
│ │ 💼 Travail (15) │ │ ← Handles pour drag & drop
│ │ ║ ║ │ │
│ │ 📖 Perso (23) │ │
│ │ ║ ║ │ │
│ └───────────────────────────────┘ │
│ │
│ [+ Nouveau Notebook] │
└─────────────────────────────────────┘
```
**Comportements:**
- **Click sur notebook** → Navigue vers ce notebook
- **Drag & drop des notebooks** → Réorganise l'ordre
- **Hover sur notebook** → Affiche les labels contextuels
- **[+ Nouveau label]** → Crée un label dans ce notebook
- **Compteurs** → Montre le nombre de notes
### 2. Création de Note
#### Cas A: Depuis un Notebook
```
User dans "Voyage" → [Nouvelle note]
├─ Note créée avec notebookId = "voyage"
├─ Peut utiliser les labels de "Voyage"
└─ UI: Badge "Voyage" visible sur la note
```
#### Cas B: Depuis Notes Générales
```
User sur page d'accueil → [Nouvelle note]
├─ Note créée avec notebookId = null
├─ PAS de labels disponibles
└─ UI: Badge "À trier" visible
```
#### Cas C: Création dans un autre notebook
```
User dans "Voyage", veut créer pour "Travail"
├─ DOIT naviguer vers "Travail" d'abord
├─ OU utilise le raccourci clavier (ex: Ctrl+N → chooser)
└─ PAS de modal à chaque création
```
### 3. Déplacement de Notes (Option C: A + B)
#### Méthode A: Drag & Drop
```
┌─────────────────────────────────────┐
│ 📝 Note à déplacer │
│ ┌───────────────────────────────┐ │
│ │ Grip handle │ Note content... │ │ ← Drag depuis ici
│ └───────────────────────────────┘ │
│ ↓ │
│ Drop vers sidebar → │
│ ┌───────────────────────────────┐ │
│ │ ✈️ Voyage [Drop zone] │ │
│ │ 💼 Travail [Drop zone] │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
```
#### Méthode B: Menu Contextuel
```
Sur une note → Click droit → Menu:
├─ 📋 Copier
├─ ✏️ Modifier
├─ 📚 Déplacer vers...
│ ├─ 📥 Notes générales
│ ├─ ✈️ Voyage
│ ├─ 💼 Travail
│ └─ 📖 Perso
├─ 🏷️ Ajouter un label
├─ 📌 Épingler
└─ 🗑️ Supprimer
```
**Validation:**
- ✅ Drag & drop vers notebook dans sidebar
- ✅ Menu contextuel "Déplacer vers..."
- ✅ Les deux méthodes disponibles
### 4. Labels Contextuels
#### Création de Label
```
Dans Notebook "Voyage":
┌─────────────────────────────────────┐
│ 🏷️ Labels │
│ • #hôtels • #vols • #restos │
│ [+ Nouveau label] │ ← Click
├─────────────────────────────────────┤
│ Modal: │
│ ┌───────────────────────────────┐ │
│ │ Nom du label: │ │
│ │ [___________] │ │
│ │ │ │
│ │ Couleur: ○ 🟡 ○ 🔴 ○ 🔵 │ │
│ │ │ │
│ │ [Annuler] [Créer] │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
```
#### Assignation de Label à Note
```
Note dans "Voyage" → Click "Ajouter label"
├─ Seuls les labels de "Voyage" sont proposés
├─ Dropdown avec checkboxes
└─ Multi-label possible sur une note
Exemple:
📝 Note: "Hôtel Tokyo Shibuya"
├─ Notebook: ✈️ Voyage
└─ Labels: #hôtels, #réservations
```
#### Suppression de Label
```
Options:
├─ Supprimer le label (du notebook)
│ └─ Warning: "Ce label sera retiré de X notes. Continuer?"
└─ Retirer des notes seulement
└─ Label existe toujours, mais plus utilisé
```
### 5. Gestion des Notebooks
#### Création de Notebook
```
Click [+ Nouveau Notebook]
├─ Modal de création
│ ├─ Nom: "Voyage"
│ ├─ Icône: [Sélecteur d'emoji]
│ ├─ Couleur: [Sélecteur de couleur]
│ └─ [Créer]
└─ Notebook créé à la fin de la liste
```
#### Édition de Notebook
```
Click droit sur notebook → Menu:
├─ ✏️ Modifier
│ └─ Modal: Nom, Icône, Couleur
├─ 📊 Statistiques
│ ├─ Nombre de notes
│ ├─ Labels utilisés
│ └─ Dernière mise à jour
├─ 🗑️ Supprimer
│ └─ Warning: "Les notes seront déplacées vers Notes générales"
└─ ❌ Fermer
```
#### Réorganisation (Drag & Drop)
```
✈️ Voyage ║ ║ ← Drag handle
💼 Travail ║ ║
📖 Perso ║ ║
Drag "Travail" vers le haut → Réordonne
```
---
## 🤖 Intégration IA
C'est la partie CRUCIALE qui rend cette feature vraiment puissante.
### IA1: Suggestion Automatique de Notebook
#### Scénario
```
User crée une note dans "Notes générales":
"Rendez-vous dermatologue mardi 15h à Paris"
IA analyse et suggère:
┌─────────────────────────────────────┐
│ 💡 Suggestion IA │
│ ┌───────────────────────────────┐ │
│ │ Cette note semble appartenir │ │
│ │ au notebook "Perso". │ │
│ │ │ │
│ │ [Ignorer] [Déplacer vers Perso]│ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
```
**Prompt IA:**
```
"Analyse cette note et suggère le notebook le plus approprié:
Note: {content}
Notebooks disponibles: {notebook_names_with_labels}
Réponds avec:
- notebook_suggéré: string
- confiance: 0-1
- raisonnement: string"
```
**Déclencheurs:**
- Note créée dans "Notes générales"
- Note modifiée significativement
- 5+ secondes après la fin de frappe (pas en temps réel)
### IA2: Suggestion de Labels Contextuels
#### Scénario
```
Note dans Notebook "Voyage":
"Hotel Shibuya Excel - 150€/nuit - Booking confirmé"
IA suggère:
┌─────────────────────────────────────┐
│ 💡 Suggestions de labels │
│ ┌───────────────────────────────┐ │
│ │ ✅ #hôtels (confiance: 95%) │ │ ← Click pour assigner
│ │ ✅ #réservations (80%) │ │
│ │ ✅ #tokyo (70%) │ │
│ │ │ │
│ │ [Tout sélectionner] [Ignorer] │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
```
**Prompt IA:**
```
"Analyse cette note et suggère les labels appropriés:
Note: {content}
Notebook actuel: {notebook_name}
Labels disponibles dans ce notebook: {available_labels}
Réponds avec un tableau de:
- label: string (doit être dans {available_labels})
- confiance: 0-1
- raisonnement: string"
```
**Comportement:**
- ✅ Maximum 3 suggestions
- ✅ Seulement si confiance > 60%
- ✅ Labels cliquables pour assignation en 1 clic
- ✅ Ne pas déranger si l'utilisateur tape activement
### IA3: Organisation Intelligente (Batch Processing)
#### Scénario
```
User a 20 notes dans "Notes générales"
Click "Organiser avec l'IA"
├─ IA analyse toutes les notes
├- Groupe par thématique
└─ Présente un plan d'organisation:
┌─────────────────────────────────────┐
│ 📋 Plan d'organisation IA │
│ ┌───────────────────────────────┐ │
│ │ Notebook: Voyage (5 notes) │ │
│ │ • Hotel Tokyo... │ │
│ │ • Vols JAL... │ │
│ │ • Restaurant Shibuya... │ │
│ │ [Tout sélectionner] │ │
│ │ │ │
│ │ Notebook: Travail (8 notes) │ │
│ │ • Réunion lundi... │ │
│ │ • Projet Alpha... │ │
│ │ ... │ │
│ │ │ │
│ │ Notebook: Perso (7 notes) │ │
│ │ • Idées livre... │ │
│ │ ... │ │
│ │ │ │
│ │ [Annuler] [Appliquer tout] │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
```
**Prompt IA:**
```
"Analyse ces {count} notes et propose une organisation:
{notes_with_content}
Notebooks disponibles: {notebooks}
Pour chaque notebook, indique:
- notebook_cible: string
- notes: [{note_id, note_title, confidence, raison}]
Retourne un plan d'organisation optimisé."
```
**Validation:**
- ✅ User peut désélectionner des notes
- ✅ User peut ajuster les destinations
- ✅ Confirmation avant application
- ✅ Undo possible (Ctrl+Z)
### IA4: Création Automatique de Labels
#### Scénario
```
Notebook "Voyage" devient peuplé de notes sur le Japon
IA détecte:
- 10+ notes mentionnant "Tokyo"
- 8+ notes mentionnant "Kyoto"
- 5+ notes mentionnant "Osaka"
IA suggère:
┌─────────────────────────────────────┐
│ 💡 Suggestions de nouveaux labels │ │
│ ┌───────────────────────────────┐ │
│ │ J'ai détecté des thèmes récurrents│
│ │ dans vos notes. Créer des labels?│
│ │ │ │
│ │ ✅ #tokyo (10 notes) │ │
│ │ ✅ #kyoto (8 notes) │ │
│ │ ✅ #osaka (5 notes) │ │
│ │ │ │
│ │ [Annuler] [Créer et assigner] │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
```
**Déclencheur:**
- Notebook atteint 15+ notes
- IA détecte 3+ mots-clés récurrents (5+ fois chacun)
- Ne propose PAS si les labels existent déjà
### IA5: Recherche Sémantique par Notebook
#### Scénario
```
User dans Notebook "Voyage" tape:
"endroit pour dormir pas cher"
IA comprend le contexte "Voyage" et cherche:
├─ Semantic search DANS ce notebook seulement
├- Priorise les labels #hôtels, #auberges
└- Résultats plus pertinents car contextuels
Résultats:
┌─────────────────────────────────────┐
│ 🔍 Résultats dans "Voyage" │
│ ┌───────────────────────────────┐ │
│ │ 📝 Capsule Hotel Shinjuku │ │
│ │ #hôtels #tokyo │ │
│ │ "Hotel capsule 30€/nuit..." │ │
│ │ Correspondance: 87% │ │
│ │ │ │
│ │ 📝 Airbnb Asakusa │ │
│ │ #hôtels #tokyo │ │
│ │ "Appartement 45€/nuit..." │ │
│ │ Correspondance: 82% │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
```
**Avantage:**
- ✅ Recherche contextuelle au notebook
- ✅ Résultats plus pertinents
- ✅ Comprend le jargon spécifique (ex: "vol" dans Voyage vs Travail)
### IA6: Synthèse par Notebook
#### Scénario
```
User clique "Résumer ce notebook" dans "Voyage"
IA génère:
┌─────────────────────────────────────┐
│ 📊 Synthèse du Notebook Voyage │
│ ┌───────────────────────────────┐ │
│ │ 🌍 Destinations │ │
│ │ • Japon (Tokyo, Kyoto) │ │
│ │ • France (Paris) │ │
│ │ │ │
│ │ 📅 Dates │ │
│ │ • 15-25 Mars 2024 │ │
│ │ │ │
│ │ 🏨 Réservations │ │
│ │ • 3 hôtels réservés │ │
│ │ • 2 vols confirmés │ │
│ │ • 5 restaurants identifiés │ │
│ │ │ │
│ │ 💰 Budget estimé: 3500€ │ │
│ │ │ │
│ │ ⚠️ Actions requises │ │
│ │ • Réserver visa japonais │ │
│ │ • Confirmer assurance voyage │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
```
**Prompt IA:**
```
"Génère une synthèse structurée de ce notebook:
{notes_with_labels}
Inclus:
- Destinations/Thèmes principaux
- Dates importantes
- Éléments réservés vs planifiés
- Actions requises
- Statistiques (nombre de notes, labels utilisés)
Format: Markdown structuré avec emojis."
```
---
## 🗄️ Structure de Données (Database Schema)
### Prisma Schema - Nouveaux Modèles
```prisma
// Modèle Notebook
model Notebook {
id String @id @default(cuid())
name String
icon String? // Emoji: "✈️", "💼", "📖"
color String? // Hex color: "#FF6B6B"
order Int // Ordre manuel dans la sidebar
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
notes Note[]
labels Label[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([userId, order])
}
// Modèle Label MODIFIÉ - Ajout notebookId
model Label {
id String @id @default(cuid())
name String
color String? // Couleur du label
notebookId String // NOUVEAU: Label appartient à un notebook
notebook Notebook @relation(fields: [notebookId], references: [id], onDelete: Cascade)
notes Note[] // Relation many-to-many via NoteLabel
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([notebookId, name]) // Un label est unique dans un notebook
@@index([notebookId])
}
// Modèle Note MODIFIÉ - Ajout notebookId (optionnel)
model Note {
id String @id @default(cuid())
title String?
content String
// ... autres champs existants ...
notebookId String? // NOUVEAU: Optionnel - null = dans "Notes générales"
notebook Notebook? @relation(fields: [notebookId], references: [id], onDelete: SetNull)
// Garantir qu'une note est dans UN SEUL notebook
@@index([userId, notebookId])
}
// Table de jonction Note-Label (existante mais gardée)
model NoteLabel {
noteId String
labelId String
note Note @relation(fields: [noteId], references: [id], onDelete: Cascade)
label Label @relation(fields: [labelId], references: [id], onDelete: Cascade)
@@id([noteId, labelId])
@@index([noteId])
@@index([labelId])
}
```
### Clés de la Structure
**Règles d'intégrité:**
1.`Note.notebookId` est **optionnel** (null = Notes générales)
2.`Label.notebookId` est **obligatoire** (labels TOUJOURS contextuels)
3.`@@unique([notebookId, name])` = Unicité des labels DANS un notebook
4.`onDelete: SetNull` sur Note→Notebook = Si notebook supprimé, notes → Notes générales
### Migration Schema
```sql
-- Étape 1: Ajouter les nouveaux modèles
CREATE TABLE "Notebook" (
"id" TEXT NOT NULL PRIMARY KEY,
"name" TEXT NOT NULL,
"icon" TEXT,
"color" TEXT,
"order" INTEGER NOT NULL,
"userId" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- Étape 2: Ajouter notebookId aux Notes (optionnel)
ALTER TABLE "Note" ADD COLUMN "notebookId" TEXT;
ALTER TABLE "Note" ADD FOREIGN KEY ("notebookId") REFERENCES "Notebook"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- Étape 3: Ajouter notebookId aux Labels (obligatoire)
ALTER TABLE "Label" ADD COLUMN "notebookId" TEXT NOT NULL DEFAULT 'TEMP_MIGRATION';
ALTER TABLE "Label" ADD FOREIGN KEY ("notebookId") REFERENCES "Notebook"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- Étape 4: Créer notebook par défaut pour la migration
INSERT INTO "Notebook" (id, name, icon, color, "order", "userId")
VALUES (
'migration_default',
'Labels existants',
'📦',
'#9CA3AF',
999,
{user_id}
);
-- Étape 5: Assigner tous les labels existants à ce notebook
UPDATE "Label" SET "notebookId" = 'migration_default' WHERE "notebookId" = 'TEMP_MIGRATION';
-- Étape 6: Laisser toutes les notes SANS notebook (Notes générales)
-- Rien à faire - notebookId est déjà NULL par défaut
-- Étape 7: Créer index pour performance
CREATE INDEX "Note_userId_notebookId_idx" ON "Note"("userId", "notebookId");
CREATE UNIQUE INDEX "Label_notebookId_name_key" ON "Label"("notebookId", "name");
```
---
## 🔄 Migration des Données Existantes
### Stratégie de Migration
#### Phase 1: Pré-migration (Backend)
```typescript
// app/actions/migration/prepare-notebooks.ts
'use server'
export async function prepareNotebookMigration() {
const session = await auth()
if (!session?.user?.id) throw new Error('Unauthorized')
// 1. Créer notebook "Import" pour les labels existants
const importNotebook = await prisma.notebook.create({
data: {
name: 'Labels existants',
icon: '📦',
color: '#9CA3AF',
order: 999,
userId: session.user.id
}
})
// 2. Assigner TOUS les labels existants à ce notebook
await prisma.label.updateMany({
where: { userId: session.user.id },
data: { notebookId: importNotebook.id }
})
// 3. Laisser les notes SANS notebook (Notes générales)
// Rien à faire - notebookId est NULL par défaut
return { success: true, importNotebookId: importNotebook.id }
}
```
#### Phase 2: Migration Interactive (User Journey)
```
┌─────────────────────────────────────────────────────────┐
│ 🎉 Bienvenue dans les Notebooks ! │
│ │
│ Nous avons organisé vos étiquettes existantes dans │
│ le notebook "Labels existants" pour ne rien perdre. │
│ │
│ 📊 État actuel: │
│ • 15 notes sans notebook (à organiser) │
│ • 1 notebook "Labels existants" │
│ • 12 étiquettes préservées │
│ │
│ Que voulez-vous faire ? │
│ │
│ [1] Laisser l'IA organiser mes notes │
│ [2] Explorer et créer mes propres notebooks │
│ [3] Tout déplacer vers "Notes générales" │
│ │
│ [Plus tard] Je déciderai plus tard │
└─────────────────────────────────────────────────────────┘
```
#### Phase 3: Organisation IA (Option 1)
Si user choisit "Laisser l'IA organiser":
```typescript
// app/actions/migration/ai-organize.ts
'use server'
export async function organizeWithAI() {
const session = await auth()
const notesWithoutNotebook = await prisma.note.findMany({
where: {
userId: session.user.id,
notebookId: null
}
})
// IA analyse et propose des notebooks
const suggestions = await aiService.suggestNotebooks(notesWithoutNotebook)
return {
success: true,
suggestions: [
{
notebookName: 'Voyage',
icon: '✈️',
color: '#3B82F6',
notes: [/* notes suggérées */],
confidence: 0.89
},
{
notebookName: 'Travail',
icon: '💼',
color: '#10B981',
notes: [/* notes suggérées */],
confidence: 0.92
}
]
}
}
```
#### Phase 4: Validation User
```
┌─────────────────────────────────────────────────────────┐
│ 📋 Plan d'organisation proposé par l'IA │
│ │
│ ✈️ Voyage (5 notes) - Confiance: 89% │
│ ☑ Hotel Tokyo Shibuya │
│ ☑ Vols JAL Tokyo-Paris │
│ ☑ Restaurant Shibuya │
│ ☑ Visa japonais │
│ ☑ Itinéraire Kyoto │
│ │
│ 💼 Travail (8 notes) - Confiance: 92% │
│ ☑ Réunion lundi │
│ ☑ Projet Alpha │
│ ☑ ... │
│ │
│ 📖 Perso (2 notes) - Confiance: 76% │
│ ☑ Idées livre │
│ ☑ Objectifs 2024 │
│ │
│ [Désélectionner] [Annuler] [Appliquer] │
└─────────────────────────────────────────────────────────┘
```
---
## 🎯 Success Metrics
### KPIs à Mesurer
**Adoption:**
- % d'utilisateurs créant au moins 1 notebook dans les 30 jours
- Nombre moyen de notebooks par utilisateur actif
- % de notes organisées (avec notebook) vs notes générales
**Engagement:**
- Temps passé par notebook (ex: Voyage plus actif avant un voyage)
- Fréquence d'utilisation des labels contextuels
- Taux d'utilisation des suggestions IA
**Satisfaction:**
- NPS (Net Promoter Score) sur la feature notebooks
- % d'utilisateurs gardant le système par défaut (Import) vs créant les leurs
- Taux d'abandon lors de la migration
**Performance IA:**
- Taux d'acceptation des suggestions IA (notebook)
- Taux d'acceptation des suggestions IA (labels)
- Précision des suggestions (feedback utilisateur)
---
## 🚀 Implementation Phases
### Phase 1: MVP (Weeks 1-3)
**Objectif:** Structure de base sans IA
- ✅ Database schema (Notebook, Label modifié, Note modifié)
- ✅ API endpoints (CRUD notebooks)
- ✅ UI: Sidebar avec notebooks
- ✅ UI: Création/édition de notebooks
- ✅ UI: Assignation de notebook aux notes
- ✅ UI: Labels contextuels (affichage)
- ✅ UI: Drag & drop des notebooks
- ✅ Migration: Notebook "Import" par défaut
- ❌ PAS d'IA encore
### Phase 2: IA Features (Weeks 4-5)
**Objectif:** IA pour organisation intelligente
- ✅ IA1: Suggestion automatique de notebook
- ✅ IA2: Suggestion de labels contextuels
- ✅ IA3: Organisation batch (Notes générales → Notebooks)
- ✅ UI: Modals de suggestions IA
- ✅ Feedback loop (accepter/rejeter suggestions)
### Phase 3: Advanced IA (Weeks 6-7)
**Objectif:** Features IA avancées
- ✅ IA4: Création automatique de labels
- ✅ IA5: Recherche sémantique contextuelle
- ✅ IA6: Synthèse par notebook
- ✅ Analytics: Dashboard d'utilisation des notebooks
### Phase 4: Polish & Testing (Week 8)
**Objectif:** Finalisation et tests
- ✅ Playwright E2E tests
- ✅ Performance optimization
- ✅ Accessibility audit
- ✅ Beta testing avec users
- ✅ Documentation
---
## 🚨 Risques & Mitigations
### Risque 1: Résistance au changement
**Description:** Users habitués aux tags globaux pourraient rejeter les notebooks
**Mitigation:**
- Phase de migration douce (optionnel)
- Mode hybride temporaire (garder vue tags pendant transition)
- Tutoriels interactifs
- Onboarding progressif
### Risque 2: Performance IA
**Description:** Suggestions IA pourraient être lentes ou inexactes
**Mitigation:**
- Cache des suggestions (24h)
- Seuils de confiance minimums (>60%)
- Feedback loop pour améliorer le modèle
- Fallback rapide si IA timeout
### Risque 3: Migration des données
**Description:** Perte de données ou organisation pendant la migration
**Mitigation:**
- Backup automatique avant migration
- Migration par défaut (notebook "Import")
- Possibilité de revenir en arrière (rollback)
- Tests exhaustifs de migration
### Risque 4: Complexité UX
**Description:** Trop de clics pour organiser les notes
**Mitigation:**
- Drag & drop intuitif
- Raccourcis clavier
- IA pour automatiser l'organisation
- Mesures d'usabilité (clics, temps)
### Risque 5: Labels contextuels = perte de flexibilité
**Description:** Users ne peuvent plus utiliser un label global (#urgent partout)
**Mitigation:**
- Éduquer: "Urgent" peut être recréé dans chaque notebook
- IA suggère de recréer les labels importants
- Option: Labels "favoris" synchronisés (feature future)
---
## 📚 Glossaire
- **Notebook:** Collection de notes sur un thème (ex: Voyage, Travail)
- **Labels Contextuels:** Tags spécifiques à un notebook (ex: #hôtels dans Voyage)
- **Inbox / Notes générales:** Zone temporaire pour les notes non organisées
- **IA:** Intelligence Artificielle (OpenAI ou Ollama)
- **Suggestion IA:** Proposition automatique basée sur l'analyse du contenu
- **Drag & Drop:** Action de glisser-déposer pour déplacer des éléments
- **Migration:** Transition du système de tags vers les notebooks
- **Notebook par défaut:** Notebook créé automatiquement pour préserver les tags existants
---
## 📝 Notes pour l'Architecture Team
### Points Critiques à Implémenter
1. **Database:**
- `Note.notebookId` est OPTIONNEL (null = Notes générales)
- `Label.notebookId` est OBLIGATOIRE
- Contrainte d'unicité: `@@unique([notebookId, name])`
2. **API:**
- Nouveau endpoint: `/api/notebooks` (CRUD complet)
- Endpoint modifié: `/api/labels` (filtre par notebook)
- Endpoint modifié: `/api/notes` (filtre par notebook)
3. **UI Components:**
- `SidebarNotebooks`: Liste des notebooks avec drag & drop
- `NotebookSelector`: Dropdown pour choisir le notebook
- `ContextualLabels`: Labels filtrés par notebook actif
- `AISuggestions`: Modals pour les suggestions IA
4. **IA Services:**
- `NotebookSuggestionService`: IA pour suggérer un notebook
- `LabelSuggestionService`: IA pour suggérer des labels
- `BatchOrganizationService`: IA pour organiser en lot
- `AutoLabelCreationService`: IA pour créer des labels
5. **Performance:**
- Index sur `Note.userId + Note.notebookId`
- Cache des suggestions IA (Redis ou in-memory)
- Virtual scrolling pour les notebooks avec 100+ notes
---
## ✅ Checklist de Validation
Avant de passer en développement, confirmer:
- [ ] Ramez valide l'UX décrite dans ce document
- [ ] L'Architecture Team a revu le schéma DB
- [ ] L'équipe IA a validé les prompts proposés
- [ ] Les risques sont acceptables et des mitigations sont en place
- [ ] Le plan de migration est testé sur un dataset de test
- [ ] Les mesures de succès (KPIs) sont définies et traçables
- [ ] Le wireframe UI est validé par Ramez
- [ ] L'implémentation est planifiée sur 8 semaines max
---
**Status:** ✅ PRD COMPLET - Prêt pour Architecture et Wireframes
**Next Steps:**
1. Créer les wireframes UX (Option XW)
2. Définir l'architecture technique
3. Commencer Phase 1 (MVP)
---
*Document créé par Sally (UX Designer) avec Ramez (Product Owner)*
*Date: 2026-01-11*
*Version: 1.0 - Final*

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -33,7 +33,7 @@ L'objectif est de créer un flux de travail où la capture reste instantanée, m
Contrairement à Google Keep (pas d'IA) et Notion (IA à la demande et complexe), Memento Phase 1 introduit **trois innovations exclusives**:
1. **Contextual Smart Assistance** - Les fonctionnalités IA n'apparaissent que quand c'est pertinent:
- Suggestions de titres uniquement après 50+ mots sans titre
- Suggestions de titres uniquement après 10+ mots sans titre
- Toast non-intrusive avec options "Voir | Plus tard | Ne plus demander"
- L'utilisateur découvre les features naturellement sans être overwhelmed
@@ -199,7 +199,7 @@ Phase 1 respects existing patterns:
**Performance:**
- **Recherche sémantique:** < 300ms pour une base de 1000 notes
- **Pourquoi:** Ne pas ralentir l'UX, l'utilisateur ne doit pas attendre
- **Suggestions titres:** < 2s après détection (50+ mots sans titre)
- **Suggestions titres:** < 2s après détection (10+ mots sans titre)
- **Pourquoi:** Doit paraître "instantané" pour l'utilisateur
- **Memory Echo analysis:** Traitement en arrière-plan, blocage UI < 100ms
- **Pourquoi:** L'utilisateur ne doit jamais sentir que l'IA "travaille"
@@ -248,7 +248,7 @@ Phase 1 respects existing patterns:
**Must-Have Capabilities:**
**1. Intelligent Title Suggestions**
- Déclenchement automatique après 50+ mots sans titre
- Déclenchement automatique après 10+ mots sans titre
- Toast non-intrusive: "J'ai 3 idées de titres pour ta note, les voir?"
- 3 suggestions IA présentées en dropdown
- Options: "Voir" | "Plus tard" | "Ne plus demander"
@@ -383,7 +383,7 @@ Trois mois plus tard:
- **"C'est comme si j'avais un assistant de recherche personnel qui lit tout ce que j'écris"**
**Journey Requirements Revealed:**
- Détection intelligente du moment opportun (50+ mots sans titre)
- Détection intelligente du moment opportun (10+ mots sans titre)
- Recherche sémantique qui comprend l'intention, pas juste les mots
- Memory Echo avec fréquence contrôlable (pas spam)
- Feedback utilisateur pour apprentissage (👍👎)
@@ -734,7 +734,7 @@ Memento introduces a new interaction pattern for AI features: **context-aware ap
**The Pattern:**
**Example 1 - Title Suggestions:**
- **Trigger:** User writes 50+ words without a title
- **Trigger:** User writes 10+ words without a title
- **Appearance:** Subtle toast: "I have 3 title ideas for this note, view them?"
- **Options:** "View" | "Not now" | "Don't ask for this note"
- **Outcome:** Feature discovered naturally, not overwhelming
@@ -1109,7 +1109,7 @@ Memento Phase 1 MVP IA combines two MVP philosophies:
**Core User Journeys Supported:**
**Journey 1: Alex (Primary User - Success Path)**
- Title suggestions when writing 50+ words
- Title suggestions when writing 10+ words
- Semantic search finds notes by meaning
- Memory Echo reveals hidden connections
- Complete workflow: capture → search → discover
@@ -1127,7 +1127,7 @@ Memento Phase 1 MVP IA combines two MVP philosophies:
**Must-Have Capabilities (MVP):**
**1. Intelligent Title Suggestions**
- Déclenchement automatique après 50+ mots sans titre
- Déclenchement automatique après 10+ mots sans titre
- Toast notification non-intrusive avec options "Voir | Plus tard | Ne plus demander"
- 3 suggestions IA générées en < 2s
- Application one-click ou saisie manuelle

View File

@@ -0,0 +1,687 @@
---
project_name: 'Keep (Memento Phase 1 MVP AI)'
user_name: 'Ramez'
date: '2026-01-10'
sections_completed: ['technology_stack', 'language_rules', 'framework_rules', 'testing_rules', 'quality_rules', 'workflow_rules', 'anti_patterns']
status: 'complete'
rule_count: 50
optimized_for_llm: true
workflow_type: 'generate-project-context'
---
# Project Context for AI Agents
_This file contains critical rules and patterns that AI agents must follow when implementing code in this project. Focus on unobvious details that agents might otherwise miss._
---
## Technology Stack & Versions
### Core Framework
**Frontend:**
- **Next.js:** 16.1.1 (App Router)
- **React:** 19.2.3
- **TypeScript:** 5.x (strict mode enabled)
**Backend:**
- **Next.js API Routes** (REST)
- **Server Actions** ('use server' directive)
- **Prisma:** 5.22.0 (ORM)
- **Database:** SQLite (better-sqlite3)
**Authentication:**
- **NextAuth:** 5.0.0-beta.30
- **Adapter:** @auth/prisma-adapter
**AI/ML:**
- **Vercel AI SDK:** 6.0.23
- **OpenAI Provider:** @ai-sdk/openai ^3.0.7
- **Ollama Provider:** ollama-ai-provider ^1.2.0
- **Language Detection:** tinyld (to be installed for Phase 1)
**UI Components:**
- **Radix UI:** Multiple primitives (@radix-ui/react-*)
- **Tailwind CSS:** 4.x
- **Lucide Icons:** ^0.562.0
- **Sonner:** ^2.0.7 (toast notifications)
**Utilities:**
- **Zod:** ^4.3.5 (schema validation)
- **date-fns:** ^4.1.0 (date formatting)
- **clsx:** ^2.1.1, **tailwind-merge:** ^3.4.0 (CSS utilities)
- **katex:** ^0.16.27 (LaTeX rendering)
- **react-markdown:** ^10.1.0 (markdown rendering)
**Drag & Drop:**
- **@dnd-kit:** ^6.3.1 (modern DnD library)
- **muuri:** ^0.9.5 (masonry grid layout)
**Testing:**
- **Playwright:** ^1.57.0 (E2E tests)
---
## Critical Implementation Rules
### TypeScript Configuration
**STRICT MODE ENABLED:**
```json
{
"strict": true,
"target": "ES2017",
"moduleResolution": "bundler",
"jsx": "react-jsx",
"paths": {
"@/*": ["./*"]
}
}
```
**CRITICAL RULES:**
- ✅ All files MUST be typed (no `any` without explicit reason)
- ✅ Use `interface` for object shapes, `type` for unions/primitives
- ✅ Import from `@/` alias (not relative paths like `../`)
- ✅ Props MUST be typed with interfaces (PascalCase names)
**Example:**
```typescript
// ✅ GOOD
interface NoteCardProps {
note: Note
onEdit?: (note: Note) => void
}
export function NoteCard({ note, onEdit }: NoteCardProps) {
// ...
}
// ❌ BAD - any without reason
export function NoteCard({ note, onEdit }: any) {
// ...
}
```
---
### Component Patterns
**Directives Required:**
- ✅ Server Components: No directive (default in Next.js 16 App Router)
- ✅ Client Components: `'use client'` at TOP of file (line 1)
- ✅ Server Actions: `'use server'` at TOP of file (line 1)
**Example:**
```typescript
// keep-notes/components/ai/ai-suggestion.tsx
'use client'
import { useState } from 'react'
import { Button } from '@/components/ui/button'
export function AiSuggestion() {
// Interactive component logic
}
```
**Component Naming:**
-**PascalCase** for component names: `NoteCard`, `LabelBadge`, `AiSuggestion`
-**kebab-case** for file names: `note-card.tsx`, `label-badge.tsx`, `ai-suggestion.tsx`
-**UI components** in `components/ui/` subdirectory: `button.tsx`, `dialog.tsx`
**Props Pattern:**
```typescript
// ✅ GOOD - Interface export
export interface NoteCardProps {
note: Note
onEdit?: (note: Note, readOnly?: boolean) => void
isDragging?: boolean
}
export function NoteCard({ note, onEdit, isDragging }: NoteCardProps) {
// ...
}
```
**Imports Order:**
```typescript
// 1. React imports
import { useState, useEffect } from 'react'
// 2. Third-party libraries
import { formatDistanceToNow } from 'date-fns'
import { Bell } from 'lucide-react'
// 3. Local imports (use @/ alias)
import { Card } from '@/components/ui/card'
import { Note } from '@/lib/types'
import { deleteNote } from '@/app/actions/notes'
```
---
### Server Actions Pattern
**CRITICAL: All server actions MUST follow this pattern:**
```typescript
// keep-notes/app/actions/ai-suggestions.ts
'use server'
import { auth } from '@/auth'
import { revalidatePath } from 'next/cache'
import { prisma } from '@/lib/prisma'
export async function generateTitleSuggestions(noteId: string) {
// 1. Authentication check
const session = await auth()
if (!session?.user?.id) {
throw new Error('Unauthorized')
}
try {
// 2. Business logic
const note = await prisma.note.findUnique({
where: { id: noteId }
})
if (!note) {
throw new Error('Note not found')
}
// 3. AI processing
const titles = await generateTitles(note.content)
// 4. Revalidate cache
revalidatePath('/')
return { success: true, titles }
} catch (error) {
console.error('Error generating titles:', error)
throw new Error('Failed to generate title suggestions')
}
}
```
**CRITICAL RULES:**
-`'use server'` at line 1 (before imports)
-**ALWAYS** check `auth()` session first
-**ALWAYS** `revalidatePath('/')` after mutations
- ✅ Use `try/catch` with `console.error()` logging
- ✅ Throw `Error` objects (not strings)
- ✅ Return `{ success: true, data }` or throw error
---
### API Routes Pattern
**CRITICAL: All API routes MUST follow this pattern:**
```typescript
// keep-notes/app/api/ai/titles/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
const requestSchema = z.object({
content: z.string().min(1, "Content required"),
noteId: z.string().optional()
})
export async function POST(req: NextRequest) {
try {
// 1. Parse and validate request
const body = await req.json()
const { content, noteId } = requestSchema.parse(body)
// 2. Business logic
const titles = await generateTitles(content)
// 3. Return success response
return NextResponse.json({
success: true,
data: { titles }
})
} catch (error) {
// 4. Error handling
if (error instanceof z.ZodError) {
return NextResponse.json(
{ success: false, error: error.issues },
{ status: 400 }
)
}
console.error('Error generating titles:', error)
return NextResponse.json(
{ success: false, error: 'Failed to generate titles' },
{ status: 500 }
)
}
}
```
**CRITICAL RULES:**
- ✅ Use **Zod schemas** for request validation
- ✅ Return `{ success: true, data: any }` for success
- ✅ Return `{ success: false, error: string }` for errors
- ✅ Handle `ZodError` separately (400 status)
- ✅ Log errors with `console.error()`
-**NEVER** expose stack traces to clients
**Response Format:**
```typescript
// Success
{ success: true, data: { ... } }
// Error
{ success: false, error: "Human-readable error message" }
```
---
### Database Access Pattern
**SINGLE DATA ACCESS LAYER:**
-**ONLY** use Prisma ORM (no raw SQL, no direct database access)
- ✅ Import from `@/lib/prisma` (singleton instance)
- ✅ Use `findMany`, `findUnique`, `create`, `update`, `delete`
```typescript
// ✅ GOOD
import { prisma } from '@/lib/prisma'
const notes = await prisma.note.findMany({
where: { userId: session.user.id },
orderBy: { createdAt: 'desc' }
})
```
**Prisma Schema Conventions:**
-**PascalCase** for model names: `User`, `Note`, `Label`, `AiFeedback`
-**camelCase** for fields: `userId`, `isPinned`, `createdAt`
- ✅ Foreign keys: `{model}Id` format: `userId`, `noteId`
- ✅ Booleans: prefix `is` for flags: `isPinned`, `isArchived`
- ✅ Timestamps: suffix `At` for dates: `createdAt`, `updatedAt`
- ✅ All new fields optional (nullable) for backward compatibility
---
### Naming Conventions
**Database:**
- Tables: **PascalCase** (`AiFeedback`, `MemoryEchoInsight`)
- Columns: **camelCase** (`noteId`, `similarityScore`)
- Indexes: Prisma `@@index([...])` annotations
**API Routes:**
- Collections: **plural** (`/api/notes`, `/api/labels`)
- Items: **singular** (`/api/notes/[id]`)
- Namespace: `/api/ai/*` for AI features
**Components:**
- Component names: **PascalCase** (`NoteCard`, `AiSuggestion`)
- File names: **kebab-case** (`note-card.tsx`, `ai-suggestion.tsx`)
**Functions:**
- Functions: **camelCase** (`getNotes`, `createNote`, `togglePin`)
- Verbs first: `get`, `create`, `update`, `delete`, `toggle`
- Handlers: prefix `handle` (`handleDelete`, `handleTogglePin`)
**Variables:**
- Variables: **camelCase** (`userId`, `isPending`, `noteId`)
- Types/interfaces: **PascalCase** (`Note`, `NoteCardProps`)
---
### State Management
**NO GLOBAL STATE LIBRARIES:**
- ❌ No Redux, Zustand, or similar
-**React useState** for local component state
-**React Context** for shared state (User session, Theme, Labels)
-**React Cache** for server-side caching
-**useOptimistic** for immediate UI feedback
-**useTransition** for non-blocking updates
**Example:**
```typescript
'use client'
import { useState, useTransition, useOptimistic } from 'react'
export function NoteCard({ note }: NoteCardProps) {
const [isPending, startTransition] = useTransition()
const [optimisticNote, addOptimisticNote] = useOptimistic(
note,
(state, newProps: Partial<Note>) => ({ ...state, ...newProps })
)
const handleTogglePin = async () => {
startTransition(async () => {
addOptimisticNote({ isPinned: !note.isPinned })
await togglePin(note.id, !note.isPinned)
router.refresh()
})
}
}
```
---
### Error Handling
**Global Pattern:**
```typescript
// API Routes
try {
// ... code
} catch (error) {
console.error('Feature name error:', error)
return NextResponse.json(
{ success: false, error: 'Human-readable message' },
{ status: 500 }
)
}
// Server Actions
try {
// ... code
} catch (error) {
console.error('Feature name error:', error)
throw new Error('Failed to action')
}
```
**CRITICAL RULES:**
- ✅ Use `console.error()` for logging (not `console.log`)
- ✅ Human-readable error messages (no technical jargon)
-**NEVER** expose stack traces to users
-**NEVER** expose internal error details
---
### Import Rules
**ALWAYS use @/ alias:**
```typescript
// ✅ GOOD
import { Button } from '@/components/ui/button'
import { Note } from '@/lib/types'
import { deleteNote } from '@/app/actions/notes'
// ❌ BAD - relative paths
import { Button } from '../../../components/ui/button'
import { Note } from '../lib/types'
```
**Import from Radix UI:**
```typescript
// ✅ GOOD - use @/components/ui/* wrapper
import { Dialog } from '@/components/ui/dialog'
import { Button } from '@/components/ui/button'
// ❌ BAD - direct Radix imports
import { Dialog } from '@radix-ui/react-dialog'
import { Button } from '@radix-ui/react-slot'
```
---
### AI Service Pattern
**All AI services follow this structure:**
```typescript
// keep-notes/lib/ai/services/title-suggestion.service.ts
import { getAIProvider } from '@/lib/ai/factory'
export class TitleSuggestionService {
private provider = getAIProvider()
async generateSuggestions(content: string): Promise<string[]> {
try {
const response = await this.provider.generateText({
prompt: `Generate 3 titles for: ${content}`,
maxTokens: 100
})
return response.titles
} catch (error) {
console.error('TitleSuggestionService error:', error)
throw new Error('Failed to generate suggestions')
}
}
}
```
**CRITICAL RULES:**
- ✅ Use `getAIProvider()` factory (not direct OpenAI/Ollama imports)
- ✅ Services are **stateless classes**
- ✅ Constructor injection of dependencies
- ✅ Methods return `Promise<T>` with error handling
- ✅ No direct database access (via Prisma)
---
### Testing Rules
**Playwright E2E Tests:**
```typescript
// tests/e2e/ai-features.spec.ts
import { test, expect } from '@playwright/test'
test('AI title suggestions appear', async ({ page }) => {
await page.goto('/')
await page.fill('[data-testid="note-content"]', 'Test content')
// Wait for AI suggestions
await expect(page.locator('[data-testid="ai-suggestions"]')).toBeVisible()
})
```
**CRITICAL RULES:**
- ✅ Use `data-testid` attributes for test selectors
- ✅ Test critical user flows (not edge cases)
- ✅ Use `await expect(...).toBeVisible()` for assertions
- ✅ Tests in `tests/e2e/` directory
---
### Brownfield Integration Rules
**ZERO BREAKING CHANGES:**
-**ALL new features must extend, not replace existing functionality**
- ✅ Existing components, API routes, and database tables MUST continue working
- ✅ New database fields: **optional** (nullable) for backward compatibility
- ✅ New features: **additive** only (don't remove existing features)
**Example:**
```prisma
// ✅ GOOD - optional new field
model Note {
// ... existing fields
language String? // NEW: optional
aiConfidence Int? // NEW: optional
}
// ❌ BAD - breaking change
model Note {
language String @default("en") // BREAKS: non-optional default
}
```
---
### Phase 1 Specific Rules
**AI Features to Implement:**
1. **Title Suggestions** - 3 suggestions after 50+ words
2. **Semantic Search** - Hybrid keyword + vector search with RRF
3. **Paragraph Reformulation** - Clarify, Shorten, Improve Style options
4. **Memory Echo** - Daily proactive note connections (background job)
5. **AI Settings** - Granular ON/OFF controls per feature
6. **Language Detection** - TinyLD hybrid (< 50 words: library, ≥ 50 words: AI)
**Performance Targets:**
- ✅ Title suggestions: < 2s after detection
- ✅ Semantic search: < 300ms for 1000 notes
- ✅ Memory Echo: < 100ms UI freeze (background processing)
- ✅ Language detection: ~8ms (TinyLD) or ~200-500ms (AI)
**Language Support:**
- ✅ System prompts: **English** (stability)
- ✅ User data: **Local language** (FR, EN, ES, DE, FA/Persian + 57 others)
- ✅ TinyLD supports 62 languages including Persian (verified)
---
### Security Rules
**API Keys:**
-**NEVER** expose API keys to client (server-side only)
- ✅ Store in environment variables (`OPENAI_API_KEY`, `OLLAMA_ENDPOINT`)
- ✅ Use SystemConfig table for provider selection
**Authentication:**
-**ALL** server actions check `auth()` session first
-**ALL** API routes require valid NextAuth session
- ✅ Public routes: `/api/auth/*`, login/register pages only
**Privacy:**
- ✅ Ollama path = 100% local (no external API calls)
- ✅ OpenAI path = cloud (verify in DevTools Network tab)
- ✅ User data never logged or exposed
---
### File Organization
**AI Services:**
```
lib/ai/services/
├── title-suggestion.service.ts
├── semantic-search.service.ts
├── paragraph-refactor.service.ts
├── memory-echo.service.ts
├── language-detection.service.ts
└── embedding.service.ts
```
**AI Components:**
```
components/ai/
├── ai-suggestion.tsx
├── ai-settings-panel.tsx
├── memory-echo-notification.tsx
├── confidence-badge.tsx
├── feedback-buttons.tsx
└── paragraph-refactor.tsx
```
**API Routes:**
```
app/api/ai/
├── titles/route.ts
├── search/route.ts
├── refactor/route.ts
├── echo/route.ts
├── feedback/route.ts
└── language/route.ts
```
---
### Development Workflow
**Before implementing ANY feature:**
1. ✅ Read `_bmad-output/planning-artifacts/architecture.md`
2. ✅ Check `project-context.md` for specific rules
3. ✅ Follow naming patterns (camelCase, PascalCase, kebab-case)
4. ✅ Use response format `{success, data, error}`
5. ✅ Add `'use server'` or `'use client'` directives
6. ✅ Import from `@/` alias only
**Quality Checklist:**
- [ ] TypeScript strict mode compliance
- [ ] Zod validation for API routes
- [ ] auth() check in server actions
- [ ] revalidatePath('/') after mutations
- [ ] Error handling with console.error()
- [ ] Response format {success, data/error}
- [ ] Import from @/ alias
- [ ] Component directives ('use client'/'use server')
- [ ] Zero breaking changes
- [ ] Performance targets met
---
## Quick Reference Card
**For AI Agents implementing features:**
| Category | Rule | Example |
|----------|------|---------|
| TypeScript | Strict mode, no `any` | `interface Props { note: Note }` |
| Components | 'use client' at top | `export function Comp() { ... }` |
| Server Actions | 'use server' + auth() + revalidate | `const session = await auth()` |
| API Routes | Zod + {success, data/error} | `return NextResponse.json({ success: true, data })` |
| Database | Prisma only, no raw SQL | `await prisma.note.findMany()` |
| Naming | camelCase vars, PascalCase types | `const userId: string` |
| Imports | @/ alias only | `import { Note } from '@/lib/types'` |
| Error Handling | console.error + human message | `throw new Error('Failed to...')` |
| AI Services | getAIProvider() factory | `const provider = getAIProvider()` |
| Performance | Target < 2s for AI features | `await withTimeout(promise, 2000)` |
---
*Generated: 2026-01-10*
*Project: Keep (Memento Phase 1 MVP AI)*
*Architecture: Based on architecture.md (2784 lines)*
*Status: Ready for AI Agent Implementation*
---
## Usage Guidelines
**For AI Agents:**
- ✅ Read this file **before** implementing any code
- ✅ Follow **ALL** rules exactly as documented
- ✅ When in doubt, prefer the more restrictive option
- ✅ Check `_bmad-output/planning-artifacts/architecture.md` for full context
- ✅ Use response format `{success, data, error}` for API routes
- ✅ Add `'use server'` or `'use client'` directives at top of files
- ✅ Import from `@/` alias only (not relative paths)
- ✅ Validate requests with Zod schemas
- ✅ Check `auth()` session in server actions
- ✅ Call `revalidatePath('/')` after mutations
- ✅ Log errors with `console.error()`
**For Humans:**
- Keep this file **lean and focused** on agent needs
- Update when **technology stack changes**
- Review **quarterly** for outdated rules
- Remove rules that become **obvious over time**
- Add new patterns when they emerge in development
**Maintenance:**
1. **Technology Changes:** Update when adding/removing dependencies
2. **Pattern Evolution:** Add new patterns as they emerge
3. **Bug Prevention:** Add rules when agents make common mistakes
4. **Optimization:** Remove redundant or obvious rules periodically
5. **Review Cycle:** Check quarterly for outdated information
---
**Last Updated:** 2026-01-10
**Optimization Status:** ✅ Optimized for LLM context (50 critical rules, 490 lines)
**Readiness:** ✅ Ready for AI Agent Implementation
---
*Workflow completed: 2026-01-10*
*Generator: Winston (Architect Agent) with Generate Project Context workflow*
*Based on: architecture.md (2784 lines) + existing codebase analysis*

View File

@@ -1,15 +1,16 @@
---
stepsCompleted: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
stepsCompleted: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
inputDocuments:
- _bmad-output/planning-artifacts/prd-phase1-mvp-ai.md
- docs/component-inventory.md
- docs/project-overview.md
workflowType: 'ux-design'
lastStep: 11
lastStep: 14
documentTitle: 'UX Design Specification - Phase 1 MVP AI'
focusArea: 'AI-Powered Note Taking Features'
status: 'in-progress'
status: 'complete'
createdAt: '2026-01-10'
completedAt: '2026-01-10'
---
# UX Design Specification - Phase 1 MVP AI
@@ -2632,3 +2633,945 @@ Build 7 custom components on top of Radix primitives:
- **MemoryEchoCard:** Defining "Aha!" experience but most complex (background processing, embeddings, feedback learning)
---
## UX Consistency Patterns
### Button Hierarchy
**When to Use:**
- **Primary Buttons:** Main actions (Save note, Apply suggestion, Create Cahier)
- **Secondary Buttons:** Alternative actions (Cancel, Keep original)
- **Tertiary Buttons:** Low-emphasis actions (Dismiss, Skip, Later)
- **AI Buttons:** AI-specific actions (✨ Reformulate, View Connection)
**Visual Design:**
| Button Type | Tailwind Classes | Usage | Example |
|-------------|------------------|-------|---------|
| Primary | `bg-purple-600 hover:bg-purple-700 text-white font-medium py-2 px-4 rounded-lg` | Main action, high emphasis | "Save Note", "Apply Suggestion" |
| Secondary | `bg-gray-100 hover:bg-gray-200 text-gray-700 font-medium py-2 px-4 rounded-lg` | Alternative action | "Cancel", "Keep Original" |
| Tertiary | `text-gray-500 hover:text-gray-700 font-medium py-2 px-2` | Low emphasis, text-only | "Dismiss", "Skip" |
| AI Action | `bg-purple-50 hover:bg-purple-100 text-purple-600 font-medium py-2 px-4 rounded-lg border border-purple-200` | AI-specific action | "✨ Reformulate", "View Connection" |
| Success | `bg-green-600 hover:bg-green-700 text-white font-medium py-2 px-4 rounded-lg` | Positive confirmation | "Link Notes", "Accept" |
| Destructive | `bg-red-600 hover:bg-red-700 text-white font-medium py-2 px-4 rounded-lg` | Destructive action | "Delete Note", "Remove" |
**Behavior:**
- **Focus:** 2px purple outline (`focus-visible:ring-2 ring-purple-600`)
- **Active:** Slightly darker shade (`active:scale-95`)
- **Disabled:** `opacity-50 cursor-not-allowed` with `aria-disabled="true"`
- **Loading:** Show spinner, disable button, preserve width
**Accessibility:**
- Minimum touch target: 44×44px (WCAG 2.5.5)
- Clear visual labels (no icon-only buttons without labels)
- Keyboard: Enter/Space to activate
- Focus indicators always visible
**Mobile Considerations:**
- Full-width buttons on mobile for primary actions
- Minimum 44px height for touch targets
- Adequate spacing between buttons (8px minimum)
---
### Feedback Patterns
**When to Use:**
- **Success Feedback:** Actions completed successfully (Note saved, Suggestion applied)
- **Error Feedback:** Actions failed (AI unavailable, Connection error)
- **Warning Feedback:** Caution needed (Last Cahier, AI limit reached)
- **Info Feedback:** Neutral information (AI processing, Feature discovery)
**Visual Design:**
**Success Feedback:**
```tsx
// Toast notification
<div className="bg-green-50 border-l-4 border-green-600 text-green-800 p-4 rounded-lg shadow-md">
<div className="flex items-center gap-3">
<span className="text-green-600"></span>
<p className="font-medium">Note saved successfully</p>
</div>
</div>
```
**Error Feedback:**
```tsx
// Toast notification
<div className="bg-red-50 border-l-4 border-red-600 text-red-800 p-4 rounded-lg shadow-md">
<div className="flex items-center gap-3">
<span className="text-red-600"></span>
<p className="font-medium">AI service unavailable. Please try again.</p>
</div>
<button className="mt-2 text-sm underline">Retry</button>
</div>
```
**Warning Feedback:**
```tsx
// Modal or banner
<div className="bg-amber-50 border-l-4 border-amber-600 text-amber-800 p-4 rounded-lg">
<div className="flex items-center gap-3">
<span className="text-amber-600"></span>
<p className="font-medium">You've reached your daily AI limit</p>
</div>
<p className="text-sm mt-1">Upgrade to Pro for unlimited AI features.</p>
</div>
```
**Info Feedback (AI Processing):**
```tsx
// Inline indicator
<div className="flex items-center gap-2 text-gray-600">
<div className="animate-spin w-4 h-4 border-2 border-amber-500 border-t-transparent rounded-full" />
<span className="text-sm">AI thinking...</span>
</div>
```
**Behavior:**
- **Auto-dismiss:** Success/info toasts auto-dismiss after 5s
- **Persistent:** Error/warning toasts require manual dismissal
- **Position:** Toasts bottom-right (desktop), bottom-center (mobile)
- **Stacking:** Multiple toasts stack vertically with 4px gap
**Accessibility:**
- `role="alert"` for errors/warnings
- `role="status"` for success/info
- `aria-live="polite"` for non-critical, `aria-live="assertive"` for critical
- Screen reader announcements with clear messages
---
### Form & Input Patterns
**When to Use:**
- Note editor (main content input)
- Title input (with AI suggestions)
- Cahier name input
- Search bar (unified semantic search)
- Settings forms (AI configuration)
**Visual Design:**
**Text Input (Title, Cahier Name):**
```tsx
<input
type="text"
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-600 focus:border-transparent outline-none transition-all"
placeholder="Note title..."
/>
```
**Textarea (Note Content):**
```tsx
<textarea
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-600 focus:border-transparent outline-none transition-all min-h-[200px] resize-y"
placeholder="Start typing..."
/>
```
**Search Input (Unified):**
```tsx
<div className="relative">
<input
type="search"
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-600 focus:border-transparent outline-none transition-all"
placeholder="Search notes..."
/>
<span className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400">🔍</span>
</div>
```
**Behavior:**
- **Focus:** Purple ring (`focus:ring-2 focus:ring-purple-600`)
- **Error state:** Red border + error message below
- **Success state:** Green border (briefly, then normal)
- **AI Suggestions:** Show dropdown below input (TitleSuggestionsDropdown)
- **Debounce:** Search input debounces 300ms before triggering
**Validation Patterns:**
| Field | Validation | Error Message |
|-------|-----------|---------------|
| Cahier Name | Required, min 2 chars | "Cahier name must be at least 2 characters" |
| Title | Optional (AI suggests if empty) | - |
| Search | Min 2 chars to trigger | "Enter at least 2 characters to search" |
**Accessibility:**
- `aria-label` or `aria-labelledby` for all inputs
- `aria-describedby` for help text/error messages
- `aria-invalid="true"` for validation errors
- Keyboard navigation: Tab to focus, Enter to submit
---
### Navigation Patterns
**When to Use:**
- Cahier switching (sidebar navigation)
- Breadcrumb navigation (header)
- Tab navigation (settings sections)
- Pagination (masonry grid infinite scroll)
**Visual Design:**
**Cahier Sidebar Navigation:**
```tsx
<nav className="w-64 bg-white border-r border-gray-200">
<ul className="py-4">
<li>
<button className="w-full flex items-center gap-3 px-4 py-2 text-left bg-purple-50 text-purple-600 border-l-4 border-purple-600 font-medium">
📓 Inbox
</button>
</li>
<li>
<button className="w-full flex items-center gap-3 px-4 py-2 text-left text-gray-700 hover:bg-gray-50">
📓 Work
</button>
</li>
<li>
<button className="w-full flex items-center gap-3 px-4 py-2 text-left text-gray-700 hover:bg-gray-50">
📓 Personal
</button>
</li>
</ul>
</nav>
```
**Active State:**
- Background: `bg-purple-50`
- Text: `text-purple-600`
- Left border: `border-l-4 border-purple-600`
- Font weight: `font-medium`
**Hover State (Inactive):**
- Background: `hover:bg-gray-50`
- Text: `text-gray-700`
- No border
**Breadcrumb Navigation (Header):**
```tsx
<nav className="flex items-center gap-2 text-sm text-gray-600">
<span className="hover:text-purple-600 cursor-pointer">Memento</span>
<span>/</span>
<span className="hover:text-purple-600 cursor-pointer">Work</span>
<span>/</span>
<span className="text-gray-900 font-medium">React Performance Tips</span>
</nav>
```
**Behavior:**
- **Instant switch:** Cahier switching happens < 100ms (no page reload)
- **Active indicator:** Current Cahier highlighted with purple left border
- **Keyboard navigation:** ↑↓ to navigate Cahiers, Enter to select
- **Mobile:** Hamburger menu (sidebar collapses to off-canvas)
**Accessibility:**
- `role="navigation"` with `aria-label="Cahiers navigation"`
- `aria-current="page"` for active Cahier
- Keyboard focus visible (2px purple outline)
- Screen reader announces Cahier names
---
### Modal & Overlay Patterns
**When to Use:**
- Reformulation modal (compare original vs AI suggestion)
- Memory Echo details modal (view 2-note connection)
- Settings modals (AI configuration)
- Confirmation dialogs (delete Cahier)
**Visual Design:**
**Modal Container:**
```tsx
<div className="fixed inset-0 z-50 flex items-center justify-center">
{/* Backdrop */}
<div className="absolute inset-0 bg-black/50 backdrop-blur-sm" />
{/* Modal */}
<div className="relative bg-white rounded-xl shadow-2xl max-w-4xl w-full mx-4 border-2 border-purple-600">
{/* Header */}
<div className="flex items-center justify-between p-6 border-b border-gray-200">
<h2 className="text-2xl font-semibold text-gray-900"> Reformulate this Paragraph</h2>
<button className="text-gray-400 hover:text-gray-600"></button>
</div>
{/* Body */}
<div className="p-6">
{/* Modal content */}
</div>
{/* Footer */}
<div className="flex justify-end gap-3 p-6 border-t border-gray-200">
<button>Keep Original</button>
<button>Apply Suggestion</button>
</div>
</div>
</div>
```
**Behavior:**
- **Opening:** Fade-in backdrop + scale modal (0.2s ease-out)
- **Closing:** Fade-out backdrop + scale down (0.1s)
- **Focus trap:** Tab stays within modal when open
- **ESC to close:** Pressing ESC closes modal
- **Click outside:** Clicking backdrop closes modal (optional for confirmation dialogs)
**Accessibility:**
- `role="dialog"` with `aria-modal="true"`
- `aria-labelledby` points to modal title
- Focus trap (first focusable element receives focus on open)
- Returns focus to trigger element on close
**Mobile Considerations:**
- Full-width modals on mobile (mx-0, max-h-screen)
- Bottom sheet style for some modals (slide-up from bottom)
- Touch-friendly button sizes (min 44px)
---
### Search Patterns
**When to Use:**
- Unified semantic search (header search bar)
- Cahier-specific search (filtered by current Cahier)
- AI-powered semantic matching
**Visual Design:**
**Unified Search Bar:**
```tsx
<div className="relative">
<input
type="search"
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-600 focus:border-transparent outline-none transition-all"
placeholder="Search notes..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
/>
<span className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400">🔍</span>
{/* Loading indicator */}
{isSearching && (
<div className="absolute right-3 top-1/2 -translate-y-1/2">
<div className="animate-spin w-4 h-4 border-2 border-purple-600 border-t-transparent rounded-full" />
</div>
)}
</div>
```
**Search Results with Semantic Badges:**
```tsx
<div className="space-y-4">
{/* Keyword match result */}
<div className="p-4 bg-white border border-gray-200 rounded-lg hover:shadow-md transition-shadow">
<h3 className="font-semibold text-gray-900">React State Management</h3>
</div>
{/* Semantic match result */}
<div className="p-4 bg-white border border-gray-200 rounded-lg hover:shadow-md transition-shadow">
<h3 className="font-semibold text-gray-900">Next.js Optimization</h3>
<div className="mt-2">
<span className="inline-flex items-center gap-1 px-2 py-1 bg-blue-50 text-blue-600 text-xs font-medium rounded">
🎯 Semantic Match (Score: 0.82)
</span>
</div>
</div>
</div>
```
**Behavior:**
- **Debounce:** 300ms debounce before triggering search
- **Hybrid search:** Simultaneously runs keyword + semantic search
- **Badge indication:** Semantic matches show blue badge with score
- **Real-time:** Results update as user types (after debounce)
- **No visible toggle:** Users don't choose keyword vs semantic - "it just works"
**Accessibility:**
- `aria-label="Search notes"`
- Live region for results: `aria-live="polite"` on results container
- Clear announcements: "5 results found, 3 semantic matches"
- Keyboard: Enter to navigate to first result
---
### Loading & Empty States
**When to Use:**
- AI processing (generating embeddings, reformulating)
- Empty Cahiers (no notes yet)
- No search results
- Loading initial data
**Visual Design:**
**AI Processing State:**
```tsx
<div className="flex flex-col items-center justify-center py-12">
<div className="animate-spin w-12 h-12 border-4 border-amber-500 border-t-transparent rounded-full mb-4" />
<p className="text-gray-600 font-medium">AI thinking...</p>
<p className="text-sm text-gray-500 mt-1">Generating embeddings for semantic search</p>
</div>
```
**Empty Cahier State:**
```tsx
<div className="flex flex-col items-center justify-center py-16 text-center">
<div className="text-6xl mb-4">📓</div>
<h3 className="text-xl font-semibold text-gray-900 mb-2">No notes in this Cahier yet</h3>
<p className="text-gray-600 mb-6">Capture your first idea to get started</p>
<button className="bg-purple-600 hover:bg-purple-700 text-white font-medium py-2 px-6 rounded-lg">
Create Note
</button>
</div>
```
**No Search Results:**
```tsx
<div className="flex flex-col items-center justify-center py-12 text-center">
<div className="text-4xl mb-4">🔍</div>
<h3 className="text-lg font-semibold text-gray-900 mb-2">No results found</h3>
<p className="text-gray-600">Try different keywords or check your spelling</p>
</div>
```
**Skeleton Loading (Masonry Grid):**
```tsx
<div className="grid grid-cols-3 gap-4">
{[1, 2, 3, 4, 5, 6].map((i) => (
<div key={i} className="h-48 bg-gray-200 rounded-lg animate-pulse" />
))}
</div>
```
**Behavior:**
- **AI Processing:** Show spinner + descriptive text (not just "Loading...")
- **Empty States:** Provide clear CTA (Create Note, Browse Other Cahiers)
- **Skeleton:** Pulse animation while loading actual content
- **Progressive enhancement:** Show content as it loads (not all-or-nothing)
**Accessibility:**
- `role="status"` with `aria-live="polite"` for loading states
- `aria-busy="true"` when content is loading
- Screen readers announce what's happening and why
- Empty states have clear headings and explanations
---
### Mobile-Specific Patterns
**When to Use:**
- Responsive navigation (collapsible sidebar)
- Touch interactions (swipe, long-press)
- Mobile-optimized modals (bottom sheets)
- Mobile search (expandable search bar)
**Visual Design:**
**Collapsible Sidebar (Mobile):**
```tsx
{/* Desktop: Always visible sidebar */}
{/* Mobile: Hamburger menu */}
<div className="md:hidden fixed top-4 left-4 z-50">
<button className="p-2 bg-white rounded-lg shadow-md">
</button>
</div>
{/* Off-canvas sidebar on mobile */}
<div className={`fixed inset-y-0 left-0 z-50 w-64 bg-white transform transition-transform ${isOpen ? 'translate-x-0' : '-translate-x-full'} md:translate-x-0`}>
{/* Sidebar content */}
</div>
```
**Bottom Sheet Modal (Mobile):**
```tsx
<div className="md:hidden fixed inset-x-0 bottom-0 bg-white rounded-t-2xl shadow-2xl transform transition-transform">
<div className="p-6">
{/* Modal content */}
</div>
</div>
```
**Expandable Search (Mobile):**
```tsx
<div className="relative">
<button className="p-2">
🔍
</button>
{/* Expands to full-width input on focus/click */}
<input
type="search"
className="fixed inset-x-4 top-16 z-40 px-4 py-3 bg-white border border-gray-300 rounded-lg shadow-lg"
placeholder="Search notes..."
/>
</div>
```
**Touch Interactions:**
- **Minimum touch target:** 44×44px (WCAG 2.5.5)
- **Swipe to dismiss:** Toast notifications, bottom sheets
- **Long-press:** Context menus (show actions on note card)
- **Pull-to-refresh:** Refresh masonry grid content
---
### Pattern Integration with Radix UI
**Consistency with Design System:**
| Pattern Category | Radix Primitive | Custom Styling | Notes |
|------------------|-----------------|----------------|-------|
| Modals | Dialog | Purple border, AI-specific padding | BaseModal extends Dialog |
| Dropdowns | Dropdown Menu | Purple text on hover | Used for TitleSuggestions |
| Toasts | Toast | Purple border for AI | AIToast extends Toast |
| Navigation | Navigation Menu | Active state = purple left border | Cahiers sidebar |
| Forms | - | Custom inputs with purple focus ring | No Radix form primitive |
**Design Token Integration:**
```css
/* All patterns use consistent design tokens */
--primary: #8B5CF6; /* Primary actions, focus rings */
--ai-accent: #3B82F6; /* Semantic search badges */
--ai-connection: #8B5CF6; /* Memory Echo borders */
--success: #10B981; /* Success feedback */
--warning: #F59E0B; /* Processing states */
--error: #EF4444; /* Error feedback */
```
**Custom Pattern Rules:**
1. **AI-specific patterns always use purple/blue accent colors**
2. **All buttons have 2px purple focus ring** (keyboard navigation)
3. **All modals have purple border** (2px solid #8B5CF6)
4. **All toasts auto-dismiss after 5s** except errors (manual dismiss)
5. **All inputs use purple focus ring** (not default blue)
6. **All empty states provide clear CTA** (not just "No results")
7. **All loading states show descriptive text** (not just spinners)
---
## Responsive Design & Accessibility
### Responsive Strategy
**Desktop Strategy (1024px+):**
- **Layout optimisé:** Sidebar Cahiers (256px) + Masonry grid (3-4 colonnes)
- **Espace maximal:** Profiter de l'écran pour afficher plus de notes
- **Features desktop:**
- Cahier sidebar toujours visible (pas de hamburger)
- Masonry grid 4 colonnes (plus de notes visibles)
- Memory Echo toast en bas à droite
- Hover interactions (✨ Reformulate apparaît au survol)
- Drag & drop pour réorganiser les Cahiers (bonus, pas MVP)
**Tablet Strategy (768px - 1023px):**
- **Layout adapté:** Sidebar Cahiers réduit (200px) ou collapsible
- **Masonry 2 colonnes:** Grid passe de 4 → 2 colonnes
- **Touch optimization:**
- Cahier sidebar devient collapsible (hamburger menu)
- Boutons plus larges (min 44px)
- Pas de hover-based interactions (✨ Reformulate bouton permanent)
- **Information density:** Moyenne - équilibre entre lisibilité et contenu
**Mobile Strategy (< 768px):**
- **Layout simplifié:** Single-column stack
- **Navigation:** Hamburger menu (sidebar off-canvas)
- **Masonry 1 colonne:** Single column, full-width cards
- **Features mobiles:**
- Cahier sidebar: Off-canvas (glisse de gauche)
- Search: Expandable (icône → input full-width)
- Memory Echo: Full-width toast en bas
- AI Badge: Compact (✨ icône seule, tap pour menu)
- Bottom sheet modals (au lieu de dialogues centrés)
- **Critical info only:** Masquer les éléments non-essentiels
---
### Breakpoint Strategy
**Using Tailwind CSS Standard Breakpoints:**
```javascript
// tailwind.config.js
module.exports = {
theme: {
screens: {
'sm': '640px', // Mobile large (landscape)
'md': '768px', // Tablet portrait
'lg': '1024px', // Desktop, laptop
'xl': '1280px', // Desktop large
'2xl': '1536px', // Extra large desktop
}
}
}
```
**Layout Adaptations by Breakpoint:**
| Element | Mobile (< 768px) | Tablet (768-1024px) | Desktop (> 1024px) |
|---------|------------------|---------------------|---------------------|
| Cahier Sidebar | Off-canvas (hamburger) | 200px or collapsible | 256px always visible |
| Masonry Grid | 1 colonne (100%) | 2 colonnes (48% each) | 3-4 colonnes (32-24% each) |
| Header | Compact (64px) | Standard (64px) | Standard (64px) |
| Search Bar | Expandable on tap | Full-width (standard) | Full-width (standard) |
| Memory Echo | Bottom-center toast | Bottom-right toast | Bottom-right toast |
| Modals | Bottom sheet | Standard dialog | Standard dialog |
**Mobile-First Approach:**
- **Développement:** Commencer par le layout mobile, ajouter des médias queries pour écrans plus larges
- **Avantages:** Performance native mobile, progressive enhancement
- **Implementation:**
```css
/* Mobile default: 1 colonne */
.masonry-grid { display: grid; grid-template-columns: 1fr; }
/* Tablet: 2 colonnes */
@media (min-width: 768px) {
.masonry-grid { grid-template-columns: repeat(2, 1fr); }
}
/* Desktop: 3-4 colonnes */
@media (min-width: 1024px) {
.masonry-grid { grid-template-columns: repeat(3, 1fr); }
}
@media (min-width: 1280px) {
.masonry-grid { grid-template-columns: repeat(4, 1fr); }
}
```
---
### Accessibility Strategy
**WCAG Compliance Level: AA (Recommended)**
**Rationale:**
- ✅ **Industry standard:** Niveau attendu pour les applications web modernes
- ✅ **Legal compliance:** Conforme aux exigences légales (ADA, European Accessibility Act)
- ✅ **User experience:** Balance optimal entre accessibilité et design
- ✅ **Feasible:** Atteignable sans compromis majeurs sur le design
**Key Accessibility Requirements:**
**1. Color Contrast (WCAG 2.1 Level AA):**
- Normal text (16px+): Minimum 4.5:1 contrast ratio
- Large text (18px+): Minimum 3:1 contrast ratio
- UI components (buttons, borders): Minimum 3:1 contrast ratio
**Implementation:**
- Purple primary (#8B5CF6) on white: 4.8:1 ✅
- Blue accent (#3B82F6) on white: 4.5:1 ✅
- Green success (#10B981) on white: 3.9:1 ✅ (OK for large text)
- Gray text (#6B7280) on white: 4.6:1 ✅
**2. Keyboard Navigation:**
- **Full keyboard support:** Tab, Shift+Tab, Enter, Escape, Arrow keys
- **Focus indicators:** 2px purple outline (focus-visible:ring-2 ring-purple-600)
- **Skip links:** "Skip to main content" link au début de la page
- **Focus order:** Logique et prévisible (header → sidebar → main content → footer)
- **No keyboard traps:** ESC ferme tous les modals/toasts
**3. Screen Reader Support:**
- **Semantic HTML:** heading hierarchy (h1 → h2 → h3), proper list structures
- **ARIA labels:** Labels descriptifs pour tous les composants interactifs
- **Live regions:** `aria-live="polite"` pour toasts et mises à jour dynamiques
- **Screen reader testing:** NVDA (Windows), VoiceOver (macOS), TalkBack (Android)
**4. Touch Target Sizes (WCAG 2.5.5):**
- Minimum 44×44px pour tous les éléments interactifs
- Espacement minimum 8px entre les éléments tactiles
- Tests sur appareils mobiles réels (iOS, Android)
**5. Focus Management:**
- **Focus trap:** Dans les modals (Tab ne quitte pas le modal)
- **Focus restoration:** Retour au trigger element après fermeture modal
- **Visible focus:** Toujours visible (pas de outline: none sauf :focus-visible)
**6. Accessibility Features for AI Components:**
- **AIToast:** `role="alert"` + `aria-live="polite"`
- **TitleSuggestions:** `role="listbox"` + `aria-label="AI-generated title suggestions"`
- **MemoryEchoCard:** `aria-label="AI notification: Note connection discovered"`
- **ProcessingIndicator:** `role="status"` + `aria-busy="true"`
---
### Testing Strategy
**Responsive Testing:**
**Device Testing:**
- **Real devices:**
- iPhone 12/13/14 (375px width)
- Samsung Galaxy S21 (360px width)
- iPad (768px - 1024px)
- Desktop (1920px width)
- **Browser testing:**
- Chrome (primary)
- Safari (iOS, macOS)
- Firefox (secondary)
- Edge (Windows)
**Network Performance Testing:**
- Test sur 3G (slow network)
- Test sur WiFi (normal network)
- Optimiser images (WebP, lazy loading)
- Minifier CSS/JS pour performance mobile
**Accessibility Testing:**
**Automated Tools:**
- **axe DevTools** (Chrome extension) - Scan automatique
- **WAVE** (WebAIM) - Contrast checker, ARIA validation
- **Lighthouse** (Chrome) - Accessibility score
**Manual Testing:**
- **Keyboard navigation:** Navigation clavier complète
- **Screen reader:** NVDA, VoiceOver, TalkBack
- **Zoom:** Test 200% text zoom (pas de horizontal scroll)
- **High contrast mode:** Windows High Contrast Mode
**User Testing:**
- **Include users with disabilities:**
- Screen reader users
- Low vision users
- Motor disability users (keyboard-only)
- **Test with real assistive technologies**
- **Gather feedback on AI features accessibility**
---
### Implementation Guidelines
**Responsive Development:**
**1. Use Relative Units:**
```css
/* ✅ GOOD - Relative units */
font-size: 1rem; /* 16px base */
padding: 1rem; /* 16px */
margin: 0.5rem 0; /* 8px top/bottom, 0 sides */
width: 100%; /* Full width */
max-width: 1200px; /* Max constraint */
/* ❌ BAD - Fixed pixels */
font-size: 16px; /* Not scalable */
padding: 16px;
width: 375px; /* Mobile fixed width */
```
**2. Mobile-First Media Queries:**
```css
/* Mobile default */
.masonry-grid {
display: grid;
grid-template-columns: 1fr;
gap: 1rem;
}
/* Tablet+ */
@media (min-width: 768px) {
.masonry-grid {
grid-template-columns: repeat(2, 1fr);
}
}
/* Desktop+ */
@media (min-width: 1024px) {
.masonry-grid {
grid-template-columns: repeat(3, 1fr);
}
}
```
**3. Touch Target Testing:**
```tsx
// ✅ GOOD - Minimum 44x44px
<button className="min-h-[44px] min-w-[44px] p-4">
Click me
</button>
// ❌ BAD - Too small
<button className="h-8 w-8 p-1">
Click me
</button>
```
**4. Image Optimization:**
```tsx
// Responsive images
<Image
src="/note-thumbnail.webp"
width={400}
height={300}
loading="lazy" // Lazy load below fold
alt="Note thumbnail"
/>
// Background images with fallback
<div
style={{
backgroundImage: 'url(/image.webp), url(/image.jpg)'
}}
/>
```
**Accessibility Development:**
**1. Semantic HTML:**
```tsx
// ✅ GOOD - Semantic
<header>
<nav aria-label="Cahiers navigation">
<ul>
<li><a href="/inbox" aria-current="page">Inbox</a></li>
</ul>
</nav>
</header>
<main>
<article>
<h1>Note Title</h1>
<p>Note content...</p>
</article>
</main>
// ❌ BAD - Non-semantic
<div class="header">
<div class="nav">
<div class="nav-item" onclick="navigate('/inbox')">Inbox</div>
</div>
</div>
```
**2. ARIA Labels and Roles:**
```tsx
// AIToast component
<div
role="alert"
aria-live="polite"
aria-label="AI notification: Note connection discovered"
>
<div className="flex items-center gap-3">
<span aria-hidden="true">💡</span>
<h3>I noticed something...</h3>
</div>
</div>
// TitleSuggestionsDropdown
<ul role="listbox" aria-label="AI-generated title suggestions">
<li role="option">✨ Title 1</li>
<li role="option">✨ Title 2</li>
<li role="option">✨ Title 3</li>
</ul>
```
**3. Keyboard Navigation:**
```tsx
// Focus trap in modal
const Modal = () => {
useEffect(() => {
// Trap focus within modal
const focusableElements = modalRef.current.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];
const handleTab = (e) => {
if (e.key === 'Tab') {
if (e.shiftKey) {
if (document.activeElement === firstElement) {
e.preventDefault();
lastElement.focus();
}
} else {
if (document.activeElement === lastElement) {
e.preventDefault();
firstElement.focus();
}
}
}
};
document.addEventListener('keydown', handleTab);
return () => document.removeEventListener('keydown', handleTab);
}, []);
// Modal JSX...
};
```
**4. Focus Management:**
```tsx
// Skip link (top of page)
<a
href="#main-content"
className="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 bg-purple-600 text-white px-4 py-2 rounded-lg"
>
Skip to main content
</a>
<main id="main-content" tabIndex={-1}>
{/* Main content */}
</main>
```
**5. High Contrast Mode Support:**
```css
/* Respect high contrast mode preference */
@media (prefers-contrast: high) {
.ai-button {
border: 2px solid currentColor;
background: transparent;
}
.note-card {
border: 2px solid currentColor;
}
}
```
---
### Component Library Resources
**Alternative UI Component Libraries (Built on Radix UI + Tailwind):**
These libraries are compatible with Memento's design system choice (Radix UI + Tailwind CSS):
1. **Aceternity UI** - https://ui.aceternity.com/components
- Modern components built with Radix UI + Tailwind
- Animated components, dark mode support
- Useful for: Advanced animations, bento grids, particles
2. **Origin UI** - https://www.originui-ng.com/
- Next.js components with Framer Motion animations
- shadcn/ui-based with enhanced styling
- Useful for: Animated cards, transitions, hero sections
3. **Magic UI** - https://magicui.design/docs/components
- Creative components with unique animations
- Built with Radix UI + Tailwind + Framer Motion
- Useful for: Special effects, interactive components
**Recommendation:**
- **Base:** Stick with Radix UI primitives (current choice)
- **Enhancement:** These libraries can provide inspiration or pre-built components for specific features
- **Customization:** All components can be customized to match Memento's design tokens (purple/blue AI colors)
**Integration Strategy:**
1. Start with Radix UI primitives (as planned)
2. Reference these libraries for component patterns and animation ideas
3. Customize any imported components to use Memento's design tokens
4. Maintain consistency with established UX patterns from Step 12
---