miro0o_miniWorldMap/tests/worldMap.test.ts
2026-06-24 22:31:25 +02:00

880 lines
44 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { DEFAULT_RADIAL_SETTINGS } from '../src/settings';
import { layoutRadialGraph, type RadialLayout } from '../src/layout/radial/layoutRadial';
import { buildWorldMap } from '../src/world/buildWorldMap';
import { ROOT_ID, type VisibleWorldGraph } from '../src/world/types';
import { buildVisibleWorldGraph, defaultVisibleGraphState } from '../src/world/visibleGraph';
const records = [
{ path: 'Atlas', basename: 'Atlas', kind: 'folder' as const },
{ path: 'Atlas/Atlas.md', basename: 'Atlas', kind: 'note' as const },
{ path: 'Atlas/Topic A.md', basename: 'Topic A', kind: 'note' as const },
{ path: 'Atlas/Sub', basename: 'Sub', kind: 'folder' as const },
{ path: 'Atlas/Sub/Topic B.md', basename: 'Topic B', kind: 'note' as const },
{ path: 'Outside/Other.md', basename: 'Other', kind: 'note' as const },
{ path: '.obsidian/Hidden.md', basename: 'Hidden', kind: 'note' as const },
];
function model(overrides: Partial<typeof DEFAULT_RADIAL_SETTINGS> = {}) {
const settings = { ...DEFAULT_RADIAL_SETTINGS, ignoreFolders: [...DEFAULT_RADIAL_SETTINGS.ignoreFolders, '.obsidian'], ...overrides };
return buildWorldMap(
records,
{
'Atlas/Topic A.md': { 'Atlas/Sub/Topic B.md': 2, 'Outside/Other.md': 1 },
'Outside/Other.md': { 'Atlas/Topic A.md': 1 },
},
{
'Atlas/Topic A.md': { Missing: 1 },
},
settings,
);
}
function angleDelta(a: number, b: number): number {
const full = Math.PI * 2;
const delta = ((a - b + Math.PI) % full + full) % full - Math.PI;
return Math.abs(delta);
}
function signedAngleDelta(a: number, b: number): number {
const full = Math.PI * 2;
return ((a - b + Math.PI) % full + full) % full - Math.PI;
}
function angleSpreadAround(center: number, angles: number[]): number {
const deltas = angles.map((angle) => signedAngleDelta(angle, center));
return Math.max(...deltas) - Math.min(...deltas);
}
function hierarchyRouteCrossings(graph: VisibleWorldGraph, layout: RadialLayout): number {
const segments: {
edge: VisibleWorldGraph['hierarchyEdges'][number];
source: { x: number; y: number };
target: { x: number; y: number };
}[] = [];
for (const edge of graph.hierarchyEdges) {
const source = layout.positions.get(edge.source);
const target = layout.positions.get(edge.target);
if (source && target) segments.push({ edge, source, target });
}
let crossings = 0;
for (let i = 0; i < segments.length; i++) {
for (let j = i + 1; j < segments.length; j++) {
const a = segments[i]!;
const b = segments[j]!;
if (a.edge.source === b.edge.source || a.edge.source === b.edge.target || a.edge.target === b.edge.source || a.edge.target === b.edge.target) continue;
if (segmentsIntersect(a.source, a.target, b.source, b.target)) crossings++;
}
}
return crossings;
}
function segmentsIntersect(
a: { x: number; y: number },
b: { x: number; y: number },
c: { x: number; y: number },
d: { x: number; y: number },
): boolean {
const abC = cross(a, b, c);
const abD = cross(a, b, d);
const cdA = cross(c, d, a);
const cdB = cross(c, d, b);
return abC * abD < -0.000001 && cdA * cdB < -0.000001;
}
function cross(a: { x: number; y: number }, b: { x: number; y: number }, c: { x: number; y: number }): number {
return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
}
describe('world-map model', () => {
it('indexes folders, notes, representatives, unresolved links, and ignored folders', () => {
const m = model();
expect(m.nodes.has(ROOT_ID)).toBe(true);
expect(m.nodes.has('Atlas')).toBe(true);
expect(m.nodes.has('Atlas/Topic A.md')).toBe(true);
expect(m.nodes.has('.obsidian/Hidden.md')).toBe(false);
expect(m.nodes.get('Atlas')?.representativeFile).toBe('Atlas/Atlas.md');
expect(m.nodes.get('Atlas/Atlas.md')?.isRepresentativeFile).toBe(true);
expect([...m.nodes.values()].filter((node) => node.type === 'unresolved')).toHaveLength(1);
expect(m.stats.notes).toBe(4);
});
it('can omit unresolved nodes', () => {
const m = model({ includeUnresolvedLinks: false });
expect([...m.nodes.values()].filter((node) => node.type === 'unresolved')).toHaveLength(0);
});
});
describe('visible world graph', () => {
it('builds an atlas graph with hierarchy edges and representative folding', () => {
const m = model();
const state = defaultVisibleGraphState(DEFAULT_RADIAL_SETTINGS);
state.rootPath = 'Atlas';
state.showLinkOverlay = true;
const graph = buildVisibleWorldGraph(m, state, DEFAULT_RADIAL_SETTINGS);
expect(graph.rootId).toBe('Atlas');
expect(graph.nodesById.has('Atlas/Atlas.md')).toBe(false);
expect(graph.hierarchyEdges.length).toBeGreaterThan(0);
expect(graph.linkEdges.length).toBeGreaterThan(0);
});
it('creates grouped and exact outside-root proxies', () => {
const m = model();
const state = defaultVisibleGraphState(DEFAULT_RADIAL_SETTINGS);
state.rootPath = 'Atlas';
state.showLinkOverlay = true;
state.externalDetailMode = 'grouped';
const grouped = buildVisibleWorldGraph(m, state, DEFAULT_RADIAL_SETTINGS);
expect(grouped.nodes.some((node) => node.type === 'external' && !node.externalProxy)).toBe(true);
expect(grouped.nodes.some((node) => node.externalProxy)).toBe(false);
state.externalDetailMode = 'exact';
const exact = buildVisibleWorldGraph(m, state, DEFAULT_RADIAL_SETTINGS);
expect(exact.nodes.some((node) => node.externalProxy)).toBe(true);
});
it('keeps hover links available when the visible link overlay is off', () => {
const m = model();
const state = defaultVisibleGraphState(DEFAULT_RADIAL_SETTINGS);
state.rootPath = 'Atlas';
state.showLinkOverlay = false;
state.showExternalLinks = false;
state.hoverHighlightMode = 'note-links';
const graph = buildVisibleWorldGraph(m, state, DEFAULT_RADIAL_SETTINGS);
expect(graph.linkEdges).toHaveLength(0);
expect(graph.hoverLinkEdges.length).toBeGreaterThan(0);
});
it('builds a focus graph around the active note', () => {
const m = model();
const state = defaultVisibleGraphState(DEFAULT_RADIAL_SETTINGS);
state.mode = 'focus';
state.focusPath = 'Atlas/Topic A.md';
state.showLinkOverlay = true;
const graph = buildVisibleWorldGraph(m, state, DEFAULT_RADIAL_SETTINGS);
expect(graph.focusId).toBe('Atlas/Topic A.md');
expect(graph.nodesById.has('Atlas/Sub/Topic B.md')).toBe(true);
});
});
describe('radial layout', () => {
it('keeps the root centered and ring depths ordered', () => {
const m = model();
const state = defaultVisibleGraphState(DEFAULT_RADIAL_SETTINGS);
state.rootPath = 'Atlas';
state.showLinkOverlay = true;
const graph = buildVisibleWorldGraph(m, state, DEFAULT_RADIAL_SETTINGS);
const layout = layoutRadialGraph(graph, { ringSpacing: 960, nodeSpacing: 126, swirlStrength: 0 });
const root = layout.positions.get(graph.rootId);
expect(root?.x).toBeCloseTo(layout.centerX);
expect(root?.y).toBeCloseTo(layout.centerY);
expect([...layout.positions.values()].every((point) => Number.isFinite(point.x) && Number.isFinite(point.y))).toBe(true);
const radii = layout.rings.map((ring) => ring.radius);
expect(radii).toEqual([...radii].sort((a, b) => a - b));
});
it('keeps folder descendants outside their parent folder rings', () => {
const m = model();
const state = defaultVisibleGraphState(DEFAULT_RADIAL_SETTINGS);
state.rootPath = ROOT_ID;
state.showLinkOverlay = false;
const graph = buildVisibleWorldGraph(m, state, DEFAULT_RADIAL_SETTINGS);
const layout = layoutRadialGraph(graph, { ringSpacing: 960, nodeSpacing: 126, swirlStrength: 0 });
const root = layout.positions.get(ROOT_ID);
const atlas = layout.positions.get('Atlas');
const sub = layout.positions.get('Atlas/Sub');
const topic = layout.positions.get('Atlas/Sub/Topic B.md');
expect(root?.radius).toBeCloseTo(0);
expect(atlas?.radius).toBeGreaterThan(root?.radius ?? -1);
expect(sub?.radius).toBeGreaterThan(atlas?.radius ?? -1);
expect(topic?.radius).toBeGreaterThan(sub?.radius ?? -1);
expect(atlas?.depth).toBe(1);
expect(sub?.depth).toBe(2);
expect(topic?.depth).toBe(3);
});
it('keeps descendant branches approximately aligned with their parents', () => {
const branchRecords = [
{ path: 'Alpha', basename: 'Alpha', kind: 'folder' as const },
{ path: 'Alpha/One.md', basename: 'One', kind: 'note' as const },
{ path: 'Alpha/Two.md', basename: 'Two', kind: 'note' as const },
{ path: 'Alpha/Sub', basename: 'Sub', kind: 'folder' as const },
{ path: 'Alpha/Sub/Deep.md', basename: 'Deep', kind: 'note' as const },
{ path: 'Beta', basename: 'Beta', kind: 'folder' as const },
{ path: 'Beta/One.md', basename: 'One', kind: 'note' as const },
{ path: 'Beta/Two.md', basename: 'Two', kind: 'note' as const },
{ path: 'Gamma', basename: 'Gamma', kind: 'folder' as const },
{ path: 'Gamma/One.md', basename: 'One', kind: 'note' as const },
];
const m = buildWorldMap(branchRecords, {}, {}, DEFAULT_RADIAL_SETTINGS);
const state = defaultVisibleGraphState(DEFAULT_RADIAL_SETTINGS);
state.showLinkOverlay = false;
const graph = buildVisibleWorldGraph(m, state, DEFAULT_RADIAL_SETTINGS);
const layout = layoutRadialGraph(graph, { ringSpacing: 960, nodeSpacing: 126, swirlStrength: 0 });
const alpha = layout.positions.get('Alpha');
const alphaSub = layout.positions.get('Alpha/Sub');
const deep = layout.positions.get('Alpha/Sub/Deep.md');
const beta = layout.positions.get('Beta');
const betaOne = layout.positions.get('Beta/One.md');
expect(angleDelta(alphaSub?.angle ?? 0, alpha?.angle ?? 0)).toBeLessThan(0.9);
expect(angleDelta(deep?.angle ?? 0, alphaSub?.angle ?? 0)).toBeLessThan(0.9);
expect(angleDelta(betaOne?.angle ?? 0, beta?.angle ?? 0)).toBeLessThan(0.9);
});
it('fans crowded siblings around the parent ray instead of stacking them on one angle', () => {
const childRecords = Array.from({ length: 28 }, (_, index) => ({
path: `Topic/Child ${index}.md`,
basename: `Child ${index}`,
kind: 'note' as const,
}));
const m = buildWorldMap(
[{ path: 'Topic', basename: 'Topic', kind: 'folder' as const }, ...childRecords],
{},
{},
DEFAULT_RADIAL_SETTINGS,
);
const state = defaultVisibleGraphState(DEFAULT_RADIAL_SETTINGS);
state.showLinkOverlay = false;
const graph = buildVisibleWorldGraph(m, state, DEFAULT_RADIAL_SETTINGS);
const layout = layoutRadialGraph(graph, { ringSpacing: 960, nodeSpacing: 126, swirlStrength: 0 });
const parent = layout.positions.get('Topic');
const childDeltas = childRecords.map((record) => signedAngleDelta(layout.positions.get(record.path)?.angle ?? 0, parent?.angle ?? 0));
const absoluteDeltas = childDeltas.map(Math.abs);
const roundedAngles = new Set(childRecords.map((record) => Math.round((layout.positions.get(record.path)?.angle ?? 0) * 100)));
expect(Math.max(...absoluteDeltas)).toBeLessThan(3.05);
expect(Math.max(...childDeltas) - Math.min(...childDeltas)).toBeGreaterThan(4.6);
expect(roundedAngles.size).toBeGreaterThan(18);
});
it('keeps a deep route from spiraling across unrelated angles', () => {
const records: { path: string; basename: string; kind: 'folder' | 'note' }[] = [{ path: 'Core', basename: 'Core', kind: 'folder' }];
for (let branch = 0; branch < 12; branch++) {
const branchPath = `Core/Branch ${branch}`;
records.push({ path: branchPath, basename: `Branch ${branch}`, kind: 'folder' as const });
for (let section = 0; section < 8; section++) {
const sectionPath = `${branchPath}/Section ${section}`;
records.push({ path: sectionPath, basename: `Section ${section}`, kind: 'folder' as const });
for (let topic = 0; topic < 6; topic++) {
const topicPath = `${sectionPath}/Topic ${topic}`;
records.push({ path: topicPath, basename: `Topic ${topic}`, kind: 'folder' as const });
records.push({ path: `${topicPath}/Leaf ${topic}.md`, basename: `Leaf ${topic}`, kind: 'note' as const });
}
}
}
const m = buildWorldMap(records, {}, {}, DEFAULT_RADIAL_SETTINGS);
const state = defaultVisibleGraphState(DEFAULT_RADIAL_SETTINGS);
state.showLinkOverlay = false;
state.nodeLimit = 1200;
const graph = buildVisibleWorldGraph(m, state, DEFAULT_RADIAL_SETTINGS);
const layout = layoutRadialGraph(graph, { ringSpacing: 960, nodeSpacing: 126, swirlStrength: 0 });
const route = [
'Core',
'Core/Branch 5',
'Core/Branch 5/Section 4',
'Core/Branch 5/Section 4/Topic 3',
'Core/Branch 5/Section 4/Topic 3/Leaf 3.md',
].map((id) => layout.positions.get(id)?.angle ?? 0);
const stepDeltas = route.slice(1).map((angle, index) => angleDelta(angle, route[index] ?? 0));
const branchAngles = Array.from({ length: 12 }, (_, index) => layout.positions.get(`Core/Branch ${index}`)?.angle ?? 0);
expect(angleSpreadAround(layout.positions.get('Core')?.angle ?? 0, branchAngles)).toBeGreaterThan(4.2);
expect(Math.max(...stepDeltas)).toBeLessThan(1.16);
expect(angleSpreadAround(route[0] ?? 0, route)).toBeLessThan(1.9);
});
it('keeps large same-depth subtrees spread across a controlled arc', () => {
const records: { path: string; basename: string; kind: 'folder' | 'note' }[] = [
{ path: 'Hub', basename: 'Hub', kind: 'folder' },
{ path: 'Hub/Big', basename: 'Big', kind: 'folder' },
{ path: 'Hub/Small A', basename: 'Small A', kind: 'folder' },
{ path: 'Hub/Small B', basename: 'Small B', kind: 'folder' },
{ path: 'Hub/Small C', basename: 'Small C', kind: 'folder' },
{ path: 'Hub/Small A/Leaf.md', basename: 'Leaf', kind: 'note' },
{ path: 'Hub/Small B/Leaf.md', basename: 'Leaf', kind: 'note' },
{ path: 'Hub/Small C/Leaf.md', basename: 'Leaf', kind: 'note' },
];
for (let index = 0; index < 18; index++) {
records.push({ path: `Hub/Big/Topic ${index}.md`, basename: `Topic ${index}`, kind: 'note' });
}
const m = buildWorldMap(records, {}, {}, DEFAULT_RADIAL_SETTINGS);
const state = defaultVisibleGraphState(DEFAULT_RADIAL_SETTINGS);
state.showLinkOverlay = false;
const graph = buildVisibleWorldGraph(m, state, DEFAULT_RADIAL_SETTINGS);
const layout = layoutRadialGraph(graph, { ringSpacing: 960, nodeSpacing: 126, swirlStrength: 0 });
const big = layout.positions.get('Hub/Big');
const bigChildAngles = Array.from({ length: 18 }, (_, index) => layout.positions.get(`Hub/Big/Topic ${index}.md`)?.angle ?? 0);
const spread = angleSpreadAround(big?.angle ?? 0, bigChildAngles);
expect(spread).toBeGreaterThan(0.55);
expect(spread).toBeLessThan(1.4);
});
it('spends extra radius on deep hierarchy bands while preserving inner ring spacing', () => {
const records: { path: string; basename: string; kind: 'folder' | 'note' }[] = [{ path: 'Root', basename: 'Root', kind: 'folder' }];
for (let branch = 0; branch < 10; branch++) {
const branchPath = `Root/Branch ${branch}`;
records.push({ path: branchPath, basename: `Branch ${branch}`, kind: 'folder' });
for (let depth = 0; depth < 5; depth++) {
const path = `${branchPath}/${Array.from({ length: depth + 1 }, (_, index) => `Layer ${index}`).join('/')}`;
records.push({ path, basename: `Layer ${depth}`, kind: 'folder' });
records.push({ path: `${path}/Leaf ${branch}-${depth}.md`, basename: `Leaf ${branch}-${depth}`, kind: 'note' });
}
}
const m = buildWorldMap(records, {}, {}, DEFAULT_RADIAL_SETTINGS);
const state = defaultVisibleGraphState(DEFAULT_RADIAL_SETTINGS);
state.showLinkOverlay = false;
state.nodeLimit = 1200;
const graph = buildVisibleWorldGraph(m, state, DEFAULT_RADIAL_SETTINGS);
const layout = layoutRadialGraph(graph, { ringSpacing: 960, nodeSpacing: 126, swirlStrength: 0 });
const hierarchyRings = layout.rings.filter((ring) => ring.depth > 0).sort((a, b) => a.depth - b.depth);
const gaps = hierarchyRings.slice(1).map((ring, index) => ring.radius - (hierarchyRings[index]?.radius ?? 0));
const averageGap = ((hierarchyRings[hierarchyRings.length - 1]?.radius ?? 0) - (hierarchyRings[0]?.radius ?? 0)) / Math.max(1, hierarchyRings.length - 1);
const innerGaps = gaps.slice(0, 1);
const middleGaps = gaps.slice(1, 4);
const outerGaps = gaps.slice(-3);
const averageOuterGap = outerGaps.reduce((sum, gap) => sum + gap, 0) / Math.max(1, outerGaps.length);
expect(layout.ringSpacing).toBeLessThan(900);
expect(Math.min(...gaps)).toBeGreaterThan(layout.ringSpacing * 0.78);
expect(Math.max(...innerGaps)).toBeLessThan(layout.ringSpacing * 1.55);
expect(Math.max(...middleGaps)).toBeGreaterThan(layout.ringSpacing * 1.55);
expect(averageGap).toBeGreaterThan(layout.ringSpacing * 1.55);
expect(averageOuterGap).toBeGreaterThan(layout.ringSpacing * 1.95);
});
it('gives crowded middle rings extra radial room on large maps', () => {
const records: { path: string; basename: string; kind: 'folder' | 'note' }[] = [{ path: 'Topic', basename: 'Topic', kind: 'folder' }];
for (let branch = 0; branch < 14; branch++) {
const branchPath = `Topic/Branch ${branch}`;
records.push({ path: branchPath, basename: `Branch ${branch}`, kind: 'folder' });
for (let group = 0; group < 14; group++) {
const groupPath = `${branchPath}/Group ${group}`;
const hubPath = `${groupPath}/Hub`;
records.push({ path: groupPath, basename: `Group ${group}`, kind: 'folder' });
records.push({ path: hubPath, basename: 'Hub', kind: 'folder' });
records.push({ path: `${hubPath}/Leaf.md`, basename: 'Leaf', kind: 'note' });
}
}
const m = buildWorldMap(records, {}, {}, DEFAULT_RADIAL_SETTINGS);
const state = defaultVisibleGraphState(DEFAULT_RADIAL_SETTINGS);
state.rootPath = 'Topic';
state.showLinkOverlay = false;
state.nodeLimit = 1200;
const graph = buildVisibleWorldGraph(m, state, DEFAULT_RADIAL_SETTINGS);
const layout = layoutRadialGraph(graph, { ringSpacing: 960, nodeSpacing: 126, swirlStrength: 0 });
const ringsByDepth = new Map(layout.rings.map((ring) => [ring.depth, ring.radius]));
const firstGap = (ringsByDepth.get(1) ?? 0) - (ringsByDepth.get(0) ?? 0);
const middleGap = (ringsByDepth.get(2) ?? 0) - (ringsByDepth.get(1) ?? 0);
const laterMiddleGap = (ringsByDepth.get(3) ?? 0) - (ringsByDepth.get(2) ?? 0);
expect(firstGap).toBeGreaterThan(layout.ringSpacing * 0.9);
expect(middleGap).toBeGreaterThan(layout.ringSpacing * 1.35);
expect(laterMiddleGap).toBeGreaterThan(layout.ringSpacing * 1.35);
});
it('keeps crowded baseline rings dominant while sparse deep tails form radial bands', () => {
const records: { path: string; basename: string; kind: 'folder' | 'note' }[] = [];
for (let index = 0; index < 180; index++) {
records.push({ path: `Entry ${index}.md`, basename: `Entry ${index}`, kind: 'note' });
}
let deepPath = 'Deep';
records.push({ path: deepPath, basename: 'Deep', kind: 'folder' });
for (let depth = 0; depth < 8; depth++) {
deepPath = `${deepPath}/Layer ${depth}`;
records.push({ path: deepPath, basename: `Layer ${depth}`, kind: 'folder' });
}
const m = buildWorldMap(records, {}, {}, DEFAULT_RADIAL_SETTINGS);
const state = defaultVisibleGraphState(DEFAULT_RADIAL_SETTINGS);
state.showLinkOverlay = false;
state.atlasDepth = 20;
state.nodeLimit = 1000;
const graph = buildVisibleWorldGraph(m, state, DEFAULT_RADIAL_SETTINGS);
const layout = layoutRadialGraph(graph, { ringSpacing: 960, nodeSpacing: 126, swirlStrength: 0 });
const ringsByDepth = new Map(layout.rings.map((ring) => [ring.depth, ring.radius]));
const sparseGaps = [4, 5, 6, 7, 8]
.map((depth) => (ringsByDepth.get(depth) ?? 0) - (ringsByDepth.get(depth - 1) ?? 0))
.filter((gap) => gap > 0);
const averageSparseGap = sparseGaps.reduce((sum, gap) => sum + gap, 0) / Math.max(1, sparseGaps.length);
const lateSparseGaps = [7, 8, 9]
.map((depth) => (ringsByDepth.get(depth) ?? 0) - (ringsByDepth.get(depth - 1) ?? 0))
.filter((gap) => gap > 0);
const averageLateSparseGap = lateSparseGaps.reduce((sum, gap) => sum + gap, 0) / Math.max(1, lateSparseGaps.length);
expect(ringsByDepth.get(1) ?? 0).toBeGreaterThan(averageSparseGap * 3);
expect(averageSparseGap).toBeGreaterThan(layout.ringSpacing * 1.2);
expect(averageLateSparseGap).toBeGreaterThan(averageSparseGap * 1.16);
});
it('allocates more root angle to dense early subtrees', () => {
const records: { path: string; basename: string; kind: 'folder' | 'note' }[] = [
{ path: 'Root', basename: 'Root', kind: 'folder' },
{ path: 'Root/Dense', basename: 'Dense', kind: 'folder' },
];
for (let group = 0; group < 14; group++) {
const groupPath = `Root/Dense/Group ${group}`;
records.push({ path: groupPath, basename: `Group ${group}`, kind: 'folder' });
for (let leaf = 0; leaf < 6; leaf++) {
records.push({ path: `${groupPath}/Leaf ${leaf}.md`, basename: `Leaf ${leaf}`, kind: 'note' });
}
}
for (let index = 0; index < 7; index++) {
const sparsePath = `Root/Sparse ${index}`;
records.push({ path: sparsePath, basename: `Sparse ${index}`, kind: 'folder' });
records.push({ path: `${sparsePath}/Note.md`, basename: 'Note', kind: 'note' });
}
const m = buildWorldMap(records, {}, {}, DEFAULT_RADIAL_SETTINGS);
const state = defaultVisibleGraphState(DEFAULT_RADIAL_SETTINGS);
state.rootPath = 'Root';
state.showLinkOverlay = false;
state.nodeLimit = 1200;
const graph = buildVisibleWorldGraph(m, state, DEFAULT_RADIAL_SETTINGS);
const layout = layoutRadialGraph(graph, { ringSpacing: 960, nodeSpacing: 126, swirlStrength: 0 });
const denseSpan = layout.positions.get('Root/Dense')?.sectorSpan ?? 0;
const denseAngle = layout.positions.get('Root/Dense')?.angle ?? 0;
const denseGroupAngles = Array.from({ length: 14 }, (_, index) => layout.positions.get(`Root/Dense/Group ${index}`)?.angle ?? denseAngle);
const denseGroupSpread = angleSpreadAround(denseAngle, denseGroupAngles);
const sparseSpans = Array.from({ length: 7 }, (_, index) => layout.positions.get(`Root/Sparse ${index}`)?.sectorSpan ?? 0);
const averageSparseSpan = sparseSpans.reduce((sum, span) => sum + span, 0) / Math.max(1, sparseSpans.length);
expect(denseSpan).toBeGreaterThan(Math.PI);
expect(denseSpan).toBeGreaterThan(averageSparseSpan * 24);
expect(denseGroupSpread).toBeGreaterThan(Math.min(denseSpan * 0.62, Math.PI * 1.25));
expect(hierarchyRouteCrossings(graph, layout)).toBe(0);
});
it('keeps a crowded lower perimeter expanded when one branch continues deeper', () => {
const records: { path: string; basename: string; kind: 'folder' | 'note' }[] = [{ path: 'Map', basename: 'Map', kind: 'folder' }];
for (let area = 0; area < 18; area++) {
const areaPath = `Map/Area ${area}`;
const shelfPath = `${areaPath}/Shelf`;
const clusterPath = `${shelfPath}/Cluster`;
records.push({ path: areaPath, basename: `Area ${area}`, kind: 'folder' });
records.push({ path: shelfPath, basename: 'Shelf', kind: 'folder' });
records.push({ path: clusterPath, basename: 'Cluster', kind: 'folder' });
for (let leaf = 0; leaf < 5; leaf++) {
records.push({ path: `${clusterPath}/Leaf ${leaf}.md`, basename: `Leaf ${leaf}`, kind: 'note' });
}
}
let deepPath = 'Map/Deep';
records.push({ path: deepPath, basename: 'Deep', kind: 'folder' });
const routeIds = [deepPath];
for (let depth = 0; depth < 8; depth++) {
deepPath = `${deepPath}/Layer ${depth}`;
records.push({ path: deepPath, basename: `Layer ${depth}`, kind: 'folder' });
routeIds.push(deepPath);
}
const m = buildWorldMap(records, {}, {}, DEFAULT_RADIAL_SETTINGS);
const state = defaultVisibleGraphState(DEFAULT_RADIAL_SETTINGS);
state.rootPath = 'Map';
state.showLinkOverlay = false;
state.atlasDepth = 20;
state.nodeLimit = 2500;
const graph = buildVisibleWorldGraph(m, state, DEFAULT_RADIAL_SETTINGS);
const layout = layoutRadialGraph(graph, { ringSpacing: 960, nodeSpacing: 126, swirlStrength: 0 });
const ringsByDepth = new Map(layout.rings.map((ring) => [ring.depth, ring.radius]));
const lowerPerimeterGap = (ringsByDepth.get(4) ?? 0) - (ringsByDepth.get(3) ?? 0);
const previousGap = (ringsByDepth.get(3) ?? 0) - (ringsByDepth.get(2) ?? 0);
const routeRadii = routeIds.map((id) => layout.positions.get(id)?.radius ?? 0);
const routeAngles = routeIds.map((id) => layout.positions.get(id)?.angle ?? 0);
const angleSteps = routeAngles.slice(1).map((angle, index) => angleDelta(angle, routeAngles[index] ?? 0));
expect(lowerPerimeterGap).toBeGreaterThan(previousGap * 1.18);
expect(lowerPerimeterGap).toBeGreaterThan(layout.ringSpacing * 1.45);
expect((routeRadii[routeRadii.length - 1] ?? 0) - (ringsByDepth.get(4) ?? 0)).toBeLessThan(layout.ringSpacing * 18);
expect(routeRadii).toEqual([...routeRadii].sort((a, b) => a - b));
expect(Math.max(...angleSteps)).toBeLessThan(0.36);
expect(hierarchyRouteCrossings(graph, layout)).toBe(0);
});
it('keeps narrow deep routes growing outward on sparse outer rings', () => {
const records: { path: string; basename: string; kind: 'folder' | 'note' }[] = [];
for (let index = 0; index < 160; index++) {
records.push({ path: `Crowd ${index}.md`, basename: `Crowd ${index}`, kind: 'note' });
}
let path = 'Atlas';
records.push({ path, basename: 'Atlas', kind: 'folder' });
const routeIds = [path];
for (let depth = 0; depth < 9; depth++) {
path = `${path}/Topic ${depth}`;
records.push({ path, basename: `Topic ${depth}`, kind: 'folder' });
routeIds.push(path);
}
const m = buildWorldMap(records, {}, {}, DEFAULT_RADIAL_SETTINGS);
const state = defaultVisibleGraphState(DEFAULT_RADIAL_SETTINGS);
state.showLinkOverlay = false;
state.atlasDepth = 20;
state.nodeLimit = 1000;
const graph = buildVisibleWorldGraph(m, state, DEFAULT_RADIAL_SETTINGS);
const layout = layoutRadialGraph(graph, { ringSpacing: 960, nodeSpacing: 126, swirlStrength: 0 });
const routeRadii = routeIds.map((id) => layout.positions.get(id)?.radius ?? 0);
const routeAngles = routeIds.map((id) => layout.positions.get(id)?.angle ?? 0);
const outerSteps = routeRadii.slice(3).map((radius, index) => radius - (routeRadii[index + 2] ?? 0));
const angleSteps = routeAngles.slice(1).map((angle, index) => angleDelta(angle, routeAngles[index] ?? 0));
expect(Math.min(...outerSteps)).toBeGreaterThan(layout.ringSpacing * 0.36);
expect(Math.max(...angleSteps)).toBeLessThan(0.36);
expect(routeRadii).toEqual([...routeRadii].sort((a, b) => a - b));
expect(hierarchyRouteCrossings(graph, layout)).toBe(0);
});
it('keeps vault-root outer hierarchy routes advancing outward near the perimeter', () => {
const records: { path: string; basename: string; kind: 'folder' | 'note' }[] = [];
for (let area = 0; area < 18; area++) {
const areaPath = `Area ${area}`;
records.push({ path: areaPath, basename: `Area ${area}`, kind: 'folder' });
for (let section = 0; section < 7; section++) {
const sectionPath = `${areaPath}/Section ${section}`;
records.push({ path: sectionPath, basename: `Section ${section}`, kind: 'folder' });
for (let leaf = 0; leaf < 2; leaf++) {
records.push({ path: `${sectionPath}/Leaf ${leaf}.md`, basename: `Leaf ${leaf}`, kind: 'note' });
}
}
}
const routeBase = 'U.S. Social Development';
const routeMid = `${routeBase}/U.S. Tertiary Education`;
const routeHub = `${routeMid}/U.S. Sports Institutions`;
records.push({ path: routeBase, basename: 'U.S. Social Development', kind: 'folder' });
records.push({ path: routeMid, basename: 'U.S. Tertiary Education', kind: 'folder' });
records.push({ path: routeHub, basename: 'U.S. Sports Institutions', kind: 'folder' });
for (const league of ['NBA', 'NFL', 'NHL', 'MLB', 'MLS', 'NCAA']) {
records.push({ path: `${routeHub}/${league}.md`, basename: `${league} (National League)`, kind: 'note' });
}
const m = buildWorldMap(records, {}, {}, DEFAULT_RADIAL_SETTINGS);
const state = defaultVisibleGraphState(DEFAULT_RADIAL_SETTINGS);
state.rootPath = ROOT_ID;
state.showLinkOverlay = false;
state.nodeLimit = 1200;
const graph = buildVisibleWorldGraph(m, state, DEFAULT_RADIAL_SETTINGS);
const layout = layoutRadialGraph(graph, { ringSpacing: 960, nodeSpacing: 126, swirlStrength: 0 });
const maxDepth = Math.max(...[...layout.positions.values()].filter((point) => !point.external).map((point) => Math.round(point.depth || 0)));
const outerSteps = graph.hierarchyEdges
.map((edge) => ({ source: layout.positions.get(edge.source), target: layout.positions.get(edge.target) }))
.filter((edge): edge is { source: NonNullable<typeof edge.source>; target: NonNullable<typeof edge.target> } =>
Boolean(edge.source && edge.target && !edge.source.external && !edge.target.external && Math.round(edge.target.depth || 0) >= maxDepth - 1),
)
.map((edge) => edge.target.radius - edge.source.radius);
expect(outerSteps.length).toBeGreaterThan(0);
expect(Math.min(...outerSteps)).toBeGreaterThan(layout.ringSpacing * 0.54);
});
it('lets delayed deep branches spend a controlled arc instead of a spiral wedge', () => {
const deepBase = 'Trunk/Branch/Layer A/Layer B/Layer C';
const records: { path: string; basename: string; kind: 'folder' | 'note' }[] = [
{ path: 'Trunk', basename: 'Trunk', kind: 'folder' },
{ path: 'Trunk/Branch', basename: 'Branch', kind: 'folder' },
{ path: 'Trunk/Branch/Layer A', basename: 'Layer A', kind: 'folder' },
{ path: 'Trunk/Branch/Layer A/Layer B', basename: 'Layer B', kind: 'folder' },
{ path: deepBase, basename: 'Layer C', kind: 'folder' },
];
for (let leaf = 0; leaf < 18; leaf++) {
records.push({ path: `${deepBase}/Leaf ${leaf}.md`, basename: `Leaf ${leaf}`, kind: 'note' });
}
const m = buildWorldMap(records, {}, {}, DEFAULT_RADIAL_SETTINGS);
const state = defaultVisibleGraphState(DEFAULT_RADIAL_SETTINGS);
state.showLinkOverlay = false;
state.nodeLimit = 1200;
const graph = buildVisibleWorldGraph(m, state, DEFAULT_RADIAL_SETTINGS);
const layout = layoutRadialGraph(graph, { ringSpacing: 960, nodeSpacing: 126, swirlStrength: 0 });
const parent = layout.positions.get(deepBase);
const leaves = Array.from({ length: 18 }, (_, index) => layout.positions.get(`${deepBase}/Leaf ${index}.md`)?.angle ?? 0);
const spread = angleSpreadAround(parent?.angle ?? 0, leaves);
expect(spread).toBeGreaterThan(0.55);
expect(spread).toBeLessThan(1.3);
});
it('lets the final visible ring spend radius before widening deep leaf fans', () => {
const hubs = ['Alpha', 'Beta'];
const records: { path: string; basename: string; kind: 'folder' | 'note' }[] = [
{ path: 'Topic', basename: 'Topic', kind: 'folder' },
];
for (const hub of hubs) {
const branchPath = `Topic/${hub}`;
const lanePath = `${branchPath}/Lane`;
const hubPath = `${lanePath}/Hub`;
records.push({ path: branchPath, basename: hub, kind: 'folder' });
records.push({ path: lanePath, basename: 'Lane', kind: 'folder' });
records.push({ path: hubPath, basename: 'Hub', kind: 'folder' });
for (let leaf = 0; leaf < 36; leaf++) {
records.push({ path: `${hubPath}/Leaf ${leaf}.md`, basename: `Leaf ${leaf}`, kind: 'note' });
}
}
const m = buildWorldMap(records, {}, {}, DEFAULT_RADIAL_SETTINGS);
const state = defaultVisibleGraphState(DEFAULT_RADIAL_SETTINGS);
state.rootPath = 'Topic';
state.showLinkOverlay = false;
state.nodeLimit = 1200;
const graph = buildVisibleWorldGraph(m, state, DEFAULT_RADIAL_SETTINGS);
const layout = layoutRadialGraph(graph, { ringSpacing: 960, nodeSpacing: 126, swirlStrength: 0 });
const ringsByDepth = new Map(layout.rings.map((ring) => [ring.depth, ring.radius]));
const finalGap = (ringsByDepth.get(4) ?? 0) - (ringsByDepth.get(3) ?? 0);
for (const hub of hubs) {
const parent = layout.positions.get(`Topic/${hub}/Lane/Hub`);
const leaves = Array.from({ length: 36 }, (_, index) => layout.positions.get(`Topic/${hub}/Lane/Hub/Leaf ${index}.md`)?.angle ?? 0);
const spread = angleSpreadAround(parent?.angle ?? 0, leaves);
expect(spread).toBeGreaterThan(0.7);
expect(spread).toBeLessThan(1.45);
}
expect(finalGap).toBeGreaterThan(layout.ringSpacing * 1.05);
expect(hierarchyRouteCrossings(graph, layout)).toBe(0);
});
it('relaxes the penultimate visible baseline ring before the map edge', () => {
const records: { path: string; basename: string; kind: 'folder' | 'note' }[] = [
{ path: 'Topic', basename: 'Topic', kind: 'folder' },
];
for (let group = 0; group < 8; group++) {
const groupPath = `Topic/Group ${group}`;
records.push({ path: groupPath, basename: `Group ${group}`, kind: 'folder' });
for (let hub = 0; hub < 10; hub++) {
const hubPath = `${groupPath}/Hub ${hub}`;
records.push({ path: hubPath, basename: `Hub ${hub}`, kind: 'folder' });
records.push({ path: `${hubPath}/Leaf.md`, basename: 'Leaf', kind: 'note' });
}
}
const m = buildWorldMap(records, {}, {}, DEFAULT_RADIAL_SETTINGS);
const state = defaultVisibleGraphState(DEFAULT_RADIAL_SETTINGS);
state.rootPath = 'Topic';
state.showLinkOverlay = false;
state.nodeLimit = 1200;
const graph = buildVisibleWorldGraph(m, state, DEFAULT_RADIAL_SETTINGS);
const layout = layoutRadialGraph(graph, { ringSpacing: 960, nodeSpacing: 126, swirlStrength: 0 });
const ringsByDepth = new Map(layout.rings.map((ring) => [ring.depth, ring.radius]));
const penultimateGap = (ringsByDepth.get(2) ?? 0) - (ringsByDepth.get(1) ?? 0);
const finalGap = (ringsByDepth.get(3) ?? 0) - (ringsByDepth.get(2) ?? 0);
expect(penultimateGap).toBeGreaterThan(layout.ringSpacing * 1.34);
expect(finalGap).toBeGreaterThan(layout.ringSpacing * 1.5);
});
it('keeps grouped and exact outside nodes beyond the regular map shell', () => {
for (const mode of ['grouped', 'exact'] as const) {
const m = model();
const state = defaultVisibleGraphState(DEFAULT_RADIAL_SETTINGS);
state.rootPath = 'Atlas';
state.showLinkOverlay = true;
state.showExternalLinks = true;
state.externalDetailMode = mode;
const graph = buildVisibleWorldGraph(m, state, DEFAULT_RADIAL_SETTINGS);
const layout = layoutRadialGraph(graph, { ringSpacing: 960, nodeSpacing: 126, swirlStrength: 0 });
const regularRadii = graph.nodes
.filter((node) => node.type !== 'external' && !node.externalProxy)
.map((node) => layout.positions.get(node.id)?.radius ?? 0);
const outsideRadii = graph.nodes
.filter((node) => node.type === 'external' || node.externalProxy)
.map((node) => layout.positions.get(node.id)?.radius ?? 0);
const maxRegularRadius = Math.max(...regularRadii);
const minOutsideRadius = Math.min(...outsideRadii);
expect(outsideRadii.length).toBeGreaterThan(0);
expect(minOutsideRadius).toBeGreaterThan(maxRegularRadius + layout.ringSpacing * 0.42);
if (mode === 'exact') {
const groupRadii = graph.nodes
.filter((node) => node.type === 'external' && !node.externalProxy)
.map((node) => layout.positions.get(node.id)?.radius ?? 0);
const fileRadii = graph.nodes
.filter((node) => node.externalProxy)
.map((node) => layout.positions.get(node.id)?.radius ?? 0);
expect(fileRadii.length).toBeGreaterThan(0);
expect(Math.min(...fileRadii)).toBeGreaterThan(Math.max(...groupRadii) + layout.ringSpacing * 0.24);
}
}
});
it('scales outside shell distance away from large regular maps', () => {
const records: { path: string; basename: string; kind: 'folder' | 'note' }[] = [
{ path: 'CyberSecurity', basename: 'CyberSecurity', kind: 'folder' },
];
const resolvedLinks: Record<string, Record<string, number>> = {};
for (let area = 0; area < 22; area++) {
const areaPath = `CyberSecurity/Area ${area}`;
records.push({ path: areaPath, basename: `Area ${area}`, kind: 'folder' });
for (let topic = 0; topic < 12; topic++) {
const topicPath = `${areaPath}/Topic ${topic}.md`;
const outsidePath = `Outside/Reference ${area}-${topic}.md`;
records.push({ path: topicPath, basename: `Topic ${topic}`, kind: 'note' });
records.push({ path: outsidePath, basename: `Reference ${area}-${topic}`, kind: 'note' });
resolvedLinks[topicPath] = { [outsidePath]: 1 };
}
}
const m = buildWorldMap(records, resolvedLinks, {}, DEFAULT_RADIAL_SETTINGS);
for (const mode of ['grouped', 'exact'] as const) {
const state = defaultVisibleGraphState(DEFAULT_RADIAL_SETTINGS);
state.rootPath = 'CyberSecurity';
state.showLinkOverlay = true;
state.showExternalLinks = true;
state.externalDetailMode = mode;
state.nodeLimit = 1200;
const graph = buildVisibleWorldGraph(m, state, DEFAULT_RADIAL_SETTINGS);
const layout = layoutRadialGraph(graph, { ringSpacing: 960, nodeSpacing: 126, swirlStrength: 0 });
const regularRadii = graph.nodes
.filter((node) => node.type !== 'external' && !node.externalProxy)
.map((node) => layout.positions.get(node.id)?.radius ?? 0);
const outsideRadii = graph.nodes
.filter((node) => node.type === 'external' || node.externalProxy)
.map((node) => layout.positions.get(node.id)?.radius ?? 0);
const maxRegularRadius = Math.max(...regularRadii);
const minOutsideRadius = Math.min(...outsideRadii);
expect(outsideRadii.length).toBeGreaterThan(0);
expect(minOutsideRadius).toBeGreaterThan(maxRegularRadius + Math.max(layout.ringSpacing * 0.72, maxRegularRadius * 0.075));
}
});
it('expands crowded deep fans without throwing the route off its parent ray', () => {
const crowdedBase = 'Branch 9/Lane/Hub';
const records: { path: string; basename: string; kind: 'folder' | 'note' }[] = [];
for (let branch = 0; branch < 22; branch++) {
const branchPath = `Branch ${branch}`;
const lanePath = `${branchPath}/Lane`;
const hubPath = `${lanePath}/Hub`;
records.push({ path: branchPath, basename: `Branch ${branch}`, kind: 'folder' });
records.push({ path: lanePath, basename: 'Lane', kind: 'folder' });
records.push({ path: hubPath, basename: 'Hub', kind: 'folder' });
const leafCount = branch === 9 ? 24 : 2;
for (let leaf = 0; leaf < leafCount; leaf++) {
records.push({ path: `${hubPath}/Leaf ${leaf}.md`, basename: `Leaf ${leaf}`, kind: 'note' });
}
}
const m = buildWorldMap(records, {}, {}, DEFAULT_RADIAL_SETTINGS);
const state = defaultVisibleGraphState(DEFAULT_RADIAL_SETTINGS);
state.showLinkOverlay = false;
state.nodeLimit = 1600;
const graph = buildVisibleWorldGraph(m, state, DEFAULT_RADIAL_SETTINGS);
const layout = layoutRadialGraph(graph, { ringSpacing: 960, nodeSpacing: 126, swirlStrength: 0 });
const parent = layout.positions.get(crowdedBase);
const leaves = Array.from({ length: 24 }, (_, index) => layout.positions.get(`${crowdedBase}/Leaf ${index}.md`)?.angle ?? 0);
const centeredLeafIndex = leaves.reduce(
(best, angle, index) => (angleDelta(angle, parent?.angle ?? 0) < angleDelta(leaves[best] ?? 0, parent?.angle ?? 0) ? index : best),
0,
);
const middleRoute = [
'Branch 9',
'Branch 9/Lane',
crowdedBase,
`${crowdedBase}/Leaf ${centeredLeafIndex}.md`,
].map((id) => layout.positions.get(id)?.angle ?? 0);
const routeSteps = middleRoute.slice(1).map((angle, index) => angleDelta(angle, middleRoute[index] ?? 0));
const leafDeltas = leaves.map((angle) => angleDelta(angle, parent?.angle ?? 0));
const spread = angleSpreadAround(parent?.angle ?? 0, leaves);
expect(spread).toBeGreaterThan(0.65);
expect(spread).toBeLessThan(1.5);
expect(Math.max(...routeSteps)).toBeLessThan(1.15);
expect(Math.max(...leafDeltas)).toBeLessThan(2.45);
});
it('keeps dense leaf fans inside their parent sectors so sibling routes do not cross', () => {
const records: { path: string; basename: string; kind: 'folder' | 'note' }[] = [
{ path: 'Topic', basename: 'Topic', kind: 'folder' },
{ path: 'Topic/Alpha', basename: 'Alpha', kind: 'folder' },
{ path: 'Topic/Beta', basename: 'Beta', kind: 'folder' },
];
for (const parent of ['Alpha', 'Beta']) {
for (let index = 0; index < 18; index++) {
records.push({ path: `Topic/${parent}/Leaf ${index}.md`, basename: `Leaf ${index}`, kind: 'note' });
}
}
const m = buildWorldMap(records, {}, {}, DEFAULT_RADIAL_SETTINGS);
const state = defaultVisibleGraphState(DEFAULT_RADIAL_SETTINGS);
state.showLinkOverlay = false;
state.nodeLimit = 1200;
const graph = buildVisibleWorldGraph(m, state, DEFAULT_RADIAL_SETTINGS);
const layout = layoutRadialGraph(graph, { ringSpacing: 960, nodeSpacing: 126, swirlStrength: 0 });
const alpha = layout.positions.get('Topic/Alpha');
const beta = layout.positions.get('Topic/Beta');
for (const parent of ['Alpha', 'Beta']) {
const parentPoint = layout.positions.get(`Topic/${parent}`);
const siblingPoint = layout.positions.get(`Topic/${parent === 'Alpha' ? 'Beta' : 'Alpha'}`);
const leaves = Array.from({ length: 18 }, (_, index) => layout.positions.get(`Topic/${parent}/Leaf ${index}.md`)?.angle ?? 0);
const maxDelta = Math.max(...leaves.map((angle) => angleDelta(angle, parentPoint?.angle ?? 0)));
const nearestSiblingDelta = Math.min(...leaves.map((angle) => angleDelta(angle, siblingPoint?.angle ?? 0)));
expect(maxDelta).toBeLessThan((parentPoint?.sectorSpan ?? Math.PI * 2) * 0.5 + 0.04);
expect(nearestSiblingDelta).toBeGreaterThan(0.08);
}
expect(angleDelta(alpha?.angle ?? 0, beta?.angle ?? 0)).toBeGreaterThan(0.8);
});
it('keeps jagged same-depth routes in hierarchy order', () => {
const parents = ['Alpha', 'Beta', 'Gamma', 'Delta'];
const records: { path: string; basename: string; kind: 'folder' | 'note' }[] = [
{ path: 'Topic', basename: 'Topic', kind: 'folder' },
...parents.map((parent) => ({ path: `Topic/${parent}`, basename: parent, kind: 'folder' as const })),
];
for (const parent of parents) {
for (let index = 0; index < 24; index++) {
records.push({ path: `Topic/${parent}/Leaf ${index}.md`, basename: `Leaf ${index}`, kind: 'note' });
}
}
const m = buildWorldMap(records, {}, {}, DEFAULT_RADIAL_SETTINGS);
const state = defaultVisibleGraphState(DEFAULT_RADIAL_SETTINGS);
state.rootPath = 'Topic';
state.showLinkOverlay = false;
state.nodeLimit = 1200;
const graph = buildVisibleWorldGraph(m, state, DEFAULT_RADIAL_SETTINGS);
const layout = layoutRadialGraph(graph, { ringSpacing: 960, nodeSpacing: 126, swirlStrength: 0 });
const leafRadii = parents.flatMap((parent) =>
Array.from({ length: 24 }, (_, index) => layout.positions.get(`Topic/${parent}/Leaf ${index}.md`)?.radius ?? 0),
);
expect(Math.max(...leafRadii) - Math.min(...leafRadii)).toBeGreaterThan(25);
expect(hierarchyRouteCrossings(graph, layout)).toBe(0);
});
it('sizes nodes primarily by note-link degree instead of descendant count', () => {
const records: { path: string; basename: string; kind: 'folder' | 'note' }[] = [
{ path: 'Archive', basename: 'Archive', kind: 'folder' },
{ path: 'Linked.md', basename: 'Linked', kind: 'note' },
];
for (let index = 0; index < 80; index++) {
records.push({ path: `Archive/Quiet ${index}.md`, basename: `Quiet ${index}`, kind: 'note' });
records.push({ path: `Targets/Target ${index}.md`, basename: `Target ${index}`, kind: 'note' });
}
const resolvedLinks = Object.fromEntries(
Array.from({ length: 24 }, (_, index) => [`Targets/Target ${index}.md`, 1]),
);
const m = buildWorldMap(records, { 'Linked.md': resolvedLinks }, {}, DEFAULT_RADIAL_SETTINGS);
const state = defaultVisibleGraphState(DEFAULT_RADIAL_SETTINGS);
state.showLinkOverlay = true;
state.nodeLimit = 1200;
const graph = buildVisibleWorldGraph(m, state, DEFAULT_RADIAL_SETTINGS);
const layout = layoutRadialGraph(graph, { ringSpacing: 960, nodeSpacing: 126, swirlStrength: 0 });
const linked = layout.positions.get('Linked.md');
const archive = layout.positions.get('Archive');
expect(linked?.nodeRadius ?? 0).toBeGreaterThan((archive?.nodeRadius ?? 0) * 1.8);
expect(archive?.nodeRadius ?? 0).toBeLessThan(11);
});
it('does not invent hierarchy rings for flat same-parent root notes', () => {
const crowdedRecords = Array.from({ length: 900 }, (_, index) => ({
path: `Note ${index}.md`,
basename: `Note ${index}`,
kind: 'note' as const,
}));
const m = buildWorldMap(crowdedRecords, {}, {}, DEFAULT_RADIAL_SETTINGS);
const state = defaultVisibleGraphState(DEFAULT_RADIAL_SETTINGS);
state.showLinkOverlay = false;
state.nodeLimit = 1200;
const graph = buildVisibleWorldGraph(m, state, DEFAULT_RADIAL_SETTINGS);
const layout = layoutRadialGraph(graph, { ringSpacing: 960, nodeSpacing: 126, swirlStrength: 0 });
const nodeRadii = [...layout.positions.entries()]
.filter(([id]) => id !== graph.rootId)
.map(([, point]) => point.radius);
const depthOneRings = layout.rings.filter((ring) => ring.depth === 1);
const minRadius = Math.min(...nodeRadii);
const maxRadius = Math.max(...nodeRadii);
expect(depthOneRings).toHaveLength(1);
expect(maxRadius - minRadius).toBeLessThan((depthOneRings[0]?.radius ?? 1) * 0.5);
});
});