# 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=` | Ouvrir une session spécifique | | `/brainstorm?seed=` | Auto-créer un brainstorm avec ce seed | | `/brainstorm?sourceNoteId=` | Créer depuis une note | | `/brainstorm?invite=` | 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** : `` 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** : `` 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 `` | Ouvre `BrainstormShareDialog` | | **Activité** | Icône ``, 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é** | `` + 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** | `` + "Deepen", bordure dashed, hover orange → `expandIdea` | | **Bouton Créer Note** | `` + "Create Note", bordure dashed, hover émeraude → `convertIdea` | | **Bouton Non pertinent** | Texte muted, hover rose → `dismissIdea` | ### Sidebar sessions (bande droite, 64px) - Icône `` 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 `` - 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 `` orange + "Aucune session" + "Démarrer →" - **Loading** : 3 skeletons pulsants - **Liste** (max 10) : - **Sessions possédées** : Cercle orange + `` + seed idea + count idées + date + bouton supprimer (rose au hover) - **Sessions partagées** : Cercle bleu + `` + 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 `` 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 `` + 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 `` émeraude - `brainstorm_joined` : icône `` 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= → 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) |