commit 2f4b142a5dc706091df105ba54e243c99c300247
parent 2358b8078b8d5c9da860df5ec6d18f27345b5815
Author: Julian Piribauer <julian.piribauer@gmail.com>
Date: Sat, 9 May 2026 19:02:46 +0000
Fix generation misalignment when one side of a partnership has more ancestors
Three changes to the generation-ranking fix loop:
- Partner alignment now also handles the case where both partners are
children (had been skipped), pushing the lower one down to max
- New step: pull parents down so they sit exactly one row above their
children (prevents Ferdinand/Christine sitting 3 rows above Julian
when Martin was pushed to gen 2 by his partnership with Silke)
- Together these ensure adding Mutter/Vater von Omi above Ingeborg no
longer cascades incorrectly through the Piribauer side
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat:
1 file changed, 32 insertions(+), 6 deletions(-)
diff --git a/static/app.js b/static/app.js
@@ -264,20 +264,31 @@ function computeLayout({ people, partnerships, parentLinks: rawLinks }, opts = {
}
for (const p of people) { if (gen[p.id] === undefined) gen[p.id] = 0; }
- // Iteratively fix generations: pull root-partners to their partner's row,
- // then push children down accordingly. Must be one combined loop because
- // moving a child up may require moving that child's root-partner up too.
+ // Iteratively fix generations until stable:
+ // 1. Align partners to the same row (max of the two).
+ // 2. Push children to be exactly one row below their parents.
+ // 3. Pull parents down so they are exactly one row above their children
+ // (handles the case where a newly-added ancestor level pushes one side
+ // of a partnership down but leaves the other side's ancestors too high).
let changed = true;
let _safetyIter = 0;
while (changed && _safetyIter++ < people.length * 4) {
changed = false;
+ // Step 1 – partner alignment
for (const ps of partnerships) {
- const g1 = gen[ps.person1_id], g2 = gen[ps.person2_id];
+ const g1 = gen[ps.person1_id] ?? 0, g2 = gen[ps.person2_id] ?? 0;
if (g1 !== g2) {
- if (!childIds.has(ps.person1_id) && g2 !== undefined) { gen[ps.person1_id] = g2; changed = true; }
- else if (!childIds.has(ps.person2_id) && g1 !== undefined) { gen[ps.person2_id] = g1; changed = true; }
+ const maxG = Math.max(g1, g2);
+ if (!childIds.has(ps.person1_id)) { gen[ps.person1_id] = maxG; changed = true; }
+ else if (!childIds.has(ps.person2_id)) { gen[ps.person2_id] = maxG; changed = true; }
+ else {
+ // Both have parents — push the lower one down to match the higher
+ if (g1 < maxG) { gen[ps.person1_id] = maxG; changed = true; }
+ else { gen[ps.person2_id] = maxG; changed = true; }
+ }
}
}
+ // Step 2 – push children down
for (const pl of parentLinks) {
let parentGen;
if (pl.partnership_id) {
@@ -291,6 +302,21 @@ function computeLayout({ people, partnerships, parentLinks: rawLinks }, opts = {
if ((gen[pl.child_id] ?? 0) < expected) { gen[pl.child_id] = expected; changed = true; }
}
}
+ // Step 3 – pull parents down (increase their gen so they sit exactly one
+ // row above their child, not two or three rows above)
+ for (const pl of parentLinks) {
+ const expectedParentGen = (gen[pl.child_id] ?? 0) - 1;
+ if (pl.partnership_id) {
+ const ps = partnerships.find(p => p.id === pl.partnership_id);
+ if (ps) {
+ for (const pid of [ps.person1_id, ps.person2_id]) {
+ if ((gen[pid] ?? 0) < expectedParentGen) { gen[pid] = expectedParentGen; changed = true; }
+ }
+ }
+ } else if (pl.parent_id != null) {
+ if ((gen[pl.parent_id] ?? 0) < expectedParentGen) { gen[pl.parent_id] = expectedParentGen; changed = true; }
+ }
+ }
}
const byGen = {};