« Ajouter à l'arbre » s'ouvre sur Nouvelle personne : lien (parent, enfant, conjoint,
frère ou sœur) et proche dans l'arbre, par le même chemin qu'« Ajouter quelqu'un ».
Dates, lieux, sexe, source en note, à vérifier. « Déjà dans l'arbre » complète une fiche.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Entrée « Quelqu'un ailleurs » dans le menu +. On tape un nom, une année et un lieu.
Liens préremplis vers Gallica, FranceArchives, Mémoire des hommes, Léonore, Geneanet,
Open Archives, WikiTree, FreeBMD et FamilySearch, ouverts par le navigateur.
Le serveur interroge les décès INSEE depuis 1970 (matchID) et WikiTree quand
SEARCH_SOURCES les nomme ; MATCHID_TOKEN au-delà d'une centaine de recherches.
Un résultat ajouté ne remplit que les champs vides, passe la personne à vérifier
et garde la source dans sa note.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- OIDC : une identité n'est plus reliée automatiquement à un compte existant qui a un mot de passe.
L'inscription ne vérifie pas l'adresse, un tiers aurait pu créer ce compte avec l'email de la victime et lire
ce qu'elle y met ensuite. Refus avec oidc_email_taken et un message clair (FR, EN). Trouvé par la relecture
de sécurité avant la release.
- Tests OIDC : chaque connexion simulée a sa propre adresse (limite de 20 appels par minute et par adresse).
- CHANGELOG 1.2.0, OIDC marqué expérimental dans les deux README.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Fusion de main : graphAdapter.js supprimé par #119 ; le sexe est relu par
normalizeSex au chargement du graphe (data/mockData.js), en plus du serveur
qui renvoie déjà male/female/other/null. Mention retirée de i18n/en/app.js.
- Sexe : trois boutons à bascule (Femme, Homme, Autre, aria-pressed). Rien de
choisi = non précisé (null), la valeur par défaut ; un second clic sur le
choix actif y revient. La relecture affiche « Non précisé » pour une valeur
vide (sexLabel).
- « Ajouter quelqu'un » : « Décédé(e), date inconnue » sort de la colonne Décès
et tient sur une ligne, comme la case du prénom (règle CSS commune).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET et OIDC_REDIRECT_URI
ajoutent « Se connecter avec … » à l'écran de connexion. Tout le protocole
passe par openid-client 6 : découverte, PKCE S256, state et nonce, échange
du code, vérification de l'ID token (signature, émetteur, audience, dates).
State, nonce et vérificateur PKCE vivent dans un cookie signé, httpOnly,
SameSite=Lax, qui lie le retour au navigateur qui a commencé la connexion.
Le compte est trouvé par (émetteur, sub), sinon relié par email vérifié,
sinon créé sans mot de passe si ALLOW_REGISTRATION le permet (instance
vide comprise). Retour vers l'app par un jeton de passage de 60 s, à usage
unique, dans le fragment, échangé contre une session.
PASSWORD_LOGIN=false retire les mots de passe Parenthèse. Un compte sans
mot de passe se supprime en retapant son email, dans les 15 minutes qui
suivent une connexion. Code OIDC masqué dans les journaux.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Sexe : Femme, Homme, Autre, Non précisé. Autre est enregistré « other »,
refusé en 400 comme toute valeur inconnue, et prend les formes neutres ou
doubles pour nommer un lien.
- GEDCOM : import SEX X (GEDCOM 7) → other, SEX U → non précisé ; export
other → SEX X, non précisé sans ligne SEX. Aller-retour testé.
- « Ajouter quelqu'un » : « Je ne connais pas son prénom » sort de la colonne
Prénom et tient sur une ligne (sous les deux champs sur ordinateur, sous
Prénom sur téléphone) ; les quatre choix du sexe tiennent sur une ligne.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AddPersonButton, DropTrashZone, CreateMyTreeButton, EditModeOverlay.jsx (App importe EditSessionBar sous ce
nom, sa feuille de style reste), InitialTreeWizard, InlinePersonForm, ProximityFeedback, useAddPersonDrag,
graphAdapter et graphDataHelpers, avec leurs feuilles de style. Aucun import, test compris ; lint, 253 tests et
build verts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Champ Sexe (femme, homme, non précisé) dans la fiche en modification, à l'ajout
d'une personne et dans une proposition de la famille ; relu « Sexe » dans les
propositions à relire.
- Valeurs ramenées à male / female / null à l'écriture et à la lecture (anciennes
valeurs M/F relues sans migration), valeur inconnue refusée (400). Export GEDCOM
inchangé dans sa forme.
- Lien au survol genré quand le sexe est connu (Père, Tante, Cousine germaine,
Belle-mère…), forme double sinon. Nommage déplacé dans utils/kinship : une
fonction par langue reçoit le chemin complet (pas, personnes, sexe, année de
naissance), pour brancher d'autres langues.
- Anglais : beau-parent distingué de parent par alliance selon l'ordre du chemin
(Stepmother / Mother-in-law).
- Propositions à relire : le nom de la personne modifiée vient de l'avant lu en
base quand la modification ne porte que le champ changé.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
À false, POST /auth/register répond 403 registration_closed et l'app ne
propose plus de créer un compte (accueil, connexion, porte d'un arbre
partagé, menu du visiteur). Les comptes existants se connectent et créent
leurs arbres, le partage par lien et mot de passe ne change pas.
Une instance sans aucun compte accepte quand même le premier, sous un
verrou de transaction PostgreSQL (deux inscriptions simultanées n'en
créent qu'un). GET /auth/options expose l'état à l'écran d'accueil.
Défaut true : rien ne change pour les instances existantes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Le cadre suivait le format de la photo : une photo couchee donnait toujours un
cadre couche. Le recadrage propose maintenant les cinq formes de l'arbre, en
dessin, et la forme choisie est gardee avec le cadrage (avatarCrop.shape, sans
migration). L'arbre la respecte ; sans choix, rien ne change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
La feuille de la fiche (PersonCard) se ferme en glissant sa poignée ou son en-tête vers le bas, comme le menu de la barre. Le geste est factorisé : logique pure dans utils/sheetDrag.js, hook dans hooks/useSheetDrag.js, utilisé aussi par NavMenu (poignée, seuil de 70 px inchangé).
- Le geste part seulement de la poignée et de l'en-tête. Jamais d'un champ de saisie, d'un libellé ni pendant une sélection de texte. Le contenu des onglets défile comme avant.
- La feuille suit le doigt (transform), ne monte jamais au-delà de sa position de repos.
- Fermeture au-delà d'un tiers de la hauteur, ou sur un lâcher rapide vers le bas. Sinon retour court.
- La fermeture passe par le même chemin que la croix (enregistrement automatique compris). Si l'enregistrement est refusé, la fiche revient et affiche son erreur.
- Mode feuille seulement (PHONE_QUERY et largeur de la feuille CSS). Sur ordinateur, rien ne change.
- prefers-reduced-motion respecté.
Tests : logique du geste (zones, direction, seuil, vitesse) en vitest. Recette Playwright iPhone 13 émulé, gestes tactiles CDP : fermeture par la poignée, retour après un petit glisser, lâcher rapide, défilement du contenu sans fermer, pas de geste depuis un champ, enregistrement puis fermeture, ordinateur inchangé, croix.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Un mariage sans date tombait tout en bas de l'onglet Vie, apres le deces du
conjoint, ce qui ne voulait rien dire. Il se place maintenant juste avant le
premier enfant du couple, sinon avant la fin de l'union ou le deces du
conjoint. Aucune annee n'est inventee : colonne vide et « date inconnue ».
Seul ce qui n'a aucun repere reste en bas, et tout moment sans date le dit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Les modifications en cours d'une fiche ne sont plus perdues quand on
change de personne, ferme la fiche, sort du mode modification, ouvre
un souvenir, ajoute une personne ou envoie les contributions.
- La fiche enregistre par le même chemin que « Enregistrer » (onSave),
pour la personne quittée. Un échec ou une validation la garde ouverte
avec le message d'erreur.
- Invité en contribution : les changements vont dans le brouillon, puis
partent avec « Envoyer ».
- Un petit message confirme l'enregistrement. Rien n'est enregistré
quand rien n'a changé, ni en démo.
- Un second enregistrement pendant l'envoi reçoit la même promesse
(utils/saveOnLeave.js), sans double appel.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Le cadrage (centre et zoom) est stocké dans Person.avatarCrop (JSON, nullable),
l'original reste sur disque et le cadrage s'applique à l'affichage (canevas et fiche).
Route PUT /trees/:id/persons/:personId/avatar/crop, réservée aux administrateurs.
La suppression et le nouvel envoi remettent le cadrage à zéro.
Migration additive (colonne nullable, sans défaut), testée sur base vide et sur base avec données.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
- Prénom inconnu : case « Je ne connais pas son prénom » à l'ajout et en
modification. Enregistré comme prénom vide, affiché « Prénom inconnu »
en italique (arbre, fiche, recherche). Pas de migration.
- Export GEDCOM : « 1 NAME /Nom/ » sans prénom. Import : prénom absent
= vide (au lieu de « ? »).
- Cliquer une personne pendant un ajout ferme le panneau d'ajout ; sa
saisie reste en mémoire comme à la fermeture par la croix.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
- API : nickname accepté à la création, à la modification et dans les
propositions de contribution (trim, vide = null, 120 caractères max)
- Mode direct et circuit des contributions : même validation, avant/après
- Édition et ajout : champ « Surnom » facultatif
- Arbre : surnom en italique sous le nom, coupé avec … au-delà de 150 px
- Recherche : trouve une personne par son surnom (libellé « Surnom »)
- Démo : surnoms sur Marie et Robert
Vérifié : lint, tests (front 159, back 224), builds (front, back tsc, landing).
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
- Deux parents d'un même enfant sans couple enregistré sont reliés en pointillés au lieu
d'avoir chacun un « Inconnu » (couple propre au dessin, rien en base) ; le tri de la
fratrie le reconnaît.
- Barre d'édition mobile : X, « Ajouter » et « Plus » (dessin, texte, sticker, photo).
Les outils s'ouvrent depuis « Plus » avec un retour, la corbeille n'apparaît que s'il y a
quelque chose à effacer, l'outil actif en aplat. La barre ne passe plus sous le zoom.
- Bandeau d'édition : sur téléphone, seul le bouton reste, le texte se coupait.
- Dates : au-delà de 4 chiffres les barres se posent seules (12031950 → 12/03/1950), le
message n'apparaît qu'en quittant le champ.
- Ajouter une personne : plus d'erreur « prénom » en quittant le champ vide, le bouton
reste actif et le dit à l'appui.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- Champ de date de la fiche et du formulaire « Ajouter quelqu'un » : une annee seule
(1950) ou une date complete (12/03/1950), une aide si le texte n'en est pas une.
L'annee seule s'affiche « 1950 » et non plus « 1 janvier 1950 ».
- Statut « Decede(e) » sans date : persons.isDeceased (migration additive), « 1908–? »
sous le portrait, « Date inconnue » dans la fiche, en bas de la frise. Import de
« 1 DEAT Y », export en « 1 DEAT Y ». Une date de deces retire le drapeau.
- Parents : plus de « (null) » quand l'union n'a pas de type.
Closes#77
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Le zip est lu dans le navigateur, sans bibliotheque (table des matieres puis
DecompressionStream, fichier par fichier) : le .ged le plus haut s'importe, GEDZIP
compris, et les photos et actes passent directement a l'etape des visages, rapproches
comme un dossier et decompresses seulement a l'envoi. Messages clairs pour un zip sans
.ged, protege par mot de passe ou trop gros. Corrige aussi « citait 1 autres fichiers ».
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Un moment peut porter plusieurs souvenirs de tout type (photo, video, enregistrement,
acte en PDF) : media_items.lifeEventId, reprise de l'ancienne colonne mediaId.
- Naissance, mariage et deces se completent depuis la frise : documents, source, note
(la date et le lieu restent dans Infos).
- La famille propose un moment par le lien de partage : il attend la relecture, avec
ses fichiers dans la meme proposition, et apparait une fois accepte.
- Fiche : Ajouter un fichier, Choisir parmi ses souvenirs, prenom du contributeur,
textes qui expliquent ce qui se passe.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Un bouton « Ajouter un moment » pour les administrateurs, une fiche courte (quoi, quand,
environ, ou, une photo), date exacte, fin, source et note sous « Plus de details ».
La photo devient un souvenir de la personne, rattache au moment (life_events.mediaId,
migration additive). Toucher un moment l'ouvre pour le modifier ou le supprimer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Naissance, unions et deces en grands reperes (direction A2 choisie par Patch le 16/10),
evenements importes plus petits, famille proche tiree de l'arbre en retrait, sources
repliees. Evenements charges a l'ouverture de l'onglet via GET /persons/:id/life,
donnees propres a la demo pour Robert.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Les arbres importés de Heredis avant le nettoyage à l'import gardent des
lieux bruts (« Ville,code INSEE,département,… »). La fiche les affiche avec
la même règle qu'à l'import, sans toucher à la valeur enregistrée ni au
champ d'édition.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Les correspondances gardent le chemin cité par le logiciel (sourcePath) :
plusieurs fichiers d'une même personne ne se confondent plus, le décompte
des fichiers ajoutés et restants est juste, et le pied du panneau parle de
fichiers quand des documents ont été ajoutés.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
À l'étape du dossier de photos, les fichiers cités par le logiciel en plus
des portraits (actes, registres, photos de famille) sont retrouvés dans le
même dossier et ajoutés aux souvenirs de chaque personne, avec le titre et
la source venus du GEDCOM. Un seul bouton « Tout ajouter » ; les fichiers
restants sont gardés pour rouvrir l'étape depuis les paramètres. Pour #59.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- avatarFromImmich (fichier préfixé « immich- ») remplace immichPersonId
côté front : le panneau ne propose le retrait que si le portrait vient
d'Immich, et le bouton de la fiche retrouve son libellé distinctif
- helper isImmichAvatarPath partagé entre le payload du graphe et la route
unassign, qui testaient le préfixe chacun de son côté
- tests : origine du portrait dans le payload (vrai/faux/null)
Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
- buildGraphPayload ne mappe plus immichPersonId ; test qu'il n'apparaît pas
- le front ne propage plus le champ (graphAdapter, mockData et son test)
- le panneau Immich propose le retrait d'association en permanence, sans lire
le champ ; unassign sans association ne fait rien : pas d'écriture, pas
d'entrée d'audit (test du no-op), et le bouton de la fiche garde un
libellé fixe
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>
- demo_opened (une fois par chargement, visiteur sans compte) et demo_interaction (personne, média, recherche, édition, création d'arbre ; une fois par type, sans contenu)
- Source d'arrivée gardée sur l'appareil (?source=hn) et jointe à account_screen_viewed et account_registered ; la landing transmet la vraie source au CTA et à l'iframe de démo au lieu de source=landing
- Plus de remise à zéro PostHog à chaque ouverture anonyme (effaçait le visiteur de la landing), seulement à la déconnexion
- Landing : un seul événement signup_cta_clicked (hero_cta_clicked retiré, plus utilisé par le funnel), envoyé avant la navigation sans sendBeacon (bloqué par Brave)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- text-wrap-style pretty sur les paragraphes, listes, légendes et chapôs, balance sur les titres
(landing, guides statiques, base pz.css de l'app). Longhand pour ne pas réactiver le retour
à la ligne là où white-space: nowrap est posé. Les noms de l'arbre sont sur le canvas, non touchés.
- Espaces insécables du français dans le texte visible : fine insécable avant ? ! ;,
insécable avant : et dans les guillemets, entre un nombre et son unité (Mo, Go, €, dpi).
Landing (i18n.js, bloc fr hors meta) et pages guides FR (corps de page, hors script).
- App : frenchTypography() appliquée par t() en français, avant le remplacement des variables ;
le dictionnaire anglais reste indexé sur le français tel qu'écrit. Tests mis à jour.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Vidéo piégée : l'expression régulière de position était quadratique, un faux MP4 de 2 Mo figeait l'API (bornée, linéaire)
- Sessions émises avant le 01/10/2026 refusées (SESSIONS_NOT_BEFORE) : des sessions ont fuité dans les annotations et les journaux jusqu'à la 1.0
- Contributions : l'« avant » montré au propriétaire vient de la base ; une suppression approuvée part en cascade (unions, liens, souvenirs, portrait, fichiers)
- Mot de passe de partage : essai compté avant bcrypt, comparaisons une par une, changement et rattachement dans une transaction
- Quota mesuré sur le disque, 10 arbres par compte, corps à 1 Mo hors envois, bornes sur annotations, contributions et GEDCOM
- GIF réencodés ; anciennes propositions d'annotation sans adresse stockée ; clé d'exemple refusée sans NODE_ENV
- Déploiement du commit validé par la CI, clé d'hôte obligatoire ; nginx : X-Forwarded-For écrasé, version masquée, journal sans query string
- PostHog verrouillé dans le code, sans script externe ; CSP sans *.posthog.com ; sauvegardes hors serveur chiffrées par défaut
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>