2026-06-21 14:09:35 +09:00
"use strict" ;
function lineageSplitByActualEdges ( component ) {
const family = lineageEdgeFamily ( world . family || { } ) ;
const nodes = Array . from ( new Map ( lineageCleanComponent ( component , family ) . filter ( Boolean ) . map ( n => [ n . id , n ] ) ) . values ( ) ) ;
const idSet = new Set ( nodes . map ( n => n . id ) ) ;
2026-06-22 02:03:19 +09:00
const parts = window . TarinaiFamilyGraph . connectedComponents ( nodes , {
sort : lineageNodeSort ,
edgesOf : ( n ) => [
... lineageMutualParentIds ( n , family , idSet ) ,
... lineageMutualChildIds ( n , family , idSet ) ,
] ,
filter : part => part . length && part . some ( x => x . alive ) ,
} ) ;
2026-06-21 14:09:35 +09:00
return parts ;
}
function lineageExpandComponent ( component , family ) {
family = lineageEdgeFamily ( family ) ;
const ids = new Set ( component . filter ( Boolean ) . map ( n => n . id ) ) ;
let changed = true ;
while ( changed ) {
changed = false ;
for ( const id of Array . from ( ids ) ) {
const n = family [ id ] ;
if ( ! n ) continue ;
for ( const p of lineageMutualParentIds ( n , family ) ) {
if ( ! ids . has ( p ) ) { ids . add ( p ) ; changed = true ; }
}
for ( const c of lineageMutualChildIds ( n , family ) ) {
if ( ! ids . has ( c ) ) { ids . add ( c ) ; changed = true ; }
}
}
}
return Array . from ( ids ) . map ( id => family [ id ] ) . filter ( Boolean ) ;
}
function lineageCleanComponent ( component , family ) {
family = lineageEdgeFamily ( family ) ;
const expanded = lineageExpandComponent ( component , family ) ;
const idSet = new Set ( expanded . map ( n => n . id ) ) ;
return expanded . filter ( n => lineageMutualParentIds ( n , family , idSet ) . length || lineageMutualChildIds ( n , family , idSet ) . length ) ;
}
function lineageFastLayoutNeeded ( nodes , idSet , family ) {
const edgeCount = nodes . reduce ( ( sum , n ) => sum + lineageParentIds ( n , idSet , family ) . length , 0 ) ;
const maxGenerationWidth = Math . max ( 0 , ... Array . from ( new Set ( nodes . map ( n => n . generation || 1 ) ) ) . map ( g => nodes . filter ( n => ( n . generation || 1 ) === g ) . length ) ) ;
return {
edgeCount ,
fast : nodes . length > 140 || edgeCount > 260 || maxGenerationWidth > 34 ,
} ;
}
function lineageLayoutRowsFast ( rows , idSet ) {
const NODE _W = 132 ;
2026-06-21 22:29:00 +09:00
const NODE _H = 84 ;
2026-06-21 14:09:35 +09:00
const SIBLING _GAP = 22 ;
const ROW _GAP = 116 ;
const LEFT _PAD = 82 ;
const TOP _PAD = 44 ;
const RIGHT _PAD = 48 ;
const BOTTOM _PAD = 30 ;
const positions = new Map ( ) ;
let width = 420 ;
rows . forEach ( ( row , rowIndex ) => {
const y = TOP _PAD + rowIndex * ( NODE _H + ROW _GAP ) ;
let x = LEFT _PAD ;
for ( const n of row ) {
positions . set ( n . id , { x , y , rowIndex , node : n } ) ;
x += NODE _W + SIBLING _GAP ;
}
width = Math . max ( width , x - SIBLING _GAP + RIGHT _PAD ) ;
} ) ;
const height = TOP _PAD + Math . max ( 0 , rows . length - 1 ) * ( NODE _H + ROW _GAP ) + NODE _H + BOTTOM _PAD ;
return { positions , width , height , nodeW : NODE _W , nodeH : NODE _H , topPad : TOP _PAD , rowGap : ROW _GAP , fastMode : true } ;
}
function lineageBuildLinksFast ( nodes , idSet , layout ) {
const { positions , nodeW , nodeH } = layout ;
const partnerLinks = [ ] ;
const childLinks = [ ] ;
const seenPartners = new Set ( ) ;
let childLinkGroups = 0 ;
let childLinkEdges = 0 ;
for ( const child of nodes ) {
const childPos = positions . get ( child . id ) ;
if ( ! childPos ) continue ;
const parents = lineageParentIds ( child , idSet ) . map ( id => positions . get ( id ) ) . filter ( Boolean ) . sort ( ( a , b ) => a . x - b . x ) ;
if ( ! parents . length ) continue ;
childLinkGroups += 1 ;
childLinkEdges += parents . length ;
const childX = childPos . x + nodeW / 2 ;
const childY = childPos . y ;
const fromY = Math . max ( ... parents . map ( p => p . y + nodeH ) ) ;
const joinX = parents . reduce ( ( sum , p ) => sum + p . x + nodeW / 2 , 0 ) / parents . length ;
if ( parents . length >= 2 ) {
const left = parents [ 0 ] ;
const right = parents [ parents . length - 1 ] ;
const partnerKey = parents . map ( p => p . node ? . id ) . filter ( Boolean ) . sort ( ) . join ( "+" ) ;
if ( partnerKey && ! seenPartners . has ( partnerKey ) ) {
seenPartners . add ( partnerKey ) ;
const y = ( ( left . y + nodeH / 2 ) + ( right . y + nodeH / 2 ) ) / 2 ;
partnerLinks . push ( ` M ${ ( left . x + nodeW ) . toFixed ( 1 ) } ${ y . toFixed ( 1 ) } L ${ right . x . toFixed ( 1 ) } ${ y . toFixed ( 1 ) } ` ) ;
}
}
const laneY = Math . min ( childY - 18 , fromY + Math . max ( 28 , ( childY - fromY ) * 0.48 ) ) ;
childLinks . push ( ` M ${ joinX . toFixed ( 1 ) } ${ fromY . toFixed ( 1 ) } L ${ joinX . toFixed ( 1 ) } ${ laneY . toFixed ( 1 ) } L ${ childX . toFixed ( 1 ) } ${ laneY . toFixed ( 1 ) } L ${ childX . toFixed ( 1 ) } ${ childY . toFixed ( 1 ) } ` ) ;
}
return { partnerLinks , childLinks , childLinkGroups , childLinkEdges } ;
}
function buildLineageTreeLayout ( component , family ) {
family = lineageEdgeFamily ( family ) ;
const cleaned = lineageCleanComponent ( component , family ) ;
const nodes = Array . from ( new Map ( cleaned . filter ( Boolean ) . map ( n => [ n . id , n ] ) ) . values ( ) ) . sort ( lineageNodeSort ) ;
const idToNode = new Map ( nodes . map ( n => [ n . id , n ] ) ) ;
const idSet = new Set ( idToNode . keys ( ) ) ;
const generations = Array . from ( new Set ( nodes . map ( n => n . generation || 1 ) ) ) . sort ( ( a , b ) => a - b ) ;
const rows = lineageRowsForLayout ( nodes , generations , idToNode , idSet ) ;
const perf = lineageFastLayoutNeeded ( nodes , idSet , family ) ;
const layout = perf . fast ? lineageLayoutRowsFast ( rows , idSet ) : lineageLayoutRows ( rows , idSet ) ;
const links = perf . fast ? lineageBuildLinksFast ( nodes , idSet , layout ) : lineageBuildLinks ( nodes , idSet , layout ) ;
const labels = generations . map ( ( g , rowIndex ) => ( { generation : g , y : layout . topPad + rowIndex * ( layout . nodeH + layout . rowGap ) + 18 } ) ) ;
const renderedNodes = nodes . map ( n => ( { node : n , ... ( layout . positions . get ( n . id ) || { } ) } ) ) . filter ( n => Number . isFinite ( n . x ) && Number . isFinite ( n . y ) ) ;
return { ... layout , labels , nodes : renderedNodes , childLinks : links . childLinks , partnerLinks : links . partnerLinks , childLinkGroups : links . childLinkGroups , childLinkEdges : links . childLinkEdges , fastMode : perf . fast , edgeCount : perf . edgeCount } ;
}
function lineageFamilyHtml ( component , index , family , signature = "" ) {
const uniqueComponent = Array . from ( new Map ( component . filter ( Boolean ) . map ( n => [ n . id , n ] ) ) . values ( ) ) . sort ( lineageNodeSort ) ;
const cacheKey = lineageFamilyCacheKey ( uniqueComponent , family , signature ) ;
if ( uiCache . archiveHtmlCache ? . has ( cacheKey ) ) return lineageHydrateFamilyHtml ( uiCache . archiveHtmlCache . get ( cacheKey ) , index ) ;
const layout = buildLineageTreeLayout ( uniqueComponent , family ) ;
const aliveCount = uniqueComponent . filter ( n => n . alive ) . length ;
const childEdgeCount = lineageExpectedChildEdgeCount ( uniqueComponent , family ) ;
const maskId = "{{LINEAGE_MASK_ID}}" ;
const nodeMaskRects = layout . nodes
. map ( p => ` <rect x=" ${ ( p . x + 1 ) . toFixed ( 1 ) } " y=" ${ ( p . y + 1 ) . toFixed ( 1 ) } " width=" ${ ( layout . nodeW - 2 ) . toFixed ( 1 ) } " height=" ${ ( layout . nodeH - 2 ) . toFixed ( 1 ) } " rx="9" fill="black"></rect> ` )
. join ( "" ) ;
const labels = layout . labels . map ( label => ` <div class="tree-generation-label" style="top: ${ Math . round ( label . y ) } px"> \u 4e16 \u 4ee3 ${ label . generation } </div> ` ) . join ( "" ) ;
const partnerPaths = layout . partnerLinks . map ( d => ` <path class="partner-link" d=" ${ d } "></path> ` ) . join ( "" ) ;
const childPaths = layout . childLinks . map ( d => ` <path class="child-link" d=" ${ d } "></path> ` ) . join ( "" ) ;
const nodeHtml = layout . nodes . map ( p => lineageTreeNodeHtml ( p . node , p . x , p . y ) ) . join ( "" ) ;
const empty = ! layout . childLinks . length ? ` <div class="lineage-empty-family"> \u 89aa \u 5b50 \u 95a2 \u 4fc2 \u 306e \u 7dda \u 306f \u 307e \u 3060 \u 3042 \u 308a \u 307e \u 305b \u 3093 \u 3002</div> ` : "" ;
const html = ` <section class="lineage-family lineage-family-tree" data-lineage-index="{{LINEAGE_INDEX}}" data-lineage-key=" ${ escapeHtml ( cacheKey ) } " data-lineage-child-edges=" ${ childEdgeCount } " data-lineage-rendered-child-edges=" ${ layout . childLinkEdges || 0 } " data-lineage-child-groups=" ${ layout . childLinkGroups || 0 } " data-lineage-child-paths=" ${ layout . childLinks . length } ">
< h3 > \u5bb6\u7cfb { { LINEAGE _INDEX } } / $ { uniqueComponent . length } \u5339 / \u751f\u5b58 $ { aliveCount } < / h 3 >
< div class = "lineage-tree-panel" style = "width:${Math.ceil(layout.width)}px;height:${Math.ceil(layout.height)}px" >
< svg class = "lineage-links" viewBox = "0 0 ${Math.ceil(layout.width)} ${Math.ceil(layout.height)}" aria - hidden = "true" >
< defs > < mask id = "${maskId}" maskUnits = "userSpaceOnUse" > < rect x = "0" y = "0" width = "${Math.ceil(layout.width)}" height = "${Math.ceil(layout.height)}" fill = "white" > < / r e c t > $ { n o d e M a s k R e c t s } < / m a s k > < / d e f s >
< g mask = "url(#${maskId})" > $ { partnerPaths } $ { childPaths } < / g >
< / s v g >
$ { labels } $ { nodeHtml } $ { empty }
< / d i v >
< / s e c t i o n > ` ;
uiCache . archiveHtmlCache ? . set ( cacheKey , html ) ;
return lineageHydrateFamilyHtml ( html , index ) ;
}
function renderArchive ( ) {
if ( ! ui . archiveContent ) return ;
if ( typeof archiveAutoUpdateEnabled === "function" && ! archiveAutoUpdateEnabled ( ) ) {
uiCache . archiveScheduled = false ;
if ( ! uiCache . archiveVersion ) {
ui . archiveContent . innerHTML = ` <div class="selected-info empty"> \u 5bb6 \u 7cfb \u 56f3 \u 306e \u 66f4 \u 65b0 \u 306fOFF \u 3067 \u 3059 \u 3002 \u 300c \u 66f4 \u 65b0 \u 300d \u 3092ON \u 306b \u 3059 \u 308b \u 3068 \u 63cf \u 753b \u 3057 \u 307e \u 3059 \u 3002</div> ` ;
} else if ( world . familyTreeDirty ) {
lineageSetArchiveStaleStatus ( true ) ;
}
return ;
}
if ( uiCache . deferArchiveUntil && performance . now ( ) < uiCache . deferArchiveUntil ) return ;
uiCache . deferArchiveUntil = 0 ;
uiCache . archiveScheduled = false ;
const scrollState = lineageCaptureArchiveScroll ( ) ;
const familyVersion = world . familyVersion || 0 ;
const familyDirty = Boolean ( world . familyTreeDirty ) ;
if ( uiCache . archiveRenderRunning ) {
if ( familyDirty ) uiCache . archivePendingDirtyAfterRun = true ;
return ;
}
if ( uiCache . archiveVersion && uiCache . archiveFamilyVersion === familyVersion && ! familyDirty ) return ;
if ( uiCache . archiveVersion && ! lineageArchiveNearViewport ( ) ) {
uiCache . archivePendingOffscreen = true ;
const hiddenInterval = 30000 ;
const hiddenNow = performance . now ( ) ;
if ( hiddenNow - ( uiCache . archiveLastHiddenCheckAt || - Infinity ) < hiddenInterval ) return ;
uiCache . archiveLastHiddenCheckAt = hiddenNow ;
}
const now = performance . now ( ) ;
const minUpdateGap = 4800 ;
if ( uiCache . archiveVersion && ! uiCache . archiveRenderForce && now - ( uiCache . archiveLastRenderedAt || 0 ) < minUpdateGap ) {
if ( ! uiCache . archiveRenderTimer ) {
uiCache . archiveRenderTimer = setTimeout ( ( ) => {
uiCache . archiveRenderTimer = 0 ;
uiCache . archiveRenderForce = true ;
renderArchive ( ) ;
uiCache . archiveRenderForce = false ;
} , Math . max ( 80 , minUpdateGap - ( now - ( uiCache . archiveLastRenderedAt || 0 ) ) ) ) ;
}
return ;
}
if ( world . familyPrunePending && world . pruneExtinctFamilies ) {
world . familyPrunePending = false ;
world . pruneExtinctFamilies ( ) ;
}
const family = world . family || { } ;
const version = String ( world . familyVersion || 0 ) ;
if ( uiCache . archiveVersion === version ) {
world . familyTreeDirty = false ;
uiCache . archivePendingDirtyAfterRun = false ;
return ;
}
const renderStarted = performance . now ( ) ;
lineageSetArchiveStaleStatus ( false ) ;
if ( uiCache . archiveRenderTimer ) {
clearTimeout ( uiCache . archiveRenderTimer ) ;
uiCache . archiveRenderTimer = 0 ;
}
uiCache . archiveVersion = version ;
uiCache . archiveFamilyVersion = world . familyVersion || 0 ;
uiCache . archiveLastScheduleFamilyVersion = world . familyVersion || 0 ;
uiCache . archiveLastRenderedAt = performance . now ( ) ;
uiCache . lastArchiveWindow = "" ;
if ( ! uiCache . archiveHtmlCache ) uiCache . archiveHtmlCache = new Map ( ) ;
const activeCacheKeys = new Set ( ) ;
const token = ` ${ version } : ${ Math . random ( ) . toString ( 36 ) . slice ( 2 ) } ` ;
uiCache . archiveRenderToken = token ;
uiCache . archiveRenderRunning = true ;
ui . archiveContent . innerHTML = ` <div class="lineage-render-status"> \u 5bb6 \u 7cfb \u 56f3 \u 3092 \u 6e96 \u 5099 \u 4e2d \u 2026</div><div class="lineage-forest"></div> ` ;
lineageRestoreArchiveScroll ( scrollState ) ;
const status = ui . archiveContent . querySelector ( ".lineage-render-status" ) ;
const forest = ui . archiveContent . querySelector ( ".lineage-forest" ) ;
const renderComponents = ( components ) => {
if ( uiCache . archiveRenderToken !== token || ! forest ) {
uiCache . archiveRenderRunning = false ;
return ;
}
const splitComponents = components . flatMap ( lineageSplitByActualEdges ) . filter ( c => c . length ) ;
uiCache . archiveRows = splitComponents ;
if ( ! splitComponents . length ) {
ui . archiveContent . innerHTML = ` <div class="selected-info empty"> \u 751f \u 304d \u 3066 \u 3044 \u 308b \u 69cb \u 6210 \u 54e1 \u 3092 \u 6301 \u 3064 \u 5bb6 \u 7cfb \u 306f \u 307e \u 3060 \u 3042 \u 308a \u 307e \u 305b \u 3093 \u 3002</div> ` ;
lineageRestoreArchiveScroll ( scrollState ) ;
uiCache . archiveRenderRunning = false ;
world . familyTreeDirty = false ;
uiCache . archivePendingDirtyAfterRun = false ;
return ;
}
if ( status ) status . textContent = ` \u 5bb6 \u 7cfb \u 56f3 \u 3092 \u 63cf \u 753b \u 4e2d \u 2026 0 / ${ splitComponents . length } ` ;
let index = 0 ;
const pump = ( ) => {
if ( uiCache . archiveRenderToken !== token || ! forest ) {
uiCache . archiveRenderRunning = false ;
return ;
}
const started = performance . now ( ) ;
let nodeBudget = 28 ;
let renderedThisSlice = 0 ;
while ( index < splitComponents . length ) {
const component = splitComponents [ index ] ;
if ( renderedThisSlice > 0 && component . length > nodeBudget ) break ;
const signature = lineageComponentSignature ( component , family ) ;
activeCacheKeys . add ( lineageFamilyCacheKey ( component , family , signature ) ) ;
forest . insertAdjacentHTML ( "beforeend" , lineageFamilyHtml ( component , index , family , signature ) ) ;
nodeBudget -= Math . max ( 1 , component . length ) ;
index += 1 ;
renderedThisSlice += 1 ;
if ( index < splitComponents . length && ( nodeBudget <= 0 || performance . now ( ) - started > 6 ) ) break ;
}
lineageRestoreArchiveScroll ( scrollState ) ;
if ( status ) status . textContent = ` \u 5bb6 \u 7cfb \u 56f3 \u 3092 \u 63cf \u 753b \u 4e2d \u 2026 ${ index } / ${ splitComponents . length } ` ;
if ( index < splitComponents . length ) {
lineageIdleSchedule ( pump , 220 ) ;
} else {
if ( status ) status . remove ( ) ;
for ( const key of Array . from ( uiCache . archiveHtmlCache . keys ( ) ) ) {
if ( ! activeCacheKeys . has ( key ) ) uiCache . archiveHtmlCache . delete ( key ) ;
}
lineageRestoreArchiveScroll ( scrollState ) ;
const renderDirtiedAgain = uiCache . archivePendingDirtyAfterRun || ( world . familyVersion || 0 ) !== familyVersion ;
uiCache . archiveRenderRunning = false ;
world . familyTreeDirty = renderDirtiedAgain ;
if ( uiCache . archiveDiagnostics ) {
uiCache . archiveDiagnostics . renders = ( uiCache . archiveDiagnostics . renders || 0 ) + 1 ;
uiCache . archiveDiagnostics . lastMs = Math . round ( ( performance . now ( ) - renderStarted ) * 10 ) / 10 ;
uiCache . archiveDiagnostics . lastFamilies = splitComponents . length ;
uiCache . archiveDiagnostics . lastNodes = splitComponents . reduce ( ( sum , component ) => sum + component . length , 0 ) ;
}
if ( renderDirtiedAgain ) {
uiCache . archivePendingDirtyAfterRun = false ;
scheduleArchiveWindowRender ( ) ;
}
}
} ;
lineageIdleSchedule ( pump , 160 ) ;
} ;
lineageBuildComponentsIdle (
family ,
token ,
( label , done , total ) => {
if ( status ) status . textContent = ` ${ label } \u 2026 ${ Math . min ( done || 0 , total || 0 ) } / ${ total || 0 } ` ;
} ,
renderComponents
) ;
}
function resetArchiveRenderState ( ) {
if ( uiCache . archiveRenderTimer ) {
clearTimeout ( uiCache . archiveRenderTimer ) ;
uiCache . archiveRenderTimer = 0 ;
}
uiCache . archiveRenderToken = "" ;
uiCache . archiveRenderRunning = false ;
uiCache . archiveScheduled = false ;
uiCache . archivePendingOffscreen = false ;
uiCache . archivePendingWhileRunning = false ;
uiCache . archivePendingDirtyAfterRun = false ;
uiCache . archiveLastHiddenCheckAt = - Infinity ;
uiCache . archiveLastScheduleFamilyVersion = null ;
uiCache . archiveVersion = "" ;
uiCache . archiveFamilyVersion = null ;
uiCache . archiveRelationSignature = "" ;
uiCache . archiveRows = [ ] ;
uiCache . lastArchiveWindow = "" ;
uiCache . archiveHtmlCache ? . clear ? . ( ) ;
}
function validateFamilyTree ( ) {
const issues = world . validateFamily ? [ ... world . validateFamily ( ) ] : [ ] ;
const rendered = new Map ( ) ;
document . querySelectorAll ( "[data-family-tarinai-id]" ) . forEach ( node => {
const id = node . getAttribute ( "data-family-tarinai-id" ) ;
rendered . set ( id , ( rendered . get ( id ) || 0 ) + 1 ) ;
} ) ;
for ( const [ id , count ] of rendered . entries ( ) ) {
if ( count > 1 ) issues . push ( { type : "duplicate-render-node" , id , count } ) ;
}
for ( const component of uiCache . archiveRows || [ ] ) {
for ( const n of component || [ ] ) {
if ( n ? . id && ! rendered . has ( n . id ) ) issues . push ( { type : "missing-render-node" , id : n . id } ) ;
}
}
document . querySelectorAll ( ".lineage-family-tree[data-lineage-child-edges]" ) . forEach ( ( section , index ) => {
const expectedEdges = Number ( section . getAttribute ( "data-lineage-child-edges" ) || 0 ) ;
const renderedEdges = Number ( section . getAttribute ( "data-lineage-rendered-child-edges" ) || 0 ) ;
const expectedGroups = Number ( section . getAttribute ( "data-lineage-child-groups" ) || 0 ) ;
const renderedPaths = Number ( section . getAttribute ( "data-lineage-child-paths" ) || 0 ) ;
const pathCount = section . querySelectorAll ( ".lineage-links .child-link" ) . length ;
if ( renderedEdges !== expectedEdges ) {
issues . push ( { type : "render-child-edge-count-mismatch" , familyIndex : index , expectedEdges , renderedEdges } ) ;
}
if ( expectedEdges > 0 && pathCount < 1 ) {
issues . push ( { type : "missing-render-child-link" , familyIndex : index , expectedEdges , pathCount } ) ;
}
if ( expectedGroups > 0 && pathCount < expectedGroups ) {
issues . push ( { type : "missing-render-child-link-group" , familyIndex : index , expectedGroups , pathCount } ) ;
}
if ( renderedPaths !== pathCount ) {
issues . push ( { type : "render-child-path-count-mismatch" , familyIndex : index , renderedPaths , pathCount } ) ;
}
} ) ;
return issues ;
}
function scheduleArchiveWindowRender ( ) {
if ( typeof archiveAutoUpdateEnabled === "function" && ! archiveAutoUpdateEnabled ( ) ) {
uiCache . archiveScheduled = false ;
if ( world . familyTreeDirty ) lineageSetArchiveStaleStatus ( true ) ;
return ;
}
const familyDirty = Boolean ( world . familyTreeDirty ) ;
if ( uiCache . archiveVersion && uiCache . archiveFamilyVersion === ( world . familyVersion || 0 ) && ! familyDirty ) return ;
if ( uiCache . archiveRenderRunning ) {
if ( familyDirty ) uiCache . archivePendingDirtyAfterRun = true ;
return ;
}
if ( uiCache . archiveVersion && ! lineageArchiveNearViewport ( ) ) {
uiCache . archivePendingOffscreen = true ;
const now = performance . now ( ) ;
if ( now - ( uiCache . archiveLastHiddenCheckAt || - Infinity ) < 30000 ) return ;
uiCache . archiveLastHiddenCheckAt = now ;
}
if ( uiCache . archiveRenderTimer ) return ;
if ( uiCache . archiveScheduled ) return ;
uiCache . archiveScheduled = true ;
const schedule = window . requestIdleCallback || ( ( fn ) => setTimeout ( fn , 0 ) ) ;
schedule ( ( ) => {
renderArchive ( ) ;
} , { timeout : 1200 } ) ;
}
function schedulePendingArchiveRender ( ) {
if ( typeof archiveAutoUpdateEnabled === "function" && ! archiveAutoUpdateEnabled ( ) ) return ;
if ( ! uiCache . archivePendingOffscreen ) return ;
if ( ! lineageArchiveNearViewport ( ) ) return ;
uiCache . archivePendingOffscreen = false ;
scheduleArchiveWindowRender ( ) ;
}
window . validateFamilyTree = validateFamilyTree ;
window . familyTreeDiagnostics = ( ) => ( { ... ( uiCache . archiveDiagnostics || { } ) } ) ;
window . resetArchiveRenderState = resetArchiveRenderState ;