Files
Momento/docs/user-stories.md
Antigravity 0784c94242
Some checks failed
CI / Lint, Test & Build (push) Failing after 57s
CI / Deploy production (on server) (push) Has been skipped
feat(notes): vues structurées tableau/kanban, flashcards et MCP robuste
Ajoute la base organisable par carnet (schéma, champs partagés, valeurs par note)
avec activation guidée, tableau éditable, kanban et suppression de colonnes.
Corrige le multiselect en vue tableau et enrichit sidebar, grille et i18n FR/EN.
Inclut aussi les améliorations flashcards SM-2, l'audit consentement IA et la
robustesse du serveur MCP (config, validation, rate-limit, métriques).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-24 23:03:16 +00:00

28 KiB
Raw Blame History

User Stories — Momento Next Phase

Basé sur l'analyse du prototype architectural-grid/ et du code production memento-note/.
Dernière mise à jour : 2026-05-24


Tableau de bord

ID Feature Statut Fichiers livrés
US-SIDEBAR Sidebar deux colonnes (rail d'icônes + panneau) LIVRÉ sidebar.tsx restructuré
US-SEARCH Recherche Globale Dual-Panel + Ctrl+K LIVRÉ search-modal.tsx, search-modal-context.tsx, bug openNote corrigé
US-LIVING-BLOCKS Blocs Vivants (Transclusion Bidirectionnelle) LIVRÉ tiptap-unique-id-extension.ts, tiptap-live-block-extension.tsx, block-picker.tsx, app/api/blocks/*, migration LiveBlockRef
US-MEMORY-ECHO Résonance Sémantique + Embed depuis Echo LIVRÉ memory-echo-section.tsx, /api/notes/[id]/live-block-refs, /api/blocks/resolve
US-INFO-RÉSEAU Panneau Info + Réseau Local LIVRÉ note-network-tab.tsx, sync-note-links.ts, migration NoteLink, picker [[
US-CLIPPER Web Clipper LIVRÉ extension/, /api/clip/*, migration sourceUrl, badge panneau Info
US-GRAPH Graphe de Connaissance Global enrichi LIVRÉ note-graph-view.tsx — filtres liens, seuil sémantique, focus voisinage, couleurs carnets, double-clic ouverture
US-INSIGHTS Clusters Sémantiques + Bridge Notes 🚧 EN COURS clusters en base mais page masquait les résultats périmés — correction affichage
US-TEMPORAL Prédictions d'accès temporelles À faire
US-FLASHCARDS Révision IA — Répétition espacée SM-2 LIVRÉ /revision, /api/flashcards/*, SM-2, génération IA depuis l'éditeur
US-STRUCTURED-VIEWS Vues Structurées (Tableau/Kanban/Galerie) LIVRÉ /api/notebooks/[id]/schema, /api/notes/[id]/properties, vues structurées + panneau propriétés éditeur

Ordre d'implémentation (dépendances)

US-LIVING-BLOCKS  ←  dépend de : TipTap UniqueID (fondation)
US-MEMORY-ECHO    ←  dépend de : pgvector existant (aucune migration)
US-INFO-RÉSEAU    ←  dépend de : wikilinks + backlinks existants
US-CLIPPER        ←  dépend de : migration Note.sourceUrl
US-GRAPH          ←  dépend de : /api/graph existant
US-INSIGHTS       ←  dépend de : Memory Echo + clusters
US-TEMPORAL       ←  dépend de : migration NoteAccessLog
US-FLASHCARDS     ←  dépend de : migration FlashcardDeck + Flashcard
US-STRUCTURED-VIEWS ← dépend de : migration NotebookSchema + NotebookProperty

Migrations Prisma requises (ordre d'application)

  1. Note.sourceUrl String? → US-CLIPPER
  2. LiveBlockRef → US-LIVING-BLOCKS
  3. NotebookSchema + NotebookProperty + NoteProperty → US-STRUCTURED-VIEWS
  4. FlashcardDeck + Flashcard + FlashcardReview → US-FLASHCARDS
  5. NoteAccessLog → US-TEMPORAL


US-SIDEBAR — Sidebar Deux Colonnes (LIVRÉ)

Contexte : Le prototype architectural-grid/Sidebar.tsx introduit un layout deux colonnes inspiré de SiYuan : une rail d'icônes étroit (54px) à gauche + un panneau de contenu dynamique à droite. L'ancienne sidebar avait des tabs horizontaux qui consommaient de l'espace vertical et manquaient de hiérarchie visuelle.

Livraison :

  • memento-note/components/sidebar.tsx — restructuré en flex-row
  • Colonne gauche (54px) : logo "M" avec dropdown profil, boutons nav verticaux avec indicateur actif, boutons utilitaires bas (notifications, corbeille, partagé, recherche Ctrl+K, thème, paramètres, déconnexion)
  • Colonne droite : panneau dynamique (carnets, agents, rappels, brainstorms, révisions)
  • NavigationView étendu à 'notebooks' | 'agents' | 'reminders' | 'brainstorms' | 'revision'
  • Tooltips au survol qui apparaissent à droite du rail (UX SiYuan)
  • Placeholder "Révisions" ajouté (en attente de US-FLASHCARDS)

Décision design : pas de toggle "ancien layout vs nouveau" dans les paramètres — design unique assumé.


US-SEARCH — Recherche Globale Redesignée (LIVRÉ)

Contexte : Le prototype SearchModal.tsx est une refonte complète avec dual-panel, regex, queries sauvegardées et preview contextuel. Il contenait un bug : le flag caseSensitive appliquait toujours 'gi'. La navigation vers la note utilisait le mauvais paramètre URL.

Livraison :

  • memento-note/components/search-modal.tsx — modal dual-panel (40% résultats + 60% préview)
  • memento-note/context/search-modal-context.tsx — contexte React + raccourci global Ctrl+K
  • memento-note/components/providers-wrapper.tsx — ajout du SearchModalProvider
  • Bouton loupe dans le rail d'icônes du sidebar

Bugs corrigés :

// Bug 1 - casse regex (prototype) :
// AVANT : const flags = caseSensitive ? 'gi' : 'gi'
// APRÈS : const flags = caseSensitive ? 'g' : 'gi'

// Bug 2 - navigation vers la note :
// AVANT : router.push(`/home?noteId=${noteId}`)
// APRÈS : router.push(`/home?openNote=${noteId}`)

US-CLIPPER — Web Clipper Intégré

Contexte : Le prototype contient ClipperSimulator.tsx (618 lignes) qui simule le clipping avec données mock. Il n'existe rien d'équivalent dans memento-note. La feature doit être réalisée en deux parties : une extension Chrome/Firefox et un modal de réception côté app.

En tant qu'utilisateur, je veux capturer n'importe quelle page web depuis mon navigateur et l'enregistrer dans Momento avec résumé IA, tags suggérés et choix du carnet — sans quitter le navigateur.

Critères d'acceptation :

Extension navigateur (memento-note/extension/ — nouveau répertoire)

  • Icône dans la barre d'outils du navigateur, clic ouvre un popup 380×520px
  • Popup affiche : titre de la page (éditable), domaine + favicon, mode de capture (Article complet / Sélection / Lien seul)
  • Bouton "Analyser avec IA" → appel POST /api/clip/analyze (URL + HTML content) → retourne { title, summary, tags[], readingTime }
  • Champ de sélection du carnet (dropdown hiérarchique, dernier carnet mémorisé)
  • Aperçu du contenu clipé (150px scrollable)
  • Bouton "Sauvegarder dans Momento" → appel POST /api/clip/save avec { url, title, content, summary, tags, notebookId }
  • Feedback visuel : spinner → "Sauvegardé ✓" avec lien direct vers la note

Route API app/api/clip/analyze/route.ts (nouvelle)

  • Reçoit { url, html }, nettoie le HTML via @mozilla/readability ou DOMPurify
  • Appelle le LLM Router existant (lib/ai/llm-router) pour générer { title, summary (3 phrases max), tags[] (5 max), readingTime }
  • Retourne JSON structuré en < 3s

Route API app/api/clip/save/route.ts (nouvelle)

  • Auth via NextAuth session
  • Crée une Note de type richtext avec content = HTML nettoyé + bloc source en bas (<hr/><small>Extrait de [domaine] le [date]</small>)
  • Sauvegarde l'URL source dans Note.sourceUrl (nouveau champ Prisma optionnel String?)
  • Retourne { noteId, noteUrl } pour que l'extension redirige

Indicateurs visuels dans l'app

  • Notification memory-echo-notification.tsx déclenché avec type: 'clip' : "Article clipé : [titre]" + bouton "Ouvrir"
  • Badge Source Web visible dans note-document-info-panel.tsx onglet Infos (la logique displayNoteType doit inclure 'clip')

Adaptation du prototype

  • ClipperSimulator.tsx → remplacer les MOCK_ARTICLES et handlers locaux par les vrais appels API
  • Conserver l'animation d'apparition (framer-motion), la structure de sélection de carnet et le layout du popup
  • Supprimer uuidv4 côté client (l'ID vient du serveur)

Migration Prisma

model Note {
  sourceUrl  String?   // URL d'origine pour les clips web
}

US-GRAPH — Graphe de Connaissance Global

Contexte : memento-note a déjà note-graph-view.tsx (vue plein écran avec react-force-graph-2d) et network-graph.tsx (rendu D3 clusters). Le prototype GraphKnowledgeMap.tsx est plus avancé : filtres, recherche dans le graphe, liens sémantiques + wiki, navigation contextuelle. L'objectif est d'enrichir note-graph-view.tsx avec ces fonctionnalités supplémentaires.

En tant qu'utilisateur, je veux explorer visuellement toute ma base de connaissance sous forme de graphe interactif, avec la possibilité de filtrer par carnet, de chercher un nœud, d'afficher ou masquer les liens sémantiques, et de voir un aperçu d'une note en cliquant sur son nœud.

Critères d'acceptation :

Panneau de contrôle latéral (nouveau dans note-graph-view.tsx)

  • Toggle Liens wiki ON/OFF — masque/montre les arêtes type: 'wikilink'
  • Toggle Liens sémantiques ON/OFF avec slider Seuil de similarité (0.30 → 0.90, pas 0.05)
  • Multi-sélecteur de carnets (checkboxes colorées) — filtre les nœuds
  • Input de recherche → met en surbrillance les nœuds correspondants et zoom vers eux

Nœuds interactifs

  • Taille du nœud proportionnelle au degree (nombre de connexions)
  • Couleur du nœud = couleur du carnet (via notebook.color en BDD)
  • Hover → tooltip {titre} — {carnet} — {nb liens}
  • Clic → panneau latéral droit (slide-in 320px) avec : titre, extrait (200 chars), date, labels, bouton "Ouvrir la note"
  • Double-clic → navigation directe vers la note (router.push)

Performance

  • API GET /api/graph existante retourne déjà { nodes, edges, clusters } — l'utiliser directement
  • Pour les graphes > 500 nœuds : mode particle (canvas optimisé)
  • Cooldown de stabilisation = 3s, puis freeze physique automatique

Navigation contextuelle

  • Bouton "Explorer à partir de ce nœud" → re-centre avec voisins directs (1 hop) en avant, reste atténué
  • Bouton "Réinitialiser la vue" → recentre sur tous les nœuds

Adaptation du prototype

  • Extraire la logique de filtrage de GraphKnowledgeMap.tsx → intégrer dans note-graph-view.tsx
  • Les couleurs CARNET_COLOR_PALETTE hardcodées → remplacer par les couleurs réelles via useNotebooks()
  • Les données mock → remplacer par l'API GET /api/graph

US-LIVING-BLOCKS — Blocs Vivants (Transclusion Bidirectionnelle)

Contexte : Le prototype LivingBlock.tsx utilise un blockIndex (numéro de ligne) pour référencer un bloc — fragile si du contenu est inséré avant. BlockPicker.tsx utilise la similarité Jaccard — insuffisant. rich-text-editor.tsx n'a pas l'extension UniqueID de TipTap.

En tant qu'utilisateur, je veux insérer dans ma note un "bloc vivant" issu d'une autre note, qui reste synchronisé en temps réel avec la source, peut être édité des deux côtés, et se détache proprement si la source est supprimée.

1. IDs stables pour chaque bloc (fondation technique)

rich-text-editor.tsx — modification :

  • Ajouter @tiptap/extension-unique-id → chaque nœud de type paragraph, heading, bulletList item, taskItem reçoit un data-id UUID v4 stable
  • Persister ces IDs dans Note.content (les attributs data-id sont conservés dans le HTML)
  • Le backend PUT /api/notes/[id] doit sauvegarder le contenu tel quel (aucun strip des data-id)

2. Insertion d'un bloc vivant

Commande slash dans l'éditeur :

  • Taper /bloc ou /embed → ouvre le BlockPicker (modal overlay)
  • BlockPicker (adapter du prototype) :
    • Onglet Suggestions IA : appel GET /api/blocks/suggestions?noteId={id} → 10 blocs les plus proches (pgvector cosine similarity)
    • Onglet Recherche : input texte → GET /api/blocks/search?q={query}
    • Onglet Memory Echo : affiche les MemoryEchoInsight déjà calculés avec bouton "Insérer ce bloc"
    • Clic sur un bloc → insère un nœud TipTap custom LiveBlock dans l'éditeur

Extension TipTap LiveBlockExtension (nouveau fichier components/tiptap-live-block-extension.tsx) :

  • Type de nœud : liveBlock, attributs : { sourceNoteId, blockId, snapshotContent }
  • Rendu : fond blueprint/5, bordure gauche blueprint, badge "LIVE" en coin supérieur droit
  • Indicateurs : wsConnected (vert) / hors-ligne (ocre) / isDeleted (rose)
  • Bouton "Détacher" → convertit en paragraph ordinaire avec le contenu actuel
  • Bouton "Ouvrir la source" → router.push vers la note source

Route API POST /api/blocks/embed (nouvelle) :

  • Body { sourceNoteId, blockId, targetNoteId } → crée un enregistrement LiveBlockRef en BDD

3. Synchronisation temps réel (Redis Pub/Sub existant)

  • Modification d'un bloc dans la note source → publier block:update:{blockId} avec le nouveau HTML
  • La note cible écoute ce canal → met à jour le snapshotContent en temps réel
  • Suppression de la source → publier block:deleted:{blockId} → état isDeleted côté cible

Route API GET /api/blocks/[blockId]/status :

  • Retourne { exists: boolean, content: string, sourceNoteTitle: string }

Migration Prisma

model LiveBlockRef {
  id             String   @id @default(cuid())
  sourceNoteId   String
  blockId        String   // data-id TipTap du bloc source
  targetNoteId   String
  createdAt      DateTime @default(now())
  sourceNote     Note     @relation("SourceLiveBlocks", fields: [sourceNoteId], references: [id], onDelete: Cascade)
  targetNote     Note     @relation("TargetLiveBlocks", fields: [targetNoteId], references: [id], onDelete: Cascade)
  @@index([sourceNoteId, blockId])
  @@index([targetNoteId])
}

Bug corrigé vs prototype

blockIndex (numéro de ligne, fragile) → remplacé par blockId (UUID stable via TipTap UniqueID).


US-STRUCTURED-VIEWS — Vues Structurées (Base de Données de Notes)

Contexte : Aucun prototype n'existe pour cette feature. Inspiration : SiYuan Database Views et Notion. Permettre de transformer un carnet en "vue structurée" avec propriétés personnalisées et plusieurs modes d'affichage.

En tant qu'utilisateur, je veux visualiser un ensemble de notes sous forme de tableau, kanban ou galerie — avec des propriétés personnalisées filtrables et triables — comme une base de données légère intégrée dans mon carnet.

1. Activation et définition des propriétés

  • Dans le header d'un carnet, icône Table → active le mode vue structurée
  • Clic + Ajouter une propriété → modal : nom, type (Texte / Nombre / Date / Sélection / Sélection multiple / Case)
  • Les options de sélection sont définies à la création (ex. Statut: À faire / En cours / Terminé)
  • Max 15 propriétés par carnet
  • Stockées dans NotebookSchema / NotebookProperty

2. Saisie des valeurs par note

  • Sidebar de l'éditeur, section "Propriétés"
  • Saisie inline : date picker, input numérique, checkbox, dropdown
  • Stockées dans NoteProperty (une ligne par note + propriété)
  • Sauvegarde en debounce (500ms) via PATCH /api/notes/[id]/properties

3. Vue Tableau — notes-structured-table.tsx (nouveau)

  • En-têtes = propriétés + Titre + Date modif
  • Tri par colonne (clic en-tête) — ASC/DESC
  • Filtre par colonne : Contient, Est égal à, Est vide
  • Édition inline de la valeur directement dans la cellule
  • Clic sur le titre → ouvre l'éditeur

4. Vue Kanban — notes-kanban-view.tsx (nouveau)

  • Colonnes = valeurs d'une propriété Sélection (désignée "propriété de groupement")
  • Drag & Drop entre colonnes (via @dnd-kit/sortable) → met à jour la propriété
  • Bouton + Nouvelle note dans chaque colonne → crée une note avec la valeur pré-remplie
  • Grille 3-4 colonnes responsive
  • Carte : illustrationSvg (si dispo) ou couleur du carnet, titre, 2 propriétés
  • Survol → aperçu des propriétés complètes

6. Navigation entre vues

  • Toggle dans le header du carnet : Liste / Tableau / Kanban / Galerie
  • Vue sélectionnée persistée dans localStorage (par carnet)

Migration Prisma

model NotebookSchema {
  id          String               @id @default(cuid())
  notebookId  String               @unique
  notebook    Notebook             @relation(fields: [notebookId], references: [id], onDelete: Cascade)
  properties  NotebookProperty[]
  createdAt   DateTime             @default(now())
}

model NotebookProperty {
  id          String   @id @default(cuid())
  schemaId    String
  schema      NotebookSchema @relation(fields: [schemaId], references: [id], onDelete: Cascade)
  name        String
  type        String   // text | number | date | select | multiselect | checkbox
  options     String?  // JSON array des options pour select/multiselect
  position    Int
  noteValues  NoteProperty[]
}

model NoteProperty {
  id         String           @id @default(cuid())
  noteId     String
  propertyId String
  value      String?          // JSON selon le type
  note       Note             @relation(fields: [noteId], references: [id], onDelete: Cascade)
  property   NotebookProperty @relation(fields: [propertyId], references: [id], onDelete: Cascade)
  @@unique([noteId, propertyId])
}

US-FLASHCARDS — Révision IA par Répétition Espacée

Contexte : Le prototype RevisionView.tsx contient une implémentation complète avec flip cards, évaluation, sessions et decks. Les types Flashcard, FlashcardDeck, FlashcardEvaluation y sont définis. Rien n'existe dans memento-note. Adapter avec un vrai algorithme SM-2 et une vraie persistance BDD.

En tant qu'utilisateur, je veux générer automatiquement des flashcards à partir de mes notes grâce à l'IA, puis les réviser en session de répétition espacée (algorithme SM-2) — et voir ma progression dans le temps.

1. Génération de flashcards par l'IA

Menu action dans l'éditeur (icône GraduationCap) :

  • Bouton "Générer des flashcards" → POST /api/flashcards/generate
  • Body : { noteId, count: 5-20 (slider), style: 'qa' | 'cloze' | 'concept' }
  • Types :
    • qa : question/réponse directe
    • cloze : phrase à trous ("La capitale de la France est ___")
    • concept : terme/définition
  • Preview des flashcards générées avant confirmation (modal avec édition possible)
  • Confirmation → sauvegarde en BDD dans le deck correspondant à la note

2. Organisation en decks

  • Un FlashcardDeck créé automatiquement par carnet (notebookIddeckId 1:1)
  • Deck manuel possible ("Créer un deck thématique")
  • Vue /decks (ou onglet sidebar) : nom, nb cartes, nb à réviser aujourd'hui (badge rouge), dernière révision

3. Session de révision (adapter RevisionView.tsx)

  • Écran d'accueil du deck : Total: X / À réviser: Y / Maîtrisées: Z
  • Mode session : carte centrée avec flip 3D CSS
  • 4 boutons d'évaluation : Difficile (1) / Dur (2) / Bien (3) / Facile (4)
  • Fin de session : récapitulatif avec stats + graphe en barres (ChartExtension existante)

Algorithme SM-2 (côté serveur) :

easinessFactor = max(1.3, EF + 0.1 - (5 - grade) * (0.08 + (5 - grade) * 0.02))
nextInterval = previousInterval * easinessFactor
nextReview = now + nextInterval (en jours)
  • Route POST /api/flashcards/[id]/review met à jour nextReviewAt, interval, easinessFactor

4. Dashboard de progression

  • Heatmap de révision (vue calendrier) — composant revision-heatmap.tsx
  • Courbe de rétention : % de cartes maîtrisées par semaine
  • "Cartes difficiles" : les 5 cartes avec le easinessFactor le plus bas

Migration Prisma

model FlashcardDeck {
  id          String      @id @default(cuid())
  userId      String
  notebookId  String?
  name        String
  createdAt   DateTime    @default(now())
  flashcards  Flashcard[]
  user        User        @relation(fields: [userId], references: [id], onDelete: Cascade)
  notebook    Notebook?   @relation(fields: [notebookId], references: [id], onDelete: SetNull)
}

model Flashcard {
  id              String   @id @default(cuid())
  deckId          String
  noteId          String?
  front           String
  back            String
  type            String   @default("qa")
  interval        Int      @default(1)
  easinessFactor  Float    @default(2.5)
  nextReviewAt    DateTime @default(now())
  createdAt       DateTime @default(now())
  deck            FlashcardDeck @relation(fields: [deckId], references: [id], onDelete: Cascade)
  note            Note?    @relation(fields: [noteId], references: [id], onDelete: SetNull)
  reviews         FlashcardReview[]
}

model FlashcardReview {
  id          String   @id @default(cuid())
  cardId      String
  grade       Int      // 1-4
  reviewedAt  DateTime @default(now())
  card        Flashcard @relation(fields: [cardId], references: [id], onDelete: Cascade)
}

US-INFO-RÉSEAU — Panneau Info Document + Réseau Local

Contexte : note-document-info-panel.tsx (407L) existe en production avec onglets info et versions. Le prototype NotebookInfoSidebar.tsx ajoute un onglet Réseau (graphe orbit SVG) avec des explicitWikiLinks hardcodés — bug critique. Il faut ajouter l'onglet Réseau au composant existant en production, en branchant les vraies données.

En tant qu'utilisateur, je veux voir depuis le panneau contextuel d'une note : ses métadonnées enrichies, ses versions avec diff, et son réseau local de connexions — le tout dans un espace cohérent.

1. Onglet Infos (enrichissement)

  • Ajouter calcul lineCount = nombre de \n dans le contenu HTML nettoyé
  • Ajouter calcul equationCount = regex \$\$[\s\S]+?\$\$|\$[^$]+\$
  • Ajouter calcul imageCount = regex <img|!\[
  • Afficher ces métriques en grille 3×2 sous les métriques actuelles (mots/chars)
  • Champ ID avec bouton copie (déjà disponible via note.id)
  • Afficher "Source Web" si note.sourceUrl est défini (post US-CLIPPER)

2. Onglet Versions (amélioration)

  • Filtre par type Manuel / Auto (basé sur reason: 'manual' | 'auto-save')
  • Tri Date ↓/↑ et Taille ↓/↑
  • Input de recherche sur le titre de la version
  • Note : alert() et window.confirm() déjà remplacés dans le code production — ne pas régresser

3. Onglet Réseau (nouveau)

Graphe SVG orbit (280×280px) :

  • Nœud central = note courante (couleur blueprint)
  • Nœuds orbitaux intérieurs = liens sortants (NoteLink en BDD)
  • Nœuds orbitaux extérieurs = liens entrants (GET /api/notes/[id]/backlinks — déjà en prod)
  • Nœuds grisés = citations non liées (mentions [[NomNote]] sans NoteLink correspondant, détectées par regex côté client)
  • Hover → tooltip {titre} + extrait du contextSnippet
  • Clic → router.push(/notes/[id])
  • Drag subtil (SVG onMouseDown + onMouseMove) pour repositionner les nœuds
  • Si > 12 nœuds : afficher les 12 les plus connectés + badge +N autres

Corrections critiques (vs prototype)

Problème dans le prototype Correction en production
explicitWikiLinks hardcodés → API backlinks réelle + NoteLink BDD
alert() pour preview versions → déjà corrigé en prod (NoteHistoryModal)
window.confirm() pour restauration → déjà corrigé en prod (AlertDialog)
Dates relatives hardcodées → déjà corrigé en prod (date-fns)

US-SEARCH — Voir section livrée ci-dessus


US-MEMORY-ECHO — Panneau de Résonance Sémantique Intégré

Contexte : editor-connections-section.tsx (255L) existe en production et appelle GET /api/ai/echo/connections (service pgvector complet). Mais il manque : le bouton "Embedder ce passage" (lien vers Living Blocks) et le panneau Rétroliens (quelles notes embarquent la note courante comme Living Block).

En tant qu'utilisateur, je veux voir en bas de chaque note le passage le plus sémantiquement proche de mes autres notes — avec la possibilité de l'insérer comme bloc vivant — et savoir quelles notes intègrent un extrait de ma note.

1. Bloc Memory Echo inline (enrichir editor-connections-section.tsx)

Apparence (adapter prototype NotebooksView.tsx L13971436) :

  • Fond gradient from-indigo-500/3, bordure indigo-500/10
  • Badge XX% affinité sémantique en mono (score = Math.round(similarity * 100)%)
  • Icône Sparkles pulse, label MEMORY ECHO uppercase tracking-widest
  • Citation en italic serif, bordure gauche indigo-500/20, 150 chars max

Bouton "Voir la connexion" :

  • router.push(/notes/[sourceNoteId]) — déjà présent, conserver

Bouton "Embedder ce passage" (nouveau) :

  • Ouvre le BlockPicker (US-LIVING-BLOCKS) pré-rempli avec { sourceNoteId, blockId }
  • Fallback (si Living Blocks non encore implémenté) : copie le texte en bloc citation ordinaire

Source des données :

  • GET /api/ai/echo/connections?noteId={id}&limit=1déjà en prod
  • Si aucune connexion → section masquée

2. Section Rétroliens Living Block (nouvelle)

Logique :

  • GET /api/notes/[id]/live-block-refs (nouvelle route, utilise LiveBlockRef de US-LIVING-BLOCKS)
  • Retourne les notes qui contiennent un liveBlock pointant sur la note courante

Affichage :

  • Titre RÉTROLIENS & INTÉGRATIONS SÉMANTIQUES uppercase tracking-widest
  • Badge animé bleu-accent (pulse)
  • Embeddé comme Living Block dans X note(s) :
  • Cartes cliquables : icône Link2 + titre de la note hôte + carnet → router.push
  • Si aucun rétrolien → section masquée

3. Connexions multiples

  • Bloc Memory Echo affiche la meilleure connexion par défaut
  • Bouton Voir toutes les connexions (N) → déplie la liste complète (réutilise editor-connections-section.tsx)
  • Chaque connexion dans la liste a son propre bouton "Embedder ce passage"

4. Chargement

  • Lazy loading : déclenché après 1.5s pour ne pas ralentir l'ouverture de la note
  • Renommage : editor-connections-section.tsxmemory-echo-section.tsx

Correction du prototype

  • Prototype calcule backlinks en parsant le contenu texte de toutes les notes (fragile, lent)
  • Production : utiliser LiveBlockRef en BDD via API (performant et fiable)

US-INSIGHTS — Vue Clusters Sémantiques & Bridge Notes

Contexte : InsightsView.tsx (482L) du prototype contient : clustering sémantique avec nommage IA, Bridge Notes, graphe réseau des clusters. En prod : bridge-notes-dashboard.tsx existe mais est séparé. À unifier en une page cohérente.

En tant qu'utilisateur, je veux une vue dédiée qui me montre comment mes notes se regroupent thématiquement, quelles notes font le pont entre clusters, et le tout visualisé dans un graphe interactif.

Page app/(main)/insights/page.tsx

Section Clusters Sémantiques :

  • Cartes colorées par cluster, nom généré par IA via bridge-notes.service.ts existant
  • Liste des 5 notes les plus centrales par cluster
  • Source : API /api/clusters existante

Section Bridge Notes :

  • Intègre bridge-notes-dashboard.tsx existant + API /api/bridge-notes
  • Suggestions de nouvelles Bridge Notes via /api/bridge-notes/suggestions

Section Graphe Réseau :

  • Monte note-graph-view.tsx existant, filtré sur les clusters
  • Toggle vue graph / vue dashboard

Adaptation depuis InsightsView.tsx

  • Remplacer runClustering / geminiService (mock) → API /api/clusters
  • Remplacer suggestBridgeIdeas → API /api/bridge-notes/suggestions
  • Conserver le layout dashboard et la logique de vue mobile/desktop

US-TEMPORAL — Prédictions d'Accès Temporelles

Contexte : TemporalView.tsx (169L) prédit quelles notes l'utilisateur voudra relire, basé sur des patterns d'accès. Rien n'existe en prod. Feature légère à fort impact perçu.

En tant qu'utilisateur, je veux voir dans la sidebar quelles notes je suis "sur le point" de vouloir relire — basé sur mes habitudes d'accès passées.

Migration Prisma

model NoteAccessLog {
  id         String   @id @default(cuid())
  noteId     String
  userId     String
  accessedAt DateTime @default(now())
  note       Note     @relation(fields: [noteId], references: [id], onDelete: Cascade)
  @@index([noteId, accessedAt])
  @@index([userId, accessedAt])
}

Logging automatique

  • POST /api/notes/[id]/access appelé à chaque ouverture de note (fire-and-forget)

Route GET /api/notes/temporal-predictions

  • Adapte la logique de temporalService.ts du prototype
  • Retourne les top 5 notes prédites : { noteId, title, predictedAt, confidence, cycleType }

Widget dans la sidebar

  • Section "Notes à relire" avec icône Clock
  • Liste des 3 meilleures prédictions
  • Badge cyclique / tendance selon le type
  • Clic → ouvre la note