Trois corrections ciblees, sans changement d'architecture ni d'interface. 1. Le style ne transporte plus l'icone L'icone d'un noeud est du contenu, comme son texte ou sa note. Copier le style de "Reseau" et le coller sur "Stockage" et "Securite" applique desormais les couleurs, la forme et la mise en page en laissant chaque noeud avec sa propre icone. Seule la presentation de l'icone (position, taille, espacement) reste une propriete de style. 2. Le cercle conserve un rapport 1:1 `keepAspectRatio` sur le redimensionneur, et contrainte cote store pour les trois autres chemins : redimensionnement direct, saisie d'une largeur ou d'une hauteur dans le panneau, et conversion d'un noeud rectangulaire en cercle. Un cercle ne peut plus devenir un ovale. 3. Point d'entree unique des documents `normalizeMapDocument()` remplace `parseMapDocument()` et devient le seul chemin par lequel une carte entre dans l'editeur, qu'elle vienne d'un fichier .mmap, d'IndexedDB ou d'un partage. Le pipeline est toujours validation -> migration -> normalisation. Les cartes locales passaient jusqu'ici a cote de la migration : elles y passent maintenant a l'ouverture, sans etre reecrites en base tant que l'utilisateur ne les modifie pas. Une carte locale videe de ses noeuds reste ouvrable, et le message d'erreur s'adapte a la provenance. Egalement - Zoom minimum abaisse de 0.1 a 0.02 : "Adapter a l'ecran" butait sur ce plancher et ne montrait pas toute une carte de 181 noeuds. - README : pipeline de normalisation, comportement du style et du cercle, limitations mesurees. Tests : 103 vitest (85 -> 103) et 31 pytest, ruff, eslint et tsc au vert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
611 lines
22 KiB
Markdown
611 lines
22 KiB
Markdown
# OpenMind
|
|
|
|
**Cartes mentales libres, local-first, sans compte et sans abonnement.**
|
|
|
|
OpenMind est une alternative gratuite et open source aux services de mind mapping
|
|
qui placent l'export, les themes ou le nombre de cartes derriere un abonnement.
|
|
Toutes les fonctionnalites sont disponibles, sans exception et sans compte.
|
|
|
|
- Aucune limitation artificielle, aucun paiement, aucun "premium".
|
|
- Aucun compte obligatoire : il n'y a d'ailleurs aucun systeme de comptes.
|
|
- Aucune telemetrie, aucun tracker, aucune publicite.
|
|
- Vos cartes vous appartiennent : elles restent dans votre navigateur.
|
|
- Export PDF, PNG, SVG et impression : gratuits.
|
|
- Un partage par lien, uniquement si vous le demandez explicitement.
|
|
- Un seul conteneur Docker a demarrer.
|
|
|
|
---
|
|
|
|
## Philosophie local-first
|
|
|
|
Une carte que vous creez n'est **jamais** envoyee au serveur.
|
|
|
|
Elle est enregistree dans l'**IndexedDB de votre navigateur**, automatiquement,
|
|
apres une courte periode d'inactivite. Le backend n'existe que pour une seule
|
|
chose : generer un lien de partage lorsque vous cliquez explicitement sur
|
|
**Partager**.
|
|
|
|
Consequences concretes :
|
|
|
|
- Fermer le navigateur, redemarrer le PC ou redemarrer le conteneur ne fait
|
|
perdre aucune carte locale.
|
|
- Une fois l'application chargee une premiere fois, elle continue de fonctionner
|
|
sans reseau (PWA) : creation, edition, export, impression.
|
|
- Vider les donnees du site, ou changer de navigateur/machine, fait disparaitre
|
|
les cartes locales. **Exportez vos cartes en `.mmap`** pour en conserver une
|
|
sauvegarde portable.
|
|
|
|
---
|
|
|
|
## Demarrage rapide
|
|
|
|
Prerequis : Docker et Docker Compose.
|
|
|
|
```bash
|
|
git clone <url-du-depot>
|
|
cd Mind-Mapping
|
|
cp .env.example .env
|
|
docker compose up -d --build
|
|
```
|
|
|
|
Puis ouvrez :
|
|
|
|
```
|
|
http://localhost:8080
|
|
```
|
|
|
|
Verifier que le service repond :
|
|
|
|
```bash
|
|
curl http://localhost:8080/api/health
|
|
```
|
|
|
|
Reponse attendue : `{"status":"ok"}`
|
|
|
|
Etat du conteneur (le healthcheck Docker interroge `/api/health`) :
|
|
|
|
```bash
|
|
docker compose ps
|
|
```
|
|
|
|
Arreter :
|
|
|
|
```bash
|
|
docker compose down
|
|
```
|
|
|
|
---
|
|
|
|
## Architecture
|
|
|
|
Un seul service applicatif. FastAPI sert a la fois l'API et les fichiers
|
|
statiques du frontend compile.
|
|
|
|
```
|
|
Navigateur
|
|
|
|
|
+-- React / TypeScript (Vite, Tailwind, React Flow, Zustand)
|
|
|
|
|
+-- IndexedDB (Dexie)
|
|
| +-- cartes privees/locales <-- ne quittent jamais le navigateur
|
|
|
|
|
+-- API, uniquement pour les fonctions serveur (partage)
|
|
|
|
|
v
|
|
FastAPI (+ fichiers statiques du frontend)
|
|
|
|
|
v
|
|
SQLite
|
|
|
|
|
+-- cartes explicitement partagees
|
|
```
|
|
|
|
Choix structurants :
|
|
|
|
| Sujet | Choix | Pourquoi |
|
|
|---|---|---|
|
|
| Hierarchie | `parentId` fait autorite, les liens en sont derives | un seul invariant a maintenir, pas de desynchronisation |
|
|
| Modele | types metier propres, adaptes vers React Flow a l'affichage | le format de fichier ne depend pas de la bibliotheque de rendu |
|
|
| Historique | instantanes `{nodes, edges}`, groupes par action | un glisser-deposer = une seule annulation |
|
|
| Auto-layout | dagre, declenche uniquement a la demande | les deplacements manuels ne sont jamais ecrases |
|
|
| Versionnement | champ `version` + point d'extension de migration | le format peut evoluer sans casser les fichiers existants |
|
|
|
|
---
|
|
|
|
## Variables d'environnement
|
|
|
|
Toutes definies dans `.env` (modele : `.env.example`). Aucun secret n'est requis.
|
|
|
|
| Variable | Defaut | Role |
|
|
|---|---|---|
|
|
| `APP_NAME` | `OpenMind` | Nom du produit, affiche dans l'interface et l'onglet |
|
|
| `APP_PORT` | `8080` | Port publie sur l'hote (le conteneur ecoute toujours 8080) |
|
|
| `DATABASE_URL` | `sqlite:////app/data/openmind.db` | Base des cartes partagees |
|
|
| `MAX_MAP_SIZE_MB` | `10` | Taille maximale d'une carte envoyee au serveur |
|
|
| `MAX_NODES` | `5000` | Nombre maximal de noeuds par carte |
|
|
| `CORS_ORIGINS` | vide | Origines externes autorisees, separees par des virgules |
|
|
| `LOG_LEVEL` | `INFO` | `DEBUG`, `INFO`, `WARNING`, `ERROR` |
|
|
|
|
Le nom du produit est centralise : cote backend via `APP_NAME`, cote frontend
|
|
dans `frontend/src/config/app.ts`. Le frontend recupere le nom a l'execution via
|
|
`GET /api/config` : changer `APP_NAME` suffit, sans recompiler.
|
|
|
|
---
|
|
|
|
## Donnees et sauvegarde
|
|
|
|
Deux stockages **totalement distincts** :
|
|
|
|
| Donnee | Ou | Sauvegarde |
|
|
|---|---|---|
|
|
| Cartes privees | IndexedDB du navigateur | Export `.mmap` depuis le tableau de bord ou l'editeur |
|
|
| Cartes partagees | SQLite, dans `./data/openmind.db` | Copie du dossier `./data` |
|
|
|
|
Le dossier `./data` est monte en **bind mount** (aucun volume Docker nomme) :
|
|
les fichiers sont donc directement lisibles depuis l'hote.
|
|
|
|
```bash
|
|
docker compose stop
|
|
tar czf openmind-partages-$(date +%F).tar.gz data/
|
|
docker compose start
|
|
```
|
|
|
|
SQLite fonctionne en mode WAL : `openmind.db`, `openmind.db-wal` et
|
|
`openmind.db-shm` doivent etre sauvegardes ensemble.
|
|
|
|
Redemarrer le serveur n'a **aucun impact** sur les cartes privees locales.
|
|
|
|
---
|
|
|
|
## Fonctionnement du partage
|
|
|
|
Une carte n'est envoyee au serveur que lorsque vous cliquez sur **Partager**,
|
|
apres une explication et une confirmation.
|
|
|
|
Deux secrets distincts sont generes cote serveur avec le module Python
|
|
`secrets` :
|
|
|
|
| Jeton | Entropie | Ou il vit | A quoi il sert |
|
|
|---|---|---|---|
|
|
| `share_token` | `secrets.token_urlsafe(12)`, environ 96 bits | dans l'URL publique `/s/<token>` | **lecture seule** |
|
|
| `management_token` | `secrets.token_urlsafe(32)`, environ 256 bits | **uniquement** dans l'IndexedDB du proprietaire | modification et suppression |
|
|
|
|
Regles appliquees :
|
|
|
|
- Le `management_token` n'apparait **jamais** dans une URL, jamais dans un export
|
|
`.mmap`, jamais dans les logs.
|
|
- Il est transmis dans l'en-tete `X-Management-Token` pour `PUT` et `DELETE`.
|
|
- Seul son **hash SHA-256** est stocke en base ; la comparaison est faite en
|
|
temps constant avec `secrets.compare_digest`.
|
|
- Connaitre le `share_token` ne permet donc **ni** de modifier **ni** de
|
|
supprimer un partage.
|
|
- Le lien est **non repertorie** : il n'apparait dans aucune liste publique,
|
|
mais toute personne qui le connait peut consulter la carte.
|
|
|
|
Cycle de vie :
|
|
|
|
1. **Partager** : un instantane de la carte est copie sur le serveur, un lien est
|
|
renvoye, par exemple `http://localhost:8080/s/a8F3xP9k`.
|
|
2. **Mettre a jour le partage** : remplace l'instantane par la version actuelle.
|
|
Tant que vous ne le faites pas, le lien public montre l'ancienne version.
|
|
3. **Supprimer le partage** : efface l'instantane serveur. Le lien affiche alors
|
|
une page propre indiquant que le partage n'existe plus. La carte locale n'est
|
|
pas affectee.
|
|
|
|
Si vous supprimez une carte locale, sa reference de partage locale part avec
|
|
elle : supprimez d'abord le partage si vous voulez aussi retirer la copie
|
|
serveur.
|
|
|
|
### API
|
|
|
|
| Methode | Route | Auth | Role |
|
|
|---|---|---|---|
|
|
| `GET` | `/api/health` | - | Etat du service |
|
|
| `GET` | `/api/config` | - | Nom de l'application et limites |
|
|
| `POST` | `/api/shares` | - | Creer un partage |
|
|
| `GET` | `/api/shares/{share_token}` | - | Lire une carte partagee |
|
|
| `PUT` | `/api/shares/{share_token}` | `X-Management-Token` | Mettre a jour |
|
|
| `DELETE` | `/api/shares/{share_token}` | `X-Management-Token` | Supprimer |
|
|
|
|
Documentation interactive : `http://localhost:8080/api/docs`.
|
|
|
|
---
|
|
|
|
## Fonctionnalites
|
|
|
|
**Tableau de bord** : creation, import, recherche, date de derniere
|
|
modification, renommage, duplication, export, suppression avec confirmation.
|
|
Une carte d'exemple est creee au tout premier lancement.
|
|
|
|
**Editeur** : canvas infini, zoom, deplacement, zoom-to-fit, minimap, grille
|
|
optionnelle, creation/suppression/deplacement de noeuds, selection multiple,
|
|
connexion entre noeuds avec detection de boucle, edition du texte au
|
|
double-clic, undo/redo, copier/coller, duplication, sauvegarde automatique.
|
|
|
|
**Mind mapping** : noeud racine et noeuds standard, branches droites, courbes ou
|
|
Bezier, branches repliables avec compteur d'elements masques.
|
|
|
|
**Auto-layout**, uniquement sur demande : mind map droite, mind map gauche,
|
|
arbre horizontal, arbre vertical. L'espacement est configurable (distance
|
|
parent/enfant et espacement entre branches), avec les presets Compact, Normal
|
|
et Aere. Modifier l'espacement ne deplace rien tant que l'organisation n'est pas
|
|
relancee.
|
|
|
|
**Proprietes du noeud**, regroupees en sections repliables (Contenu,
|
|
Typographie, Apparence, Mise en page, Branche) : texte, icone, URL, notes ;
|
|
police, taille, gras, italique, alignement horizontal et vertical, interligne ;
|
|
couleurs de fond/texte/bordure, forme (rectangle, arrondi, capsule, cercle),
|
|
ombre ; taille automatique ou personnalisee, largeur, hauteur, padding
|
|
horizontal et vertical, retour a la ligne, largeur maximale du texte ; style,
|
|
epaisseur et couleur de la branche. Un reglage s'applique a toute la selection,
|
|
avec un etat *Mixte* quand les valeurs different.
|
|
|
|
**Mise en page visuelle** : redimensionnement des noeuds a la souris (une seule
|
|
annulation par redimensionnement), taille automatique qui epouse le contenu,
|
|
alignement et distribution d'une multi-selection, aimantation sur la grille,
|
|
guides d'alignement pendant le deplacement, copier / coller / reinitialiser le
|
|
style (Ctrl+Maj+C, Ctrl+Maj+V), menu contextuel par clic droit. Le style couvre
|
|
les couleurs, la forme, la typographie, la mise en page, la presentation de
|
|
l'icone et la branche ; l'icone elle-meme, comme le texte, la note et l'URL,
|
|
appartient au contenu du noeud et n'est jamais remplacee par un collage.
|
|
|
|
**Bibliotheque d'icones** : emojis et icones integrees (Lucide), avec position
|
|
(gauche, droite, au-dessus), taille et espacement configurables.
|
|
|
|
**Themes** : Default, Minimal, Dark, Midnight, Ocean, Forest, Pastel,
|
|
Monochrome, Blueprint. Plus un mode clair/sombre pour l'interface. Changer de
|
|
theme ne supprime jamais une personnalisation explicite d'un noeud.
|
|
|
|
**Recherche dans la carte** : resultats listes, navigation entre eux, centrage
|
|
et mise en evidence temporaire du noeud, depliage automatique des branches
|
|
repliees.
|
|
|
|
**Exports** : `.mmap` (format natif), PNG (x1, x2, fond transparent), SVG, PDF
|
|
(A4/A3, portrait/paysage, marges, adapter a une page ou taille reelle, fond,
|
|
titre, date), impression via `@media print`.
|
|
|
|
**PWA** : installable, et utilisable hors ligne pour les cartes locales une fois
|
|
l'application chargee.
|
|
|
|
---
|
|
|
|
## Raccourcis clavier
|
|
|
|
`Cmd` remplace `Ctrl` sur macOS.
|
|
|
|
| Raccourci | Action |
|
|
|---|---|
|
|
| `Tab` | Ajouter un noeud enfant |
|
|
| `Entree` | Ajouter un noeud frere |
|
|
| `Suppr` / `Retour arriere` | Supprimer la selection, hors saisie de texte |
|
|
| `F2` | Editer le texte du noeud selectionne |
|
|
| Double-clic | Editer le texte du noeud |
|
|
| `Ctrl+Z` | Annuler |
|
|
| `Ctrl+Y` ou `Ctrl+Shift+Z` | Retablir |
|
|
| `Ctrl+C` | Copier |
|
|
| `Ctrl+V` | Coller |
|
|
| `Ctrl+D` | Dupliquer |
|
|
| `Ctrl+Maj+C` | Copier le style du noeud (sans son icone) |
|
|
| `Ctrl+Maj+V` | Coller le style sur la selection |
|
|
| `Ctrl+A` | Tout selectionner |
|
|
| `Ctrl+S` | Sauvegarde explicite |
|
|
| `Ctrl+F` | Rechercher dans la carte |
|
|
| `F` | Adapter la carte a l'ecran |
|
|
| `Echap` | Quitter l'edition ou deselectionner |
|
|
|
|
---
|
|
|
|
## Format de fichier `.mmap`
|
|
|
|
Un `.mmap` est un fichier **JSON UTF-8** contenant le document complet de la
|
|
carte. Il ne contient jamais de jeton de partage.
|
|
|
|
Le format est en **version 2**. Les fichiers v1 restent lisibles : tous les
|
|
champs ajoutes en v2 (`layout`, `iconStyle`, `branch`, reglages d'espacement)
|
|
sont optionnels et un champ absent signifie simplement *herite du theme*. La
|
|
seule transformation appliquee a l'import d'un fichier v1 est le deplacement de
|
|
`style.width` vers `layout.width`.
|
|
|
|
Toute carte qui entre dans l'editeur passe par le meme point d'entree,
|
|
`normalizeMapDocument()`, quelle que soit sa provenance :
|
|
|
|
```
|
|
fichier .mmap ┐
|
|
IndexedDB ├─> validation -> migration de version -> normalisation -> modele courant
|
|
partage ┘
|
|
```
|
|
|
|
La normalisation est **non destructive** : le document fourni n'est jamais
|
|
modifie et rien n'est reecrit en base. Une carte v1 restee en IndexedDB s'ouvre
|
|
donc exactement comme avant, et n'est reenregistree au format courant que si
|
|
vous la modifiez.
|
|
|
|
```json
|
|
{
|
|
"version": 1,
|
|
"id": "uuid",
|
|
"name": "Architecture Homelab",
|
|
"createdAt": "2026-01-01T10:00:00.000Z",
|
|
"updatedAt": "2026-01-01T12:30:00.000Z",
|
|
"nodes": [
|
|
{
|
|
"id": "uuid",
|
|
"type": "root",
|
|
"text": "Infrastructure",
|
|
"position": { "x": 0, "y": 0 },
|
|
"parentId": null,
|
|
"collapsed": false,
|
|
"icon": "\ud83d\udda5\ufe0f",
|
|
"url": "https://exemple.fr",
|
|
"note": "Texte libre",
|
|
"style": {
|
|
"backgroundColor": "#eef2ff",
|
|
"shape": "rounded",
|
|
"textAlign": "center",
|
|
"verticalAlign": "middle",
|
|
"lineHeight": 1.35,
|
|
"fontFamily": "sans",
|
|
"shadow": "soft"
|
|
},
|
|
"layout": {
|
|
"widthMode": "fixed",
|
|
"width": 240,
|
|
"heightMode": "auto",
|
|
"paddingX": 14,
|
|
"paddingY": 8,
|
|
"wrap": true,
|
|
"maxTextWidth": 260
|
|
},
|
|
"iconStyle": { "kind": "emoji", "position": "left", "size": 16, "gap": 8 },
|
|
"branch": { "style": "bezier", "width": 2, "color": "#94a3b8" }
|
|
}
|
|
],
|
|
"edges": [{ "id": "e-a-b", "source": "a", "target": "b" }],
|
|
"viewport": { "x": 0, "y": 0, "zoom": 1 },
|
|
"settings": {
|
|
"theme": "default",
|
|
"layout": "mindmap-right",
|
|
"branchStyle": "bezier",
|
|
"showGrid": true,
|
|
"layoutSpacing": { "parentGap": 100, "siblingGap": 50 },
|
|
"snapToGrid": false,
|
|
"snapGrid": 20,
|
|
"alignmentGuides": true
|
|
}
|
|
}
|
|
```
|
|
|
|
A l'import, le fichier est **valide et normalise** : structure, version, champs
|
|
essentiels, limite de noeuds, assainissement des URL (`javascript:` et `data:`
|
|
sont supprimes), rejet des styles invalides. Le contenu n'est jamais execute ni
|
|
interprete comme du HTML ; un fichier invalide produit un message clair.
|
|
|
|
Le champ `version` permet les migrations futures : le point d'extension se
|
|
trouve dans `frontend/src/services/mapSerialization.ts`, fonction `migrate`.
|
|
|
|
---
|
|
|
|
## Developpement
|
|
|
|
### Backend
|
|
|
|
```bash
|
|
cd backend
|
|
python -m venv .venv
|
|
source .venv/bin/activate
|
|
pip install -r requirements-dev.txt
|
|
DATABASE_URL=sqlite:///./openmind-dev.db STATIC_DIR=./static uvicorn app.main:app --reload --port 8080
|
|
```
|
|
|
|
Sous Windows, activez l'environnement avec `.venv\Scripts\activate`.
|
|
|
|
### Frontend
|
|
|
|
```bash
|
|
cd frontend
|
|
npm install
|
|
npm run dev
|
|
```
|
|
|
|
Le serveur Vite ecoute sur `http://localhost:5173` et proxyfie `/api` vers
|
|
`http://localhost:8080`.
|
|
|
|
Autres commandes :
|
|
|
|
```bash
|
|
npm run build
|
|
npm run lint
|
|
npm run typecheck
|
|
npm run test
|
|
```
|
|
|
|
Regenerer les icones de la PWA :
|
|
|
|
```bash
|
|
python scripts/generate_icons.py
|
|
```
|
|
|
|
---
|
|
|
|
## Tests et qualite
|
|
|
|
Backend :
|
|
|
|
```bash
|
|
cd backend
|
|
python -m pytest -q
|
|
ruff check .
|
|
ruff format --check .
|
|
```
|
|
|
|
Frontend :
|
|
|
|
```bash
|
|
cd frontend
|
|
npm run test
|
|
npm run lint
|
|
npm run typecheck
|
|
```
|
|
|
|
Sans installation locale de Node ou Python, tout peut tourner via Docker :
|
|
|
|
```bash
|
|
docker run --rm -v "$PWD/backend:/work" -w /work python:3.12-slim sh -c "pip install -q -r requirements-dev.txt && ruff check . && python -m pytest -q"
|
|
```
|
|
|
|
```bash
|
|
docker run --rm -v "$PWD/frontend:/app" -w /app node:20-alpine sh -c "npm ci && npm run lint && npm run test && npm run build"
|
|
```
|
|
|
|
Les tests backend couvrent : healthcheck, creation, lecture, mise a jour et
|
|
suppression d'un partage, refus avec un mauvais jeton de gestion, refus sans
|
|
jeton, partage inexistant, payload invalide, limites de taille et de noeuds,
|
|
non-divulgation du jeton de gestion, repli SPA.
|
|
|
|
Les tests frontend couvrent : stockage IndexedDB, serialisation et validation
|
|
d'import, migrations, store Zustand (creation, suppression de branche,
|
|
undo/redo, groupement du glisser-deposer, copier/coller, duplication, detection
|
|
de boucle, recherche, auto-layout, lecture seule), assainissement des URL.
|
|
|
|
---
|
|
|
|
## Structure du projet
|
|
|
|
```
|
|
.
|
|
├── Dockerfile # build multi-stage : node -> python -> image finale
|
|
├── docker-compose.yml # un seul service, bind mount ./data
|
|
├── .env.example
|
|
├── README.md
|
|
├── scripts/
|
|
│ └── generate_icons.py # generation des icones PWA
|
|
├── data/ # bind mount : SQLite des partages
|
|
├── backend/
|
|
│ ├── requirements.txt
|
|
│ ├── pyproject.toml # configuration ruff et pytest
|
|
│ ├── app/
|
|
│ │ ├── main.py # assemblage uniquement
|
|
│ │ ├── config.py
|
|
│ │ ├── logging_config.py
|
|
│ │ ├── middleware.py # en-tetes de securite, limite de taille
|
|
│ │ ├── web.py # service des fichiers statiques du frontend
|
|
│ │ ├── api/ # routes health, config, shares
|
|
│ │ ├── db/ # moteur, session, initialisation du schema
|
|
│ │ ├── models/ # SharedMap (SQLAlchemy)
|
|
│ │ ├── schemas/ # validation Pydantic
|
|
│ │ └── services/ # jetons, logique de partage, erreurs metier
|
|
│ └── tests/
|
|
└── frontend/
|
|
├── index.html
|
|
├── public/ # manifest PWA, service worker, icones
|
|
└── src/
|
|
├── config/app.ts # nom du produit et constantes
|
|
├── types/map.ts # modele metier versionne
|
|
├── db/ # schema Dexie
|
|
├── services/ # depots, serialisation, client API
|
|
├── stores/ # Zustand : editeur, interface, configuration
|
|
├── hooks/ # routeur minimal, debounce
|
|
├── components/ # briques d'interface reutilisables
|
|
├── features/
|
|
│ ├── editor/ # canvas, noeud, barre d'outils, panneaux, layout, themes
|
|
│ ├── maps/ # tableau de bord
|
|
│ ├── export/ # PNG, SVG, PDF, impression
|
|
│ └── sharing/ # dialogue de partage, page publique
|
|
├── utils/
|
|
└── styles/
|
|
```
|
|
|
|
---
|
|
|
|
## Securite
|
|
|
|
- Le texte d'un noeud est **toujours** traite comme du texte. Aucun
|
|
`dangerouslySetInnerHTML`, aucun HTML issu d'une carte n'est interprete.
|
|
- Les URL sont assainies : seuls `http`, `https` et `mailto` sont acceptes. Les
|
|
liens s'ouvrent avec `rel="noopener noreferrer nofollow"`.
|
|
- Validation Pydantic de tout ce qui entre dans l'API, avec limites de taille et
|
|
de nombre de noeuds ; un depassement produit un message clair, jamais un
|
|
plantage.
|
|
- En-tetes de securite : CSP stricte, `X-Content-Type-Options`,
|
|
`X-Frame-Options`, `Referrer-Policy`, `Permissions-Policy`,
|
|
`Cross-Origin-Opener-Policy`.
|
|
- CORS restrictif : aucune origine externe par defaut.
|
|
- Acces SQL exclusivement via l'ORM SQLAlchemy.
|
|
- Jetons cryptographiquement aleatoires, jeton de gestion stocke hashe et jamais
|
|
journalise.
|
|
- Le conteneur tourne avec un utilisateur non-root.
|
|
- Aucun secret dans le frontend.
|
|
|
|
---
|
|
|
|
## Limitations connues
|
|
|
|
- **Export SVG** : les noeuds sont encapsules dans un `foreignObject` (HTML
|
|
embarque). Le fichier reste vectoriel et se redimensionne sans perte, les
|
|
branches sont de vrais chemins SVG, mais certains editeurs vectoriels
|
|
n'exploitent pas le contenu HTML des noeuds. Preferez PNG ou PDF pour un usage
|
|
bureautique.
|
|
- **Exports image et PDF en arriere-plan** : la generation s'appuie sur une
|
|
frame d'animation du navigateur, qui n'est pas produite lorsque l'onglet est
|
|
masque. Gardez l'onglet au premier plan pendant un export ; sinon l'operation
|
|
est interrompue au bout de 45 secondes avec un message explicite.
|
|
- **PDF en taille reelle** : si la carte depasse la page choisie, elle est
|
|
reduite pour tenir entierement plutot que d'etre coupee ; un message le
|
|
signale.
|
|
- **Gestion d'un partage** : le jeton de gestion vit dans l'IndexedDB du
|
|
navigateur d'origine. Depuis un autre appareil, un partage ne peut etre ni mis
|
|
a jour ni supprime.
|
|
- **Dispositions reservees** : `mindmap-both`, `organization` et `freeform`
|
|
existent dans le modele de donnees mais ne sont pas proposees dans
|
|
l'interface.
|
|
- **Types de noeuds reserves** : `note`, `image`, `group`, `link` et `task` sont
|
|
acceptes par le format, mais la V1 ne produit que `root` et `standard`.
|
|
- **Migrations de schema SQLite** : la V1 initialise le schema avec
|
|
`create_all`, une seule table. Alembic n'est pas utilise ; il faudra
|
|
l'introduire au premier changement de schema.
|
|
- **Guides d'alignement** : ils apparaissent pendant un deplacement lorsqu'un
|
|
bord ou un centre coincide (a 6 px pres) avec celui d'un autre noeud. Ils sont
|
|
purement visuels : ils n'aimantent pas le noeud. Pour aimanter, activez
|
|
*Aimanter sur la grille*.
|
|
- **Alignement vertical** : il n'a d'effet visible que si le noeud est plus haut
|
|
que son texte, c'est-a-dire avec une hauteur fixe ou un padding vertical
|
|
important.
|
|
- **Cercle** : un noeud circulaire garde toujours un rapport 1:1. Modifier sa
|
|
largeur ajuste sa hauteur et inversement ; convertir un noeud rectangulaire en
|
|
cercle le ramene a un carre. Il n'est donc pas possible d'obtenir un ovale.
|
|
- **Deplacement sur une tres grande carte** : sur une carte de ~180 noeuds, une
|
|
frame de glisser-deposer coute environ 60 a 80 ms car React Flow recalcule
|
|
l'ensemble des noeuds controles. Le deplacement reste utilisable mais moins
|
|
fluide que sur une carte de taille courante.
|
|
- **Mobile** : consultation et edition basique fonctionnent, mais l'interface
|
|
est concue pour desktop et tablette.
|
|
- **Aucun compte utilisateur** : c'est voulu. Pas de connexion, pas de
|
|
collaboration temps reel, pas de synchronisation cloud.
|
|
|
|
---
|
|
|
|
## Licences des dependances
|
|
|
|
Toutes les dependances sont sous licence libre permissive, compatible avec une
|
|
application publique et gratuite. Aucune bibliotheque a licence commerciale
|
|
n'est utilisee.
|
|
|
|
| Dependance | Licence |
|
|
|---|---|
|
|
| React, React DOM | MIT |
|
|
| `@xyflow/react` (React Flow) | MIT |
|
|
| Zustand | MIT |
|
|
| Dexie | Apache-2.0 |
|
|
| dagre | MIT |
|
|
| html-to-image | MIT |
|
|
| jsPDF | MIT |
|
|
| lucide-react | ISC |
|
|
| Vite, Tailwind CSS, TypeScript, ESLint, Vitest | MIT |
|
|
| FastAPI, Starlette, Pydantic, SQLAlchemy, Uvicorn | MIT / BSD |
|
|
|
|
---
|
|
|
|
## Licence
|
|
|
|
OpenMind est distribue sous licence **MIT** : voir le fichier [LICENSE](LICENSE).
|