feat: brainstorm sessions, PDF document Q&A, embedding fixes, and UI improvements
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 7s

- Add brainstorm feature with collaborative canvas, AI idea generation, live cursors, playback, and export
- Add PDF upload/extraction/ingestion pipeline with pgvector document search (RAG)
- Add document Q&A overlay with streaming chat and PDF preview
- Add note attachments UI with status polling, grid layout, and auto-scroll
- Add task extraction AI tool and agent executor improvements
- Fix NoteEmbedding missing updatedAt column, re-index 66 notes with 1536-dim embeddings
- Fix brainstorm 'Create Note' button: add success toast and redirect to created note
- Fix memory echo notification infinite polling
- Fix chat route to always include document_search tool
- Add brainstorm i18n keys across all 14 locales
- Add socket server for real-time brainstorm collaboration
- Add hierarchical notebook selector and organize notebook dialog improvements
- Add sidebar brainstorm section with session management
- Update prisma schema with brainstorm tables, attachments, and document chunks
This commit is contained in:
Antigravity
2026-05-14 17:43:21 +00:00
parent 195e845f0a
commit 1fcea6ed7d
228 changed files with 57656 additions and 1059 deletions

View File

@@ -0,0 +1,534 @@
# Brainstorm — Documentation Complète
## Vue d'ensemble
Le brainstorm est un outil de génération d'idées assistée par IA basé sur un graphe radial D3. L'utilisateur saisit une "graine" (seed idea), l'IA génère 9 idées en 3 vagues (Variations, Analogies, Disruptions), et l'utilisateur peut approfondir, dismiss, convertir en note, ou ajouter ses propres idées. Le tout en temps-réel via Socket.io.
---
## Architecture
```
[Browser] ←→ [Next.js API Routes] ←→ [PostgreSQL]
↕ ↕
[Socket.io Client] ←→ [Socket.io Server :3002]
```
- **Frontend** : React + D3.js + React Query + Socket.io Client
- **Backend** : Next.js App Router + Prisma + OpenAI
- **Temps réel** : Socket.io sur le port 3002
---
## Modèles de données (Prisma)
### BrainstormSession
| Champ | Type | Description |
|-------|------|-------------|
| id | String (cuid) | PK |
| seedIdea | String | L'idée graine saisie par l'utilisateur |
| sourceNoteId | String? | FK vers la note source (optionnel) |
| contextNoteIds | String? | JSON string des IDs des notes de contexte |
| exportedNoteId | String? | FK vers la note exportée |
| userId | String | FK vers le propriétaire |
| inviteToken | String? | Token d'invitation unique |
| inviteExpiry | DateTime? | Date d'expiration du token |
| status | String | "active" par défaut |
| Relations | | ideas[], participants[], activities[], shares[] |
### BrainstormIdea
| Champ | Type | Description |
|-------|------|-------------|
| id | String (cuid) | PK |
| sessionId | String | FK vers la session |
| waveNumber | Int | 1, 2 ou 3 |
| title | String | Titre de l'idée |
| description | String | Description détaillée |
| connectionToSeed | String? | Lien avec l'idée graine |
| noveltyScore | Int? | Score de nouveauté (0-100) |
| parentIdeaId | String? | FK vers l'idée parent (auto-référence) |
| convertedToNoteId | String? | FK vers la note créée par conversion |
| relatedNoteIds | String? | JSON string des IDs des notes liées |
| status | String | "active", "dismissed", "converted" |
| positionX | Float? | Position X sur le canvas |
| positionY | Float? | Position Y sur le canvas |
| createdBy | String? | FK vers l'utilisateur créateur |
| createdByType | String | "ai" ou "human" |
| Relations | | session, parentIdea?, children[], noteRefs[], creator? |
### BrainstormNoteRef
| Champ | Type | Description |
|-------|------|-------------|
| id | String (cuid) | PK |
| ideaId | String | FK vers l'idée |
| noteId | String? | FK vers la note (peut être null) |
| relation | String | "derived_from", "opposes", "extends", "synthesizes", "transposes", "none_found" |
| explanation | String | Pourquoi cette note est liée |
| verdict | String | "unresolved", "accepted", "dismissed" |
### BrainstormParticipant
| Champ | Type | Description |
|-------|------|-------------|
| sessionId + userId | Unique | Un user = un rôle par session |
| role | String | "host", "editor", "viewer" |
| joinedAt, lastSeenAt | DateTime | Présence |
### BrainstormActivity
| Champ | Type | Description |
|-------|------|-------------|
| action | String | "wave_generated", "manual_idea", "idea_dismissed", "idea_converted", "joined", "invite_created" |
| details | String? | JSON stringifié |
### BrainstormShare
| Champ | Type | Description |
|-------|------|-------------|
| sessionId + userId | Unique | Un partage par couple session/user |
| sharedBy | String | FK vers l'envoyeur |
| status | String | "pending", "accepted", "declined", "removed" |
| permission | String | "editor", "viewer" |
---
## Pages et URLs
| URL | Description |
|-----|-------------|
| `/brainstorm` | Page principale du brainstorm |
| `/brainstorm?session=<id>` | Ouvrir une session spécifique |
| `/brainstorm?seed=<text>` | Auto-créer un brainstorm avec ce seed |
| `/brainstorm?sourceNoteId=<id>` | Créer depuis une note |
| `/brainstorm?invite=<token>` | Rejoindre via token d'invitation |
---
## API Routes
### `POST /api/brainstorm` — Créer un brainstorm
**Input** : `{ seedIdea, sourceNoteId?, contextNoteIds?, locale? }`
**Processus** :
1. Si pas de `contextNoteIds` → embedding du seed → recherche des 8 notes les plus proches
2. LLM classe chaque note : SUPPORT, TENSION, ou EXTENSION
3. LLM génère 9 idées en 3 vagues :
- **Wave 1 (Variations)** : 3 idées, dont au moins 1 de SUPPORT, 1 répondant à TENSION
- **Wave 2 (Analogies)** : 3 idées, dont au moins 1 d'EXTENSION, 1 transposition de pattern
- **Wave 3 (Disruptions)** : 3 idées, dont au moins 1 inversion de SUPPORT, 1 synthèse de contradictions
4. Chaque idée a : wave, title, description, connectionToSeed, noveltyScore, noteRefs[]
5. Positionnement radial : angle = `(idx % 3) * (2π/3) + (wave-1) * 0.5`, radius = `wave * 150`
6. Crée session + host participant + log activity + 9 idées avec noteRefs
**Retour** : Session complète + `{ support, tension, extension }` counts
### `GET /api/brainstorm` — Lister les sessions de l'utilisateur
**Retour** : `BrainstormSessionListItem[]` (id, seedIdea, totalIdeas, activeIdeas, dates)
### `GET /api/brainstorm/shared` — Sessions partagées acceptées
**Processus** : Fetch shares where userId + accepted, upsert participant si manquant
**Retour** : Même format que la liste + `_isShared: true`
### `GET /api/brainstorm/[sessionId]` — Détail d'une session
**Accès** : Propriétaire OU participant OU share accepted
**Retour** : Session complète avec ideas (ordonnées par wave/date), noteRefs, creator, sourceNote, exportedNote
### `DELETE /api/brainstorm/[sessionId]` — Supprimer une session
**Accès** : Propriétaire uniquement
### `POST /api/brainstorm/[sessionId]/expand` — Approfondir une idée
**Accès** : Propriétaire uniquement
**Input** : `{ ideaId, locale? }`
**Processus** :
1. Charge les noteRefs de l'idée parent + 5 notes via embedding
2. LLM génère 9 sous-idées en 3 vagues relatives au parent
3. Positionnement radial autour du parent
4. Crée les idées avec `parentIdeaId = source idea`
**Retour** : Session mise à jour
### `POST /api/brainstorm/[sessionId]/manual-idea` — Ajouter une idée manuelle
**Accès** : Participant avec rôle editor
**Input** : `{ title, description?, parentIdeaId?, locale? }`
**Processus** :
1. Wave = `min(parent.wave + 1, 3)` si parent, sinon 1
2. Crée l'idée avec `createdBy = userId`, `createdByType = 'human'`
3. Auto-link : embedding → 3 notes les plus proches → BrainstormNoteRef avec relation "extends"
4. **Enrichissement IA** (non-bloquant) : LLM polit le titre, enrichit la description, set noveltyScore. Respecte la locale (`IMPORTANT: You MUST write ALL text in ${lang}`)
**Retour** : Session mise à jour (201)
### `POST /api/brainstorm/[sessionId]/dismiss` — Rejeter une idée
**Accès** : Participant avec rôle editor
**Input** : `{ ideaId }`
**Processus** : Transaction → status = "dismissed" + tous noteRefs verdict = "dismissed"
### `POST /api/brainstorm/[sessionId]/convert` — Convertir en note
**Accès** : Propriétaire uniquement
**Input** : `{ ideaId }`
**Processus** :
1. Crée une Note avec :
- Titre = titre de l'idée
- Contenu = markdown formaté (description, connection, novelty, source brainstorm, noteRefs avec relations)
- Labels = `['brainstorm', 'idée']`
- Même notebook que la note source
2. Transaction : status = "converted", convertedToNoteId = note.id, noteRefs verdict = "accepted", tag les notes référencées avec "brainstorm-fruitful"
**Retour** : Note créée (201)
### `POST /api/brainstorm/[sessionId]/finalize` — Finaliser la session
**Accès** : Propriétaire uniquement
**Processus** :
- Pour chaque note référencée : si tous les refs sont dismissed → tag "brainstorm-dry"
- Compte notesEnriched (≥1 accepted) et notesMarkedDry (all dismissed)
**Retour** : `{ notesSolicited, notesEnriched, notesMarkedDry }`
### `POST /api/brainstorm/[sessionId]/export` — Exporter en note
**Accès** : Propriétaire uniquement
**Processus** :
- Génère un markdown complet : header, summary, sections par wave, notes sollicitées, notes converties
- Crée une Note avec labels `['brainstorm', 'export']`
- Update session.exportedNoteId
**Retour** : Note créée (201)
### `POST /api/brainstorm/[sessionId]/update-position` — Sauvegarder la position d'un nœud
**Input** : `{ ideaId, positionX, positionY }`
### `POST /api/brainstorm/[sessionId]/invite` — Créer une invitation
**Accès** : Host uniquement
**Input** : `{ role, expiresInHours?, email? }`
- Si `email` fourni : trouve l'utilisateur, crée une Notification, retourne `{ mode: 'email' }`
- Sinon : génère un token, retourne `{ mode: 'link', inviteUrl }`
### `POST /api/brainstorm/join` — Rejoindre via token
**Input** : `{ token }`
**Processus** : Vérifie token + expiry → crée BrainstormParticipant → notifie le owner
### `GET /api/brainstorm/[sessionId]/activity` — Feed d'activité
**Retour** : 50 dernières activités avec user info
---
## Server Actions (`app/actions/brainstorm.ts`)
### `createBrainstormShare(sessionId, recipientEmail, permission?)`
Partage par email (même pattern que NoteShare).
1. Vérifie que le user est propriétaire
2. Cherche le recipient par email
3. Gère les cas existants :
- `accepted` → retourne `{ message: 'already_shared' }` (succès info)
- `pending` → retourne `{ message: 'already_pending' }` (succès info)
- `declined/removed` → ré-invite (remet à pending)
4. Sinon → crée un `BrainstormShare` avec status pending
### `respondToBrainstormShare(shareId, action)`
Accepter ou refuser un partage.
- Accept → upsert `BrainstormParticipant` (idempotent)
- Decline → update status
### `getPendingBrainstormShares()`
Retourne les shares pending pour l'utilisateur courant (avec session + sharer info).
### `getAcceptedBrainstormShares()`
Retourne les shares accepted (pour afficher dans la sidebar).
### `removeBrainstormShare(sessionId)`
Passe le share en status "removed".
---
## Interface utilisateur — Page Brainstorm
### Layout global
```
┌──────────────────────────────────────────────────────────────┐
│ HEADER (fixed, border-bottom, backdrop-blur) │
│ [Wind icon] Waves of Thought │
│ "Unfold dimensions of potentiality" │
│ [__________ input seed idea __________] [+] │
│ • • • AI is harvesting seeds of thought... │
├──────────────────────────────────┬─────────────┬────┐ │
│ │ │ │ │
│ CANVAS (D3) │ DETAIL │ S │ │
│ │ PANEL │ I │ │
│ ○ ring 1 │ (400px) │ D │ │
│ ○ ring 2 │ │ E │ │
│ ○ ring 3 │ │ B │ │
│ │ │ A │ │
│ [toolbar flottant en bas] │ │ R │ │
│ │ │ │ │
├──────────────────────────────────┴─────────────┴────┘ │
│ [Activity Feed panel] (slide depuis la droite) │
│ [BrainstormShareDialog] (modal) │
│ [Impact Toast] (fixed bottom center) │
└──────────────────────────────────────────────────────────────┘
```
### Header
- **Icône** : `<Wind>` dans un carré orange qui tourne pendant la génération
- **Titre** : "Waves of Thought" (serif)
- **Sous-titre** : "Unfold dimensions of potentiality" (muted)
- **Input seed** : Grand champ serif italic avec glow gradient au focus
- **Bouton submit** : `<Plus>` icône, positionné à droite dans l'input, disabled si vide ou en génération
- **Indicateur de génération** : 3 points orange pulsants + texte "AI is harvesting seeds of thought..."
### Canvas D3 (WaveCanvas)
#### Éléments visuels
- **Fond** : `#F8F7F2` avec grille de points (20px)
- **3 anneaux** : rayons 200, 400, 600 — traits gris pointillés, opacité 0.5
- **Nœud racine** (seed) :
- Cercle noir (#141414), rayon 40
- Texte "SEED" en blanc, 10px bold
- Label de l'idée graine en serif italic 18px, positionné à y=80
- **Nœuds idée** :
- Cercle blanc avec bordure colorée par wave (orange/blue/violet)
- Rayon 28 (18 si dismissed)
- Texte : titre tronqué à 18 caractères, positionné sous le cercle
- Badges :
- **Converti** : fond vert + checkmark ✓
- **Notes liées** : emoji 📎 en haut-gauche
- **Créé par humain** : cercle bleu avec initiale en haut-droite
- **Créé par IA** : symbole ✦ violet en haut-droite
- **Dismissed** : opacité 0.3
- **Liens** :
- Wave (racine → idée) : gris pointillé, 1.5px
- Parent (idée → sous-idée) : jaune solid, 2px
#### Forces D3
- `forceLink` : distance = wave × 200 (wave links), 180 (parent links)
- `forceManyBody` : strength -800 (répulsion)
- `forceRadial` : rayon = wave × 200, strength 0.8
- `forceCollide` : radius + 30
#### Interactions
| Action | Effet |
|--------|-------|
| **Clic sur nœud** | Sélectionne l'idée → ouvre le panneau détail |
| **Double-clic sur nœud** | Ouvre l'éditeur inline pour créer un enfant |
| **Double-clic sur canvas** | Ouvre l'éditeur inline pour créer une idée racine |
| **Drag d'un nœud** | Déplace le nœud, fixe la position, émet `idea:moved` au socket |
| **Zoom/Pan** | d3.zoom, échelle [0.1, 5], centré avec scale 0.8 |
#### Éditeur inline (double-clic)
Apparaît sous le point de clic, carte flottante (260px) :
- En-tête : icône "+" bleue + label "Réponse" ou "Nouvelle idée"
- Input serif avec fond subtil
- Raccourcis : `↵ enregistrer` · `esc annuler`
- Si enfant : label "→ enfant"
- Triangle pointeur sous la carte
- **Enter** → appelle `onCreateIdea({ title, parentIdeaId, x, y })`
- **Escape** → ferme
#### Stabilité du canvas
- Le useEffect D3 ne se redéclenche QUE quand `sessionId` ou `ideasKey` (string sérialisée) changent
- Tous les callbacks (onNodeSelect, onPositionUpdate, onCreateIdea) sont passés via refs (`onNodeSelectRef.current`) pour éviter les re-renders
- Les remote moves (socket) mettent à jour directement les nœuds D3 sans re-render React
### Toolbar flottante (bas du canvas)
Pilule animée qui apparaît quand une session est active :
| Élément | Style | Action |
|---------|-------|--------|
| **Wave 1/2/3** | 3 points colorés (orange/blue/violet) | Légende visuelle |
| **Avatars** | Cercles colorés empilés (max 4, "+N") | Présence des utilisateurs connectés |
| **Exporter** | Texte orange | Appelle `exportBrainstorm` puis `finalizeBrainstorm` |
| **Inviter** | Texte émeraude, icône `<UserPlus>` | Ouvre `BrainstormShareDialog` |
| **Activité** | Icône `<Activity>`, muted/orange si actif | Toggle le panneau Activity Feed |
| **Supprimer** | Icône poubelle, muted → rose au hover | Supprime la session |
### Panneau détail (droite, 400px, slide animé)
Apparaît quand une idée est sélectionnée :
| Section | Contenu |
|---------|---------|
| **Badge wave** | Pilule colorée (Wave 1 orange / Wave 2 blue / Wave 3 violet) |
| **Note créée** | Badge vert si convertie |
| **Fermer** | Bouton chevron droite |
| **Titre** | 3xl serif bold |
| **Score de nouveauté** | `<Zap>` + nombre |
| **Créateur** | Humain = cercle bleu + initiale, IA = spark violet + "IA" |
| **Description** | Texte de l'idée |
| **Connexion au seed** | Carte slate, citation italic |
| **Origine de l'idée** | Cartes noteRef avec badge de relation (emerald=positive, rose=opposes, amber=autre), bouton "View" |
| **Bouton Approfondir** | `<Wind>` + "Deepen", bordure dashed, hover orange → `expandIdea` |
| **Bouton Créer Note** | `<FileText>` + "Create Note", bordure dashed, hover émeraude → `convertIdea` |
| **Bouton Non pertinent** | Texte muted, hover rose → `dismissIdea` |
### Sidebar sessions (bande droite, 64px)
- Icône `<History>` en haut
- Liste scrollable de boutons circulaires (40px) avec la première lettre du seed
- **Actif** : fond sombre, texte blanc, scale 110, shadow
- **Partagé** : fond orange clair, texte orange
- **Normal** : fond blanc, texte muted
- Filet décoratif en bas
### Activity Feed (panneau coulissant, 320px)
Slide depuis la droite avec animation spring :
- En-tête : icône + "Activity" + bouton fermer
- État vide : "No activity yet" italic
- Items : icône par type d'action, username bold, label d'action, titre idée (tronqué 30 chars), temps relatif (1m/1h/1d)
- Types : `manual_idea` (ampoule bleue), `wave_generated` (éclair orange), `joined` (user+ vert), `idea_dismissed` (X rose), `invite_created` (user+ violet)
### BrainstormShareDialog (modal)
- Trigger : bouton "Inviter" de la toolbar
- Header : icône orange + "Partager le brainstorm" + seed idea tronqué
- Champ email avec label "Adresse email"
- Bouton "Partager" orange avec `<UserPlus>`
- Messages de feedback :
- Succès (vert + ✓) : "Invitation envoyée!" / "Invitation renvoyée!"
- Info (ambre + ⚠) : "Cette personne a déjà accès" / "Invitation déjà en attente"
- Erreur (rose + ⚠) : "No account found with this email" etc.
- Note : "La personne recevra une notification pour accepter ou refuser."
### Impact Toast (fixed bottom center)
Apparaît après Export + Finalize :
- "X note(s) enriched / X note(s) marked dry"
- Disparaît après 4 secondes
---
## Sidebar gauche (app principale)
### Section Brainstorms (`SidebarBrainstorms`)
- **Vue vide** : Icône `<Sparkles>` orange + "Aucune session" + "Démarrer →"
- **Loading** : 3 skeletons pulsants
- **Liste** (max 10) :
- **Sessions possédées** : Cercle orange + `<Sparkles>` + seed idea + count idées + date + bouton supprimer (rose au hover)
- **Sessions partagées** : Cercle bleu + `<Sparkles>` + seed idea + badge "partagé" + count idées + date (pas de bouton supprimer)
- Clic → navigate vers `/brainstorm?session={id}`
### Navigation
- Bouton "brainstorms" dans le toggle de vues (icône `<Sparkles>` orange, highlight orange quand actif)
---
## Notifications (cloche)
### Partages brainstorm pending
- Section dédiée entre les reminders et les share requests de notes
- Avatar orange gradient avec initiale de l'envoyeur
- Icône `<Wind>` + label "BRAINSTORM" orange
- Nom de l'envoyeur + "invited you to a brainstorm" + aperçu du seed (tronqué 35 chars)
- Boutons Accepter (orange) / Refuser
### Notifications système
- `brainstorm_invite` : icône `<Wind>` émeraude
- `brainstorm_joined` : icône `<Wind>` bleue
---
## Temps réel (Socket.io)
### Événements client → serveur
| Event | Data | Quand |
|-------|------|-------|
| `cursor:move` | `{ x, y }` ou `null` | Mouvement souris sur le canvas |
| `idea:moved` | `{ ideaId, positionX, positionY, userId }` | Fin de drag d'un nœud |
| `activity:new` | `{ action, userId, userName, details }` | Action utilisateur |
### Événements serveur → client
| Event | Data | Effet |
|-------|------|-------|
| `presence:update` | `PresenceUser[]` | Mise à jour de la liste des utilisateurs connectés |
| `cursor:update` | `{ userId, cursor: {x,y} }` | Déplacement du curseur d'un autre user |
| `activity:new` | `ActivityEvent` | Nouvelle activité dans le feed |
| `idea:moved` | `{ ideaId, positionX, positionY }` | Déplacement d'un nœud par un autre user |
| `idea:added` | — | Placeholder (non utilisé côté client) |
| `idea:dismissed` | — | Placeholder (non utilisé côté client) |
### Flux de déplacement en temps réel
1. User A drag un nœud → fin de drag → `handlePositionUpdate` → émet `idea:moved` + POST API (persist)
2. Socket server → broadcast `idea:moved` aux autres dans la room
3. User B → `useBrainstormSocket` callback → `setRemoteMove(...)` (avec compteur séquentiel)
4. WaveCanvas → `useEffect([remoteMove])` → fixe fx/fy du nœud cible → 30 ticks de simulation → update positions liens/nœuds directement dans D3
### Curseurs live
- Chaque user a un curseur coloré (SVG arrow + nom) affiché sur le canvas des autres
- 12 couleurs assignées en round-robin
- `useCursorTracking` attache un listener mousemove au container et émet `cursor:move`
### Ghost Cursor IA
- Quand une vague IA est en génération, un curseur violet animé apparaît
- Se déplace aléatoirement dans un rayon de 150-350px du centre
- Pulsation violette + badge "AI ✦"
---
## Flux de partage
### Par email (server action — principal)
```
Propriétaire → Dialog email → createBrainstormShare()
→ Cherche recipient par email
→ Crée BrainstormShare (pending)
→ Destinataire voit dans NotificationPanel (cloche)
→ Accept → Upsert BrainstormParticipant
→ Decline → Update status
→ Apparaît dans sidebar (bleu, badge "partagé")
```
### Par lien (API invite — secondaire)
```
Propriétaire → invite route → génère token → copie URL
→ Destinataire ouvre URL avec ?invite=<token>
→ join route → vérifie token + expiry
→ Crée BrainstormParticipant
→ Notifie le owner
```
---
## Hooks React Query
| Hook | Type | Query Key | API |
|------|------|-----------|-----|
| `useBrainstormSessions()` | Query | `['brainstorm', 'sessions']` | GET /api/brainstorm |
| `useSharedBrainstormSessions()` | Query | `['brainstorm', 'shared-sessions']` | GET /api/brainstorm/shared |
| `useBrainstormSession(id)` | Query | `['brainstorm', 'session', id]` | GET /api/brainstorm/{id} |
| `useCreateBrainstorm()` | Mutation | — | POST /api/brainstorm |
| `useExpandIdea(id)` | Mutation | — | POST /api/brainstorm/{id}/expand |
| `useDismissIdea(id)` | Mutation | — | POST /api/brainstorm/{id}/dismiss |
| `useConvertIdea(id)` | Mutation | — | POST /api/brainstorm/{id}/convert |
| `useExportBrainstorm(id)` | Mutation | — | POST /api/brainstorm/{id}/export |
| `useFinalizeBrainstorm(id)` | Mutation | — | POST /api/brainstorm/{id}/finalize |
| `useDeleteBrainstorm()` | Mutation | — | DELETE /api/brainstorm/{id} |
| `useAddManualIdea(id)` | Mutation | — | POST /api/brainstorm/{id}/manual-idea |
| `useBrainstormActivity(id)` | Query | — | GET /api/brainstorm/{id}/activity (refetch 10s) |
---
## Fichiers sources
| Fichier | Rôle |
|---------|------|
| `components/brainstorm/brainstorm-page.tsx` | Page principale complète |
| `components/brainstorm/wave-canvas.tsx` | Canvas D3 (noeuds, liens, interactions) |
| `components/brainstorm/brainstorm-share-dialog.tsx` | Modal de partage par email |
| `components/brainstorm/activity-feed.tsx` | Panneau d'activité |
| `components/brainstorm/ghost-cursor.tsx` | Curseur IA animé |
| `components/brainstorm/live-cursors.tsx` | Curseurs des autres users + avatars |
| `hooks/use-brainstorm.ts` | Tous les hooks React Query |
| `hooks/use-brainstorm-socket.ts` | Hook Socket.io |
| `app/actions/brainstorm.ts` | Server actions (partage) |
| `app/api/brainstorm/route.ts` | POST create + GET list |
| `app/api/brainstorm/[sessionId]/route.ts` | GET/DELETE session |
| `app/api/brainstorm/[sessionId]/expand/route.ts` | POST approfondir |
| `app/api/brainstorm/[sessionId]/manual-idea/route.ts` | POST idée manuelle |
| `app/api/brainstorm/[sessionId]/dismiss/route.ts` | POST rejeter |
| `app/api/brainstorm/[sessionId]/convert/route.ts` | POST convertir en note |
| `app/api/brainstorm/[sessionId]/finalize/route.ts` | POST finaliser |
| `app/api/brainstorm/[sessionId]/export/route.ts` | POST exporter |
| `app/api/brainstorm/[sessionId]/update-position/route.ts` | POST position |
| `app/api/brainstorm/[sessionId]/invite/route.ts` | POST invitation par lien |
| `app/api/brainstorm/[sessionId]/activity/route.ts` | GET activités |
| `app/api/brainstorm/join/route.ts` | POST rejoindre par token |
| `app/api/brainstorm/shared/route.ts` | GET sessions partagées |
| `socket-server.ts` | Serveur Socket.io |
| `types/brainstorm.ts` | Types TypeScript |
| `lib/brainstorm-collab.ts` | verifyParticipant + logActivity |
| `prisma/schema.prisma` | Modèles DB (lignes 454-572) |
| `components/notification-panel.tsx` | Notifications partage brainstorm |
| `components/sidebar.tsx` | SidebarBrainstorms (lignes 85-169) |
---
## Composants obsolètes (existent mais non utilisés)
| Fichier | Note |
|---------|------|
| `components/brainstorm/manual-idea-dialog.tsx` | Remplacé par l'éditeur inline du canvas |
| `components/brainstorm/invite-dialog.tsx` | Remplacé par BrainstormShareDialog |
| `components/brainstorm/brainstorm-create-dialog.tsx` | Remplacé par l'input inline du header |
| `components/brainstorm/brainstorm-canvas.tsx` | Ancienne implémentation avec react-force-graph-2d, remplacé par WaveCanvas (D3 direct) |

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff