# US-4 Redesign — Vue Structurée Inline dans l'Éditeur > **Status :** DRAFT — En attente de validation fondateur > **Remplace :** US-4 « Bloc de Base de Données Relationnelle Inline » (DEPRECATED) > **Epic :** US-NEXTGEN-EDITOR > **Dépend de :** US-STRUCTURED-VIEWS ✅ (livré), US-LIVING-BLOCKS ✅ (livré) --- ## 1. Problem Statement — Pourquoi l'ancienne US-4 est morte L'implémentation actuelle (fichiers `tiptap-database-block-extension.tsx`, `database-block-editor.tsx`, `database-block-types.ts`) a été rejetée pour les raisons suivantes : ### 1.1 Copie de démo marketing, pas une feature produit Le code source dans `createDefaultDatabaseBlockData()` insère **Jules Verne** et **Liu Cixin** avec leurs œuvres en données pré-remplies. C'est une copie directe du prototype `architectural-grid1/ModernBlockNoteEditor.tsx` qui était conçu pour une landing page, pas pour un produit de prise de notes réel. ### 1.2 Modèle de données parallèle et isolé L'implémentation stocke `dbAuthorsJson` et `dbBooksJson` comme attributs HTML dans le nœud TipTap : ```html
``` Problèmes critiques : - **Isolation totale** : ces données n'ont aucun lien avec le carnet, les notes, ni les propriétés Prisma. - **Scalabilité zéro** : stocker des listes entières de rows dans les attributs d'un nœud TipTap = explostion de la taille du HTML à chaque sauvegarde. - **Duplication du modèle** : `NotebookSchema` + `NotebookProperty` + `NoteProperty` existent déjà en BDD et fonctionnent. ### 1.3 Confusion utilisateur Un utilisateur qui tape `/database` s'attend à voir ses propres données structurées — pas un jeu de rôle bibliothèque de SF. Cette confusion est documentée comme rejet non-négociable par le fondateur. ### 1.4 Audit du code à supprimer | Fichier | Statut | Raison | |---------|--------|--------| | `memento-note/components/tiptap-database-block-extension.tsx` | **SUPPRIMER** | Extension TipTap avec modèle `dbAuthors`/`dbBooks` | | `memento-note/components/database-block-editor.tsx` | **SUPPRIMER** | UI Auteurs & Œuvres, hardcodé | | `memento-note/lib/editor/database-block-types.ts` | **SUPPRIMER** | Types + données Verne/Liu Cixin | | `rich-text-editor.tsx` L28, L193-194, L395, L412-420, L1286, L1335-1338, L1397-1401 | **MODIFIER** | Retirer imports et références `/database` anciens | | `block-action-menu.tsx` L15-17, L33, L39, L51, L134-135 | **MODIFIER** | Retirer option `database` du menu "Transformer en" | | `locales/*.json` — clés `databaseBlock.*` | **SUPPRIMER** | ~15 clés obsolètes | --- ## 2. Personas & Jobs-to-be-Done ### Persona 1 — Chloé, chercheuse (carnet "Thèse Linguistique") Chloé a configuré son carnet avec des propriétés : `Statut` (select), `Langue` (select), `Lu` (checkbox), `Source` (text). Elle a 47 notes dans ce carnet. Elle rédige un chapitre synthétique et veut **voir le tableau de ses sources directement sous son paragraphe d'introduction**, sans quitter l'éditeur. **Job-to-be-done :** *"Quand je rédige, je veux voir le tableau de mes données structurées en contexte, sans naviguer vers le carnet."* ### Persona 2 — Mehdi, chef de projet (carnet "Sprint Q2 2026") Carnet structuré avec propriétés `Statut` (Kanban), `Priorité`, `Assigné`. Il prend ses notes de réunion et veut **insérer une vue en lecture de son Kanban** pour partager l'état du sprint dans la note de compte-rendu. **Job-to-be-done :** *"Dans ma note de réunion, je veux une vue rapide de l'état des tâches du sprint."* ### Persona 3 — Yasmin, étudiante (carnet "Lectures Persan") Carnet avec propriétés `Lu` (checkbox), `Note` (number). Elle lit en persan (RTL). Elle veut **voir sa liste de lectures sous forme de tableau** inline dans ses notes de révision. **Job-to-be-done :** *"Dans mes notes, je veux voir mon tableau de lectures avec les bonnes valeurs — en persan, dans le bon sens."* --- ## 3. Options Produit pour `/database` — Analyse & Recommandation ### Option A — Embed Structured View (Recommandée ✅) **Description :** Le bloc inline affiche une vue filtrée (Table ou Galerie) du schéma du carnet courant. Données lues depuis l'API existante. Le bloc stocke seulement `notebookId`, `displayMode`, `filterJson` dans ses attributs TipTap. **Avantages :** - Réutilise `NotesStructuredTable` et `NotesGalleryView` existants (quasi plug-and-play). - Données cohérentes avec `/home` (même source de vérité). - Léger : 3 attributs string dans le nœud TipTap, pas de payload. - Facile à migrer : si le schéma du carnet change, le bloc se met à jour automatiquement. - Clair pour l'utilisateur : "c'est mon carnet, mes données." **Risques :** - Éditeur doit recevoir `notebookId` (actuellement absent — patch mineur dans `note-content-area.tsx`). - Si le carnet n'a pas de schéma, le bloc doit le gérer gracieusement. **Verdict :** Option principale retenue. ### Option B — Linked Database Block (style Notion) **Description :** L'utilisateur choisit n'importe quel carnet structuré (pas forcément le carnet de la note courante), avec filtre et vue configurables dans le bloc. **Avantages :** Plus puissant — permet de croiser des carnets. **Risques :** - UI de sélection de carnet complexe dans le NodeView. - Scope plus large que ce que justifie une US-4 isolée. - Cross-notebook = scope Living Blocks étendu → reporter à v2. **Verdict :** Déféré à v2 après que l'Option A soit stabilisée. ### Option C — Mini-table locale (colonnes libres dans la note) **Description :** Table simple dans la note, indépendante des carnets, avec colonnes définissables par l'utilisateur. **Avantages :** Pas de dépendance à Structured Views. **Risques :** - Crée exactement le second système de données parallèle qu'on veut éviter. - Ne répond pas aux personas (Chloé et Mehdi veulent leurs vraies données). - Rejectée explicitement par le fondateur. **Verdict :** Hors scope. Ne pas implémenter. ### Option D — Supprimer `/database` **Description :** Retirer complètement la commande slash et l'option du menu. **Avantages :** Simple, propre. **Risques :** - Perd une surface UX qui a du sens (une fois bien connectée). - L'annonce "database inline" est une attente utilisateur légitime. **Verdict :** Fallback si l'Option A prend trop de temps — mais l'Option A est faisable en taille M. --- ## 4. User Story & Critères d'Acceptation (Given/When/Then) ### US-4 (Nouvelle) : Vue Structurée de Carnet Inline dans l'Éditeur **En tant que** rédacteur dans un carnet structuré, **Je veux** insérer une vue en lecture de mon tableau de notes directement dans le corps de ma note, **Afin de** voir mes données structurées en contexte, sans quitter l'éditeur ni naviguer vers le carnet. --- #### Scénario 1 — Insertion dans un carnet structuré **Given** que ma note est dans un carnet qui a un schéma (`NotebookSchema` défini) **When** je tape `/database` dans l'éditeur **Then** un bloc `structuredViewBlock` est inséré, affichant la vue Table du carnet (colonnes = propriétés du schéma, lignes = notes du carnet) **And** aucune donnée de démo n'est insérée **And** l'utilisateur peut modifier les valeurs simples directement dans le bloc (checkbox, select, texte) — les changements se sauvegardent via `PATCH /api/notes/:id/properties` --- #### Scénario 2 — Carnet sans schéma **Given** que ma note est dans un carnet sans `NotebookSchema` **When** j'insère un bloc `/database` **Then** le bloc affiche un message contextuel : *"Ce carnet n'a pas encore de vue structurée. Configurez-en une depuis l'en-tête du carnet."* avec un lien vers le wizard **And** aucune donnée n'est chargée, aucun crash --- #### Scénario 3 — Note sans carnet **Given** que ma note n'appartient à aucun carnet (notebookId absent) **When** j'insère un bloc `/database` **Then** le bloc affiche : *"Ce bloc nécessite un carnet. Déplacez cette note dans un carnet pour l'utiliser."* --- #### Scénario 4 — Migration des blocs legacy **Given** qu'une note contient un ancien nœud `data-database-block="true"` (auteurs/œuvres) **When** la note est ouverte **Then** le bloc obsolète est silencieusement retiré (ou affiché comme placeholder "bloc obsolète") **And** le reste du contenu de la note est intact **And** aucun crash ne se produit --- #### Scénario 5 — RTL / locale persane **Given** que la langue de l'app est `fa` (persan, RTL) **When** le bloc est affiché **Then** le wrapper du bloc a `dir="auto"`, le tableau s'aligne correctement en RTL **And** les libellés du bloc sont traduits en persan (via `useLanguage()`) --- #### Scénario 6 — Bascule de vue (Table → Galerie) **Given** que le bloc est inséré dans un carnet avec `illustrationSvg` ou couleur **When** l'utilisateur clique sur le sélecteur de vue dans le bloc **Then** il peut basculer entre `Table` et `Galerie` **And** l'attribut `displayMode` du nœud TipTap est mis à jour **And** la vue se met à jour sans re-insertion du bloc --- ## 5. Out of Scope Explicite | Fonctionnalité | Raison | |----------------|--------| | Vue Kanban inline | Drag-and-drop dans un NodeView TipTap = mauvaise UX, reporté en v2 | | Sélection d'un carnet différent de la note courante | Scope trop large → v2 | | Formules / rollups calculés | Hors scope Structured Views v1 | | Relations multi-carnets | Hors scope — nécessite migration Prisma dédiée | | Filtre personnalisé via UI dans le bloc | v2 — le bloc affiche tout le carnet en v1 | | Pagination dans le bloc | v2 si le carnet dépasse 50 notes | | Export PDF/CSV depuis le bloc | Fonctionnalité séparée | --- ## 6. Composants Prototype à Référencer | Composant prototype | Usage | Note | |--------------------|-------|------| | `architectural-grid/` — `NotesStructuredTable` pattern | Colonnes et cellules — **référence design** | Utiliser le composant prod `notes-structured-table.tsx` | | `architectural-grid/` — `NotesGalleryView` pattern | Vue galerie — **référence design** | Utiliser le composant prod `notes-gallery-view.tsx` | | `architectural-grid1/ModernBlockNoteEditor.tsx` L1704+ | **NE PAS COPIER** — démo marketing Verne/Liu Cixin | Audit uniquement | > ⚠️ Le prototype `architectural-grid1` est la référence de ce qu'il **ne faut pas** shipper. Le prototype `architectural-grid` (sans `1`) est la référence de design courant. --- ## 7. Modèle de Données ### 7.1 Nœud TipTap — Attributs (référence légère uniquement) ```typescript // Extension TipTap — tiptap-structured-view-block-extension.tsx Node.create({ name: 'structuredViewBlock', group: 'block', atom: true, draggable: true, addAttributes() { return { notebookId: { default: null, parseHTML: (el) => el.getAttribute('data-sv-notebook-id'), renderHTML: (attrs) => attrs.notebookId ? { 'data-sv-notebook-id': attrs.notebookId } : {}, }, displayMode: { default: 'table', parseHTML: (el) => el.getAttribute('data-sv-mode') || 'table', renderHTML: (attrs) => ({ 'data-sv-mode': attrs.displayMode }), }, filterJson: { default: '{}', parseHTML: (el) => el.getAttribute('data-sv-filter') || '{}', renderHTML: (attrs) => ({ 'data-sv-filter': attrs.filterJson }), }, } }, parseHTML: () => [{ tag: 'div[data-structured-view-block]' }], renderHTML: ({ HTMLAttributes }) => ['div', mergeAttributes(HTMLAttributes, { 'data-structured-view-block': 'true' })], addNodeView: () => ReactNodeViewRenderer(StructuredViewBlockEmbed), }) ``` ### 7.2 Aucune migration Prisma requise Le bloc réutilise le schéma Prisma existant : - `NotebookSchema` / `NotebookProperty` → définition des colonnes - `NoteProperty` → valeurs par note - `Note` → lignes du tableau Aucune nouvelle table. Aucun risque BDD. ### 7.3 Diagramme de relations ```mermaid graph TD A["structuredViewBlock node
(TipTap attrs)"] -->|"notebookId"| B["GET /api/notebooks/:id/schema"] B --> C["NotebookSchema
(Prisma)"] C --> D["NotebookProperty[]
(colonnes)"] A -->|"notebookId"| E["GET /api/notebooks/:id/notes
(avec properties)"] E --> F["Note[]
(lignes)"] F --> G["NoteProperty[]
(valeurs)"] D & G --> H["NotesStructuredTable
(composant prod réutilisé)"] H --> I["Rendu inline dans l'éditeur"] ``` --- ## 8. Plan de Migration & Rollback du Code Actuel ### 8.1 Ordre de suppression (pour éviter les imports cassés) 1. **Supprimer** `memento-note/lib/editor/database-block-types.ts` 2. **Supprimer** `memento-note/components/database-block-editor.tsx` 3. **Supprimer** `memento-note/components/tiptap-database-block-extension.tsx` 4. **Modifier** `block-action-menu.tsx` — retirer import + option `database` 5. **Modifier** `rich-text-editor.tsx` — retirer import + slash entry + extension registration 6. **Créer** `tiptap-structured-view-block-extension.tsx` + `structured-view-block-embed.tsx` 7. **Modifier** `note-content-area.tsx` — passer `notebookId` 8. **Modifier** `locales/en.json` + `fr.json` — swap clés i18n ### 8.2 Gestion des notes existantes avec l'ancien bloc Le nœud `databaseBlock` sera inconnu de TipTap après suppression de l'extension. Par défaut ProseMirror le drop silencieusement. Options : - **Option simple (v1) :** Laisser ProseMirror supprimer le nœud inconnu au premier rendu — aucune action développeur. - **Option propre (recommandée) :** Ajouter dans la nouvelle extension une règle `parseHTML` qui matche `div[data-database-block]` et le convertit en paragraphe `⚠️ Bloc base de données obsolète — ce contenu a été retiré.` ### 8.3 Rollback possible Si le fondateur rejette l'Option A après implémentation : - Les 3 fichiers supprimés sont dans git history — `git checkout HEAD~1 -- ` les restaure. - La suppression de l'extension de `rich-text-editor.tsx` est une ligne — réversible en 2 minutes. --- ## 9. Risques Performance & Mitigations | Risque | Probabilité | Impact | Mitigation | |--------|-------------|--------|-----------| | NodeView React lourd — re-render à chaque transaction TipTap | Haute | Haute | `shouldRerenderOnTransaction: false` sur l'éditeur (US-EDITOR-PERF) + `React.memo` sur le bloc embed + `trackNodeViewPosition: false` | | SWR fetch bloque le rendu initial de la note | Moyenne | Moyenne | Suspense avec skeleton loader ; ne pas bloquer le rendu éditeur ; lazy-mount après 500ms | | Note dans carnet avec 200+ notes — tableau trop long | Basse | Moyenne | Afficher les 20 premières notes avec `"Voir toutes les N notes dans le carnet"` CTA | | RTL — cellules du tableau cassées | Basse | Haute | `dir="auto"` sur le wrapper ; tester avec locale `fa` avant merge | --- ## 10. i18n — Clés à Ajouter / Supprimer ### 10.1 Clés à ajouter (EN/FR minimum) | Clé | EN | FR | |-----|----|----| | `structuredViewBlock.insertLabel` | Structured View | Vue structurée du carnet | | `structuredViewBlock.insertDesc` | Embed your notebook's structured data | Intégrez les données structurées de votre carnet | | `structuredViewBlock.noSchema` | This notebook has no structured view yet. Set one up from the notebook header. | Ce carnet n'a pas encore de vue structurée. Configurez-en une depuis l'en-tête du carnet. | | `structuredViewBlock.noNotebook` | This block requires a notebook. Move this note to a notebook first. | Ce bloc nécessite un carnet. Déplacez cette note dans un carnet pour l'utiliser. | | `structuredViewBlock.openInNotebook` | Open in notebook | Ouvrir dans le carnet | | `structuredViewBlock.displayModeTable` | Table | Tableau | | `structuredViewBlock.displayModeGallery` | Gallery | Galerie | | `structuredViewBlock.loadError` | Failed to load structured data. | Impossible de charger les données structurées. | | `structuredViewBlock.retry` | Retry | Réessayer | | `structuredViewBlock.deprecatedBlock` | Outdated block removed. | Bloc obsolète retiré. | > **Note :** Si l'utilisateur demande à compléter les 15 locales, ne remplir que les clés — laisser la traduction des 13 autres locales à l'outil de traduction externe (règle AGENTS.md). ### 10.2 Clés à supprimer (après vérification pas d'autre consommateur) `databaseBlock.title`, `databaseBlock.viewTable`, `databaseBlock.viewCards`, `databaseBlock.hint`, `databaseBlock.colAuthor`, `databaseBlock.colWorks`, `databaseBlock.colRollup`, `databaseBlock.noLinkedWorks`, `databaseBlock.deleteShort`, `databaseBlock.addAuthor`, `databaseBlock.authorPlaceholder`, `databaseBlock.createAuthor`, `databaseBlock.worksBase`, `databaseBlock.storedCount`, `databaseBlock.addWork`, `databaseBlock.bookTitlePlaceholder`, `databaseBlock.selectAuthor`, `databaseBlock.tagPlaceholder`, `databaseBlock.coverPlaceholder`, `databaseBlock.insertWork`, `databaseBlock.deleteCard`, `databaseBlock.insertFailed`, `databaseBlock.defaultTag` --- ## 11. Checklist QA Manuelle ### 11.1 Français (LTR) - [ ] Taper `/database` dans une note d'un carnet structuré → bloc s'insère avec les données réelles - [ ] Taper `/database` → même résultat (rétrocompatibilité keyword) - [ ] Taper `/database` dans une note sans carnet → message "nécessite un carnet" - [ ] Taper `/database` dans une note d'un carnet sans schéma → callout wizard avec lien - [ ] Ouvrir une note avec un ancien bloc `databaseBlock` → note s'ouvre, bloc absent ou placeholder, pas de crash - [ ] Menu "Transformer en" → option "Vue structurée" présente, option "Base de données" (ancienne) absente - [ ] Bascule Table ↔ Galerie dans le bloc → vue change, pas de réinsertion du bloc - [ ] `npm run build` → 0 erreur, aucun import vers fichiers supprimés ### 11.2 Persan / RTL (fa) - [ ] Changer la langue en `fa` → libellés du bloc en persan - [ ] Le bloc wrapper est `dir="auto"` → tableau aligné à droite - [ ] Cellules du tableau RTL lisibles (pas de texte coupé) - [ ] Callout "no schema" en persan → texte lisible RTL --- ## 12. Estimation Effort | Tâche | Taille | |-------|--------| | Supprimer 3 fichiers legacy | XS | | Patcher `rich-text-editor.tsx` + `block-action-menu.tsx` | S | | Patcher `note-content-area.tsx` (`notebookId` prop) | XS | | Créer `tiptap-structured-view-block-extension.tsx` | S | | Créer `structured-view-block-embed.tsx` (SWR + états + RTL) | M | | i18n EN + FR | XS | | QA manuelle FR + fa | S | | **Total estimé** | **M** (~1–2 jours dev) | --- ## 13. Décisions Produit Actées ### Édition inline — ACTIVÉE en v1 L'utilisateur peut modifier les valeurs simples directement dans le bloc (checkbox, select, texte court). Les changements se sauvegardent via `PATCH /api/notes/:id/properties` avec mise à jour optimiste. C'est la meilleure expérience utilisateur — un bloc en lecture seule n'aurait aucun intérêt. ### Kanban inline — REPORTÉ en v2 Table + Galerie uniquement en v1. Le drag-and-drop dans un NodeView TipTap crée de mauvaises interactions. Le Kanban s'utilisera depuis le carnet comme aujourd'hui. --- ## Annexe — Diff Proposé pour `docs/story-nextgen-editor.md` Remplacer la section `### US-4: Bloc de Base de Données Relationnelle Inline` (lignes 71–85) par : ```markdown ### US-4: Vue Structurée de Carnet Inline *(Redesign — voir `docs/story-nextgen-editor-us4-redesign.md`)* > ⚠️ **DEPRECATED** — La spécification précédente (bloc "Auteurs & Œuvres") est rejetée. > Voir [`docs/story-nextgen-editor-us4-redesign.md`](./story-nextgen-editor-us4-redesign.md) pour la nouvelle spec. **En tant que** rédacteur dans un carnet structuré, **Je veux** insérer une vue en lecture de mon tableau de notes directement dans le corps de ma note, **Afin de** voir mes données structurées en contexte, sans quitter l'éditeur. *(Critères d'acceptation détaillés dans le fichier redesign ci-dessus.)* ``` Et remplacer la section `### 1. Structure du Nœud de Base de Données Tiptap` + `### 2.` + `### 3.` dans les Spécifications Techniques (lignes 90–109) par : ```markdown ### 1. Structure du Nœud `structuredViewBlock` (TipTap) Extension TipTap `StructuredViewBlockExtension` dans `tiptap-structured-view-block-extension.tsx` : - Attributs : `notebookId` (string), `displayMode` ('table'|'gallery'), `filterJson` (string JSON) - ReactNodeViewRenderer → `structured-view-block-embed.tsx` ### 2. Suppression du code legacy - Supprimer : `tiptap-database-block-extension.tsx`, `database-block-editor.tsx`, `lib/editor/database-block-types.ts` - Modifier : `rich-text-editor.tsx`, `block-action-menu.tsx`, `locales/*.json` - Voir plan complet dans `docs/story-nextgen-editor-us4-redesign.md` §8. ``` --- ## Annexe — Diff Proposé pour `docs/user-stories.md` ### Dans le tableau de bord (ligne 23) ```diff -| **US-NEXTGEN-EDITOR** | Éditeur Next-Gen : Drag Handle + Menu Bloc + DB Inline + Smart Paste | 🚧 **PLANIFIÉ** | Voir `docs/story-nextgen-editor.md` | +| **US-NEXTGEN-EDITOR** | Éditeur Next-Gen : Drag Handle + Menu Bloc + Vue Structurée Inline + Smart Paste | 🚧 **PLANIFIÉ** | Voir `docs/story-nextgen-editor.md` + `docs/story-nextgen-editor-us4-redesign.md` | ``` ### Dans la section US-NEXTGEN-EDITOR (US-4, ligne 622–626) ```diff -### US-4 : Bloc de Base de Données Relationnelle Inline -- Slash `/database` → insère un React NodeView -- Vues Tableau / Fiches avec Rollup dynamique -- Modèle relationnel local (auteurs/livres par défaut, ou lié au carnet) +### US-4 : Vue Structurée de Carnet Inline *(Redesign)* +> Spec complète : [`docs/story-nextgen-editor-us4-redesign.md`](./story-nextgen-editor-us4-redesign.md) +- Slash `/database` (+ keywords `db`, `tableau`, `structured`) → insère un `structuredViewBlock` +- Affiche Table ou Galerie du carnet courant via l'API Structured Views existante +- Bloc stocke uniquement `notebookId` + `displayMode` (pas de données en attrs) +- Graceful fallback si carnet sans schéma ou note sans carnet +- Supprime le code legacy `tiptap-database-block-extension.tsx` (Verne/Liu Cixin) +- **Dépend de :** US-STRUCTURED-VIEWS ✅ (livré) ``` ### Dans la liste de fichiers (ligne 627–631) ```diff -### Fichiers -- `[NEW]` `tiptap-drag-handle-plugin.ts` — Plugin ProseMirror pur -- `[NEW]` `tiptap-database-block-extension.tsx` — NodeView React -- `[MODIFY]` `rich-text-editor.tsx` — Intégration drag handle + DB + paste intercept -- `[MODIFY]` `globals.css` — Gutter, poignée, glassmorphic dropdowns +### Fichiers +- `[NEW]` `tiptap-drag-handle-plugin.ts` — Plugin ProseMirror pur (US-1, inchangé) +- `[NEW]` `tiptap-structured-view-block-extension.tsx` — NodeView Vue Structurée (remplace DB) +- `[NEW]` `structured-view-block-embed.tsx` — Composant embed avec SWR + states +- `[DELETE]` `tiptap-database-block-extension.tsx` — Bloc legacy auteurs/œuvres rejeté +- `[DELETE]` `database-block-editor.tsx` — UI legacy rejetée +- `[DELETE]` `lib/editor/database-block-types.ts` — Types legacy rejetés +- `[MODIFY]` `rich-text-editor.tsx` — Intégration drag handle + Vue Structurée + paste intercept +- `[MODIFY]` `note-content-area.tsx` — Passer `notebookId` à l'éditeur +- `[MODIFY]` `block-action-menu.tsx` — Remplacer option "Database" par "Vue structurée" +- `[MODIFY]` `globals.css` — Gutter, poignée, glassmorphic dropdowns ```