L'association remplaçait et supprimait le portrait en place sans rien
demander. Le serveur refuse maintenant le remplacement d'un portrait
existant (409 avatar_exists), quelle que soit son origine, tant que la
requête ne porte pas replaceAvatar ; le panneau Immich transforme ce
refus en demande de confirmation avant de rejouer la requête. Sans
vignette Immich, l'association ne touche pas au portrait et aucune
confirmation n'est demandée.
Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
- Nouvelle table tree_immich_configs (adresse + clé d'API chiffrée AES-256-GCM,
clé dérivée de JWT_SECRET) et routes GET/PUT/DELETE /trees/:id/immich/config
réservées aux administrateurs de l'arbre ; journal d'audit sans secret
(adresse et « clé changée » uniquement).
- Fin des variables d'env IMMICH_ENABLED / IMMICH_API_URL / IMMICH_API_KEY :
l'adresse se choisit parmi IMMICH_ALLOWED_API_URLS, la liste établie par
l'opérateur du serveur. Elle filtre l'enregistrement ET chaque requête :
retirer une adresse coupe aussitôt les connexions qui l'utilisent.
- Changer d'instance exige une nouvelle clé : l'ancienne est écrasée et ne
voyage jamais vers le nouveau serveur.
- Le désassignement reste possible sans connexion : purement local, il nettoie
association et portrait stocké même après suppression de la connexion ; le
panneau affiche « Retirer l'association » dans tous les états.
- Onglet Immich dans Paramètres de l'arbre : menu des instances autorisées,
test de connexion, suppression avec confirmation ; i18n anglais des nouveaux
messages.
Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
Reprise de upstream (IliesAllali/main) dans la branche Immich. Seul
conflit : l'import des icônes dans PersonCard.jsx — les deux lignes
sont gardées (ImagePlus pour le portrait vide de « Premières photos »,
Sparkles pour le bouton « Depuis Immich »). Tout le reste fusionne
automatiquement ; l'intégration Immich (routes, exports de media.ts,
mapping immichPersonId, panneau) est inchangée.
Vérifié après la fusion : tests Immich (16), tests backend focalisés
gedcom-photos (4), build tsc, eslint et les 107 tests du front. Seul
« rate-limits login endpoint » dépasse son délai de 5 s, déjà le cas
sur main avant la fusion (bcrypt, machine lente).
Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
- Colonne persons.immichPersonId (schéma + migration 20261012_001) et
exposition dans le graphe pour l'afficher côté fiche.
- Client Immich (src/lib/immich.ts) sur le contrat OpenAPI v3.1.0 :
ping, /api/people paginé, vignette par personne ; erreurs distinctes
entre injoignable, clé refusée et introuvable, identifiants validés
comme UUID v4.
- Routes /trees/:id/immich/* (status, people, thumbnail, assign,
unassign), réservées aux administrateurs de l'arbre ; l'adresse et la
clé d'Immich ne quittent jamais le backend.
- La vignette passe par le pipeline des portraits (type lu dans les
octets, métadonnées retirées, quota de l'arbre) : le fichier est écrit
avant la transaction (personne + journal immich_assign) et l'ancien
portrait est supprimé après ; un échec du nettoyage est journalisé
sans faire échouer la requête. Le désassignement retire le portrait
seulement s'il vient d'Immich (préfixe immich-) et préserve un
portrait envoyé à la main.
- Côté front : bouton « Depuis Immich » sur la fiche en édition, panneau
de sélection avec aperçu et retrait de l'association, traductions
anglaises, messages d'erreur distincts.
- Tests : 16 tests backend (succès, 400/401/403/404/413/502/503, quota,
échec de transaction, préservation du portrait manuel, serveur
désactivé) et le mapping immichPersonId côté front.
Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
- Création d'arbre : « J'ai déjà un arbre » importe le .ged sans fiche « vous » en double.
- Import GEDCOM : lit les photos citées (OBJE / FILE, en ligne ou par renvoi, _PRIM d'abord)
et renvoie leur chemin ; rien n'est gardé côté serveur.
- Fenêtre « Il manque les visages » après un import : dossier des photos apparié par nom de
fichier, cinq visages à demander (famille de « Moi », sinon les plus jeunes).
- Portrait vide chez le propriétaire : cadre cliquable sur la fiche hors édition, « + photo »
au survol sur l'arbre.
- Photos de plus de 5 Mo réduites à 2000 px avant l'envoi ; portraits à 120 envois par minute.
- PostHog : gedcom_imported, portraits_bulk_placed, portrait_added (effectifs arrondis).
- Corrigé : une union à un seul parent connu relie ses enfants à ce parent, sous un seul
« Inconnu », au lieu de les laisser sans lien.
- Corrigé : réimporter un GEDCOM reprend les personnes, unions et liens déjà présents au lieu
de tout doubler.
Recette locale (vraie API, vraie base) : 29/29 sur le parcours photos, 47/47 sur la recette
de non-régression des parcours. Tests 123 serveur, 107 app, lint et build propres.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
La PR montait @eslint/js seul, dont la config recommandée ajoute no-useless-assignment, no-unassigned-vars et preserve-caught-error. eslint passe aussi en 10 (configuration à plat déjà en place, aucun commentaire eslint-env, Node 24) ; eslint-plugin-react-hooks 7.1 et eslint-plugin-react-refresh 0.5 la déclarent prise en charge. Les trois signalements de no-useless-assignment étaient fondés : une valeur initiale jamais lue, chaque branche suivante l'écrase ou quitte la fonction (libellé de parenté, contenu JSON d'une photo ou d'un tracé posé sur l'arbre). Valeur initiale retirée, comportement inchangé.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
La version 1 ne publie plus dist/esm/icons/*.js ni __iconNode : renderers.js importait ces fichiers internes pour dessiner les vignettes carte et GPX sur le canvas, d'où l'échec du build (CI Frontend et image Docker). Les tracés viennent maintenant de @lucide/icons, le paquet officiel des données d'icônes (export nommé, propriété node), à la même version que lucide-react. Les 47 icônes utilisées existent toujours sous leur nom (Edit3, HelpCircle, MoreHorizontal, Trash2 restent des alias) et rendent un SVG identique à la 0.564, aria-hidden compris. Les icônes de marque retirées en v1 ne sont pas utilisées ici.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
React et react-dom 19.3.0, @types/react et @types/react-dom 19.3.0, posthog-js 1.434.15, autoprefixer 10.6.1, eslint-plugin-react-hooks 7.1.1, eslint-plugin-react-refresh 0.5.7. La règle react-hooks/refs de la 7.1 voit maintenant les refs lus ou écrits pendant le rendu, corrigés plutôt que désactivés : le nom de l'arbre partagé affiché par l'écran compte passe par un état (un ref ne redessine rien), la personne de départ de l'arbre est recopiée dans un effet déclaré avant celui du cadrage, l'API de zoom est exposée par useImperativeHandle, l'horodatage des portraits est posé au premier chargement au lieu de Date.now() au rendu (react-hooks/purity). react-refresh 0.5 garde l'export par défaut et la config vite utilisés ici.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Avec un arbre, la fenêtre « Mon compte » ne menait nulle part : elle a maintenant « Gérer mon compte », qui ouvre « Vos arbres » (déconnexion, suppression du compte), la croix ramène à l'arbre. Sans arbre, « Mon compte » renvoyait aussitôt à l'assistant de création : le repli vers la démo ne se déclenche plus quand l'utilisateur a ouvert l'écran lui-même. Sur le lien d'un arbre supprimé, la session du compte est restaurée, « Créer mon arbre » ouvre le compte connecté au lieu d'un écran de connexion. Page vie privée alignée sur le chemin réel.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Portraits, souvenirs, photos posées sur l'arbre et aperçus de relecture chargeaient leur image avec la session complète dans l'adresse. Ils utilisent maintenant le jeton de lecture des médias fourni avec le graphe (utils/mediaToken.js), jamais ajouté à une adresse externe. L'aperçu d'une photo posée par la famille n'accepte qu'une photo de cet arbre, reconstruite depuis son chemin. Message pour un arbre plein (storage_quota_exceeded).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Le cache d'images de l'arbre chargeait l'adresse de chaque média, vidéos YouTube
comprises, sans que personne lance la vidéo. Seules les photos, les seules
dessinées, sont chargées.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
L'identifiant doit faire 11 caractères (lettres, chiffres, - et _), sinon rien
n'est intégré. Le lecteur vient de youtube-nocookie.com. La carte OpenStreetMap
n'envoie plus que l'origine en referrer, plus l'adresse de l'arbre.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Module frontend/src/i18n : le texte français sert de clé, dictionnaires anglais
par zone, langue du navigateur puis choix mémorisé, ?lang=en pour forcer.
Bascule dans le menu du compte, la pastille visiteur, le menu Plus, l'écran
d'accès et la connexion. Démo traduite (noms et lieux gardés). Liens d'aide et
de vie privée selon la langue. Textes français sans accents corrigés.
Test : chaque texte passé à t() a sa traduction.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Le cadrage de l'étape 5 s'appuyait sur la personne de départ, absente de
certains arbres réels : on retombait sur l'arbre entier illisible.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Replays du 24/09 : personne ne trouvait comment ajouter. Pour qu'une
grand-mère s'en sorte seule depuis un lien :
- arbre ouvert zoomé sur la personne de départ quand il est trop grand
pour être lisible en entier, sans fiche ouverte par-dessus
- carte d'accueil de deux phrases, une seule fois par arbre
- barre du bas en toutes lettres : Ajouter un souvenir, Ajouter une
personne, Plus (dessin, mode d'emploi, compte)
- « Pour qui ? » : recherche par prénom avec portraits, puis les souvenirs
de la personne ; panneau simplifié pour la famille
- une personne ajoutée part tout de suite à l'envoi (prénom, un mot)
- fiche ouverte sur Souvenirs avec le bouton, dates en toutes lettres
- bandeau d'envoi masqué tant qu'il n'y a rien à envoyer
- portraits de l'arbre perdus après un rechargement sans nouveau
placement : horodatage stable et images rechargées à chaque données
- page parenthese.io/aide/ avec captures de l'arbre de démo, liée depuis
l'accueil, le menu Plus et le message d'invitation
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Visites : chaque ouverture de l'arbre est notée (prénom facultatif demandé avec le mot de passe, appareil, compte), sans adresse IP, effacée après un an ; onglet Visites dans les paramètres, réservé aux administrateurs
- Recherche : un résultat choisi recentre l'arbre sur la personne, la liste se replie
- Écran de compte ouvert depuis l'arbre : croix pour y revenir
- Enfant presque sous l'union : trait direct au lieu d'un petit S
- Page vie privée : paragraphe sur les visites
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Conjoint marié sans parent ni enfant dans l'arbre : petit portrait collé à son partenaire, sans emplacement de nœud ni orbite
- Enfant placé sous l'autre lignée : pastilles « Voir les parents » et « Voir <prénom> » cliquables au lieu du trait qui traverse l'arbre
- Fil terracotta clair en accolade arrondie, union marquée du rond terracotta
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Le panneau du propriétaire affichait la copie gardée par le navigateur où le
mot de passe avait été défini : changé depuis un autre appareil, il montrait
l'ancien. Le serveur garde maintenant le mot de passe de partage chiffré
(AES-256-GCM, clé dérivée de JWT_SECRET) dans une table à part, pour ne pas
toucher la version des accès déjà ouverts.
- GET /trees/:id/access/passwords : le mot de passe en vigueur, admin seulement
- POST .../remember : arbres d'avant, le propriétaire saisit celui déjà donné,
vérifié contre les hash, retenu sans rien changer aux accès
- rotation à deux mots de passe distincts : plus de valeur lisible
- anciennes copies locales effacées à l'ouverture du panneau
- message d'invitation (lien, mot de passe, une phrase) modifiable et à copier
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Prénom demandé à l'envoi d'une contribution et d'un souvenir, retenu
sur l'appareil pour la fois suivante (submittedByLabel). Le propriétaire
lit « par Camille » au lieu de « par Contributeur ».
- Mot d'accompagnement enregistré (ContributionSession.comment, migration
additive) et affiché à la relecture.
- Une personne ajoutée par la famille garde ses liens : identifiant
provisoire « tmp:… » remplacé par le vrai id à l'application ; un lien
vers une personne refusée est écarté au lieu de faire échouer la
relecture. Frère ou sœur : parents calculés aussi pour la famille.
- Soumission et relecture en transaction, appliquées dans l'ordre
personnes, unions, liens : tout ou rien.
- Bouton « Ajouter une personne » pour la famille sur ordinateur ; après
l'ajout, le panneau se ferme et un message confirme que la personne est
dans les modifications ; il ne réapparaît plus au retour en édition.
- Relecture : les liens se lisent « Léon, enfant de Marc ».
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Un membre de la famille (lien de partage ou membre non administrateur)
peut maintenant envoyer une photo, une vidéo, une voix ou une citation.
Le serveur la stocke « en attente » et crée une contribution d'une ligne,
qui arrive dans « Propositions à relire » avec l'aperçu de la photo.
Accepter la rend visible, refuser la masque. En mode « application
immédiate », elle est visible tout de suite.
- Migration additive : MediaItem.status (approved par défaut, tout
l'existant reste visible), MediaItem.contributionSessionId, valeur
« media » dans ContributionEntityType.
- Graphe, statistiques, ordre des souvenirs : souvenirs acceptés seuls.
- Relecture d'une session contenant un dessin : ne plante plus
(l'annotation tombait dans la branche des liens).
- App : message « Merci, c'est envoyé » après un envoi en attente ;
réordonner, supprimer et changer la photo de profil réservés au
propriétaire (le serveur les refuse aux autres).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Le déverrouillage ouvre toujours en contributeur, quel que soit le mot de
passe tapé : les deux mots de passe déjà envoyés aux familles continuent
de marcher. Les jetons et accès « visitor » existants peuvent proposer
sans rien ressaisir. La rotation accepte un mot de passe unique, écrit
dans les deux colonnes (pas de migration). Côté app, un nouvel arbre
reçoit un seul mot de passe, gardé dans le navigateur du propriétaire,
et le panneau de partage n'affiche plus qu'un champ, avec Copier.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Pendant une attente, le rond se divise en trois points qui respirent,
puis se referme avec un léger rebond quand c'est prêt.
- App : écran de démarrage (BootLoader, visible seulement au-delà de 400 ms,
au moins 900 ms pour ne pas clignoter), remplace « Chargement de l'arbre
généalogique... ».
- App : les boutons en attente (Création…, Envoi…, Un instant…) montrent
trois points à la place du libellé, largeur figée, texte gardé pour les
lecteurs d'écran (PzBusy).
- Landing : chargement de la démo intégrée.
- Réduire les animations : points sur place, fondu seul.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Landing refaite : nouvelle palette claire, Newsreader et DM Sans, logo validé,
sections (garanties, trois gestes, démo, pour qui, engagements, guides, bloc final).
Illustration du hero conservée et recolorée, portraits alignés sur les âges.
- App : couleurs et typographie passées à la nouvelle DA, structure et fonctionnalités inchangées.
Composants communs dans src/styles/pz.css.
- Refaits de zéro : fiche d'une personne (souvenirs, infos, liens, panneau mobile),
connexion, création de compte, liste des arbres, accès par mot de passe, recherche,
barre d'actions, menu du compte, zoom, et toutes les fenêtres (compte, création d'arbre,
contributions, envoi, sortie d'édition, brouillon, ajout de personne, souvenirs, visionneuse,
paramètres de l'arbre).
- Arbre : chaque personne garde sa forme de cadre tirée au hasard, cadre vertical pour une photo carrée.
- Démo : photos d'époque libres de droits hébergées dans public/demo (sources et licences
dans sources.json et CREDITS.md), famille Dupont crédible, Isabelle née à Fort-de-France.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Utilisateurs
- Suppression de compte depuis l'écran « Votre compte », confirmée par mot de passe :
arbres possédés et médias effacés, email retiré des contributions, sessions fermées partout.
- Suppression d'arbre définitive, en base et sur le disque.
- Ajout d'un frère ou d'une sœur relié aux parents existants ; relations parent et enfant
corrigées en mode démo.
- Liste d'attente retirée (route, envoi d'emails, dépendance Resend).
Projet ouvert
- CI : front, landing, backend avec vérification des migrations, test Docker de bout en bout.
- Déploiement lancé seulement après une CI réussie sur main.
- Image API : CLI Prisma installée à part pour que les migrations tournent au démarrage.
- SECURITY.md, CONTRIBUTING.md, modèles d'issue et de pull request.
- Migration renommée avec un nom descriptif, contenu identique.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Galaxie familiale : personnes, unions et filiations, photos, vidéos et voix sur chaque personne,
partage par lien avec consultation sans compte et contributions validées par le propriétaire.
Auto-hébergeable avec Docker. Licence PolyForm Noncommercial 1.0.0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>