diff --git a/src/app.js b/src/app.js index db4f777..1beee36 100644 --- a/src/app.js +++ b/src/app.js @@ -2458,6 +2458,7 @@ const pedTree = document.getElementById("pedigree-tree"); const pedBtn = document.getElementById("pedigree-btn"); const pedRefresh = document.getElementById("pedigree-refresh"); + const pedRepeatNote = document.getElementById("pedigree-repeat-note"); const PED_OPEN_DEPTH = 3; // show 3 generations expanded by default; deeper collapses let pedPollTimer = null; @@ -2580,20 +2581,24 @@ // fills many positions); "generations back" is the depth of the deepest // position (floor(log2(pos)), since sire = 2·pos and dam = 2·pos+1). function pedCounts() { - const seen = new Set(); + const counts = new Map(); let maxPos = 1; for (const k in pedNodes) { const n = pedNodes[k]; const key = n.reg || n.name; - if (key) seen.add(key); + if (key) counts.set(key, (counts.get(key) || 0) + 1); const p = Number(k); if (p > maxPos) maxPos = p; } - return { distinct: seen.size, gens: Math.floor(Math.log2(maxPos)) }; + let repeated = 0; + counts.forEach((c) => { if (c > 1) repeated++; }); + return { distinct: counts.size, repeated, gens: Math.floor(Math.log2(maxPos)) }; } function pedCountText() { - const { distinct: a, gens: g } = pedCounts(); - return `${a} ancestor${a === 1 ? "" : "s"} back ${g} generation${g === 1 ? "" : "s"}`; + const { distinct: a, repeated: r, gens: g } = pedCounts(); + let s = `${a} ancestor${a === 1 ? "" : "s"} back ${g} generation${g === 1 ? "" : "s"}`; + if (r > 0) s += `, ${r} appearing more than once`; + return s; } function setPedDone() { setPedStatus(`Traced ${pedCountText()}.`, "done"); } function setPedStatus(text, kind) { @@ -2649,9 +2654,29 @@ // 2n+1. We render it top-down like a family tree — the dog on top, parents // branching below — as a nested
Some ancestors appear in more than one place further back (pedigree collapse). Expand the tree to reveal their ×N badges, then tap one to highlight every spot that dog appears.
diff --git a/src/style.css b/src/style.css index 01933fc..91bc6be 100644 --- a/src/style.css +++ b/src/style.css @@ -1223,6 +1223,27 @@ section.collapsed > :not(h2) { display: none; } .ped-sire > .ped-card::after { content: "♂"; color: var(--sleep); } .ped-dam > .ped-card::after { content: "♀"; color: var(--training); } +/* pedigree collapse: a dog filling more than one position gets a ×N badge, a + stable hue, and lights up (with every copy) when tapped. */ +.ped-card.ped-repeat { cursor: pointer; } +.ped-repeat-badge { + position: absolute; + right: 5px; + bottom: 5px; + font-size: 0.6rem; + font-weight: 700; + line-height: 1; + padding: 2px 5px; + border-radius: 999px; + color: #fff; + background: hsl(var(--repeat-hue, 0), 58%, 48%); +} +.ped-card.ped-lit { + border-color: hsl(var(--repeat-hue, 0), 70%, 50%); + box-shadow: 0 0 0 2px hsl(var(--repeat-hue, 0), 70%, 50%), var(--shadow); +} +.pedigree-repeat-note { margin: 0 0 10px; } + /* expand/collapse toggle (top-left of the card) */ .ped-toggle { position: absolute;