{#if gutterVisible} {#each gutterRows as row, i (`stripe-${row.rank}`)}
{/each} {/if} {#each parentLinks.shared as group (group.key)} {@const aCenter = nodeCenter(group.parentA)} {@const bCenter = nodeCenter(group.parentB)} {@const childCenters = group.childIds .map((id) => nodeCenter(id)) .filter((c): c is { x: number; y: number } => c !== null)} {#if aCenter && bCenter && childCenters.length > 0} {@const midX = (aCenter.x + bCenter.x) / 2} {@const parentBottomY = aCenter.y + NODE_H / 2} {@const childTopY = childCenters[0].y - NODE_H / 2} {@const barY = (parentBottomY + childTopY) / 2} {@const xs = childCenters.map((c) => c.x)} {@const minX = Math.min(midX, ...xs)} {@const maxX = Math.max(midX, ...xs)}
{#if minX !== maxX}
{/if} {#each childCenters as cc, i (group.childIds[i])}
{/each} {/if} {/each} {#each parentLinks.single as link (link.key)} {@const parentCenter = nodeCenter(link.parentId)} {@const childCenter = nodeCenter(link.childId)} {#if parentCenter && childCenter} {@const parentBottomY = parentCenter.y + NODE_H / 2} {@const childTopY = childCenter.y - NODE_H / 2} {@const barY = (parentBottomY + childTopY) / 2}
{#if parentCenter.x !== childCenter.x}
{/if}
{/if} {/each} {#each spouseEdges as e (e.id)} {@const aCenter = nodeCenter(e.personId)} {@const bCenter = nodeCenter(e.relatedPersonId)} {#if aCenter && bCenter}
{/if} {/each} {#each nodes as node (node.id)} {@const pos = layout.positions.get(node.id)} {#if pos} {@const isSelected = selectedId === node.id} {@const isFocused = focusedId === node.id}
onSelect(node.id)} onkeydown={(e) => handleNodeKey(e, node.id)} onfocus={() => (focusedId = node.id)} onblur={() => (focusedId = null)} class="cursor-pointer focus:outline-none" > {#if isFocused}
{/if}
{#if isSelected}
{/if}
{node.displayName}
{#if node.birthYear || node.deathYear}
{node.birthYear ?? '?'}–{node.deathYear ?? ''}
{/if}
{/if} {/each}