/* Prerequisite graph view — hierarchical DAG, Vignelli-style */ const PrerequisiteGraph = ({ initialView = "hierarchical" } = {}) => { const [view, setView] = React.useState(initialView); const [hovered, setHovered] = React.useState(null); const [filterEdge, setFilterEdge] = React.useState({USES:true, DEFINES:true, PROVES:true, GENERALIZES:true, IS_EXAMPLE:true}); // Hand-laid hierarchical layout — 8 ranks, columnar to a baseline grid. // x: column index 0..11 (12-col grid), y: rank 0..7 (top → bottom) const nodes = [ // rank 0 — axioms / primitives { id:"real_numbers", label:"Real numbers ℝ", kind:"axiom", mastery:"mastered", x:1, y:0 }, { id:"set", label:"Set", kind:"definition", mastery:"mastered", x:4, y:0 }, { id:"function", label:"Function", kind:"definition", mastery:"mastered", x:7, y:0 }, { id:"interval", label:"Open interval", kind:"definition", mastery:"mastered", x:10,y:0 }, // rank 1 — basic structures { id:"sequence", label:"Sequence", kind:"definition", mastery:"mastered", x:1, y:1 }, { id:"slope_line", label:"Slope of a line", kind:"definition", mastery:"learning", x:3, y:1 }, { id:"absolute_val", label:"Absolute value", kind:"definition", mastery:"mastered", x:5.5, y:1 }, { id:"composition", label:"Composition", kind:"definition", mastery:"mastered", x:8, y:1 }, // rank 2 — limits foundation { id:"limit_seq", label:"Limit · sequence", kind:"definition", mastery:"mastered", x:1.5, y:2 }, { id:"limit_func", label:"Limit · function", kind:"definition", mastery:"mastered", x:5, y:2 }, { id:"epsilon_delta", label:"ε–δ definition", kind:"definition", mastery:"learning", x:8.5, y:2 }, // rank 3 — derived limit lemmas { id:"limit_sum", label:"Limit · sum rule", kind:"lemma", mastery:"mastered", x:2, y:3 }, { id:"limit_product", label:"Limit · product rule", kind:"lemma", mastery:"learning", x:5, y:3 }, { id:"limit_compose", label:"Limit · composition", kind:"lemma", mastery:"learning", x:8, y:3 }, // rank 4 — continuity + secants { id:"continuity", label:"Continuity", kind:"definition", mastery:"learning", x:3, y:4 }, { id:"secant", label:"Secant line", kind:"definition", mastery:"frontier", x:6, y:4 }, { id:"diff_quot", label:"Difference quotient", kind:"definition", mastery:"frontier", x:9, y:4 }, // rank 5 — DERIVATIVE node, central { id:"derivative", label:"Derivative", kind:"definition", mastery:"frontier", x:6, y:5, focus:true }, // rank 6 — first-order theorems { id:"diff_implies_cont", label:"Differentiability ⇒ continuity", kind:"theorem", mastery:"mastered", x:1.5, y:6 }, { id:"sum_rule", label:"Sum rule (deriv.)", kind:"theorem", mastery:"mastered", x:4, y:6 }, { id:"product_rule", label:"Product rule", kind:"theorem", mastery:"learning", x:6.5, y:6 }, { id:"chain_rule", label:"Chain rule", kind:"theorem", mastery:"frontier", x:9, y:6 }, // rank 7 — downstream { id:"mvt", label:"Mean value theorem", kind:"theorem", mastery:"locked", x:2, y:7 }, { id:"l_hopital", label:"L'Hôpital's rule", kind:"theorem", mastery:"locked", x:5, y:7 }, { id:"taylor", label:"Taylor's theorem", kind:"theorem", mastery:"locked", x:8, y:7 }, { id:"newtons_method", label:"Newton's method", kind:"procedure", mastery:"locked", x:10.5, y:7 }, ]; const edges = [ // upstream into derivative {a:"function", b:"derivative", t:"USES"}, {a:"limit_func", b:"derivative", t:"USES"}, {a:"diff_quot", b:"derivative", t:"DEFINES"}, {a:"secant", b:"derivative", t:"USES"}, {a:"continuity", b:"derivative", t:"USES"}, {a:"interval", b:"derivative", t:"USES"}, // foundations {a:"real_numbers", b:"limit_seq", t:"USES"}, {a:"set", b:"function", t:"USES"}, {a:"function", b:"limit_func", t:"USES"}, {a:"function", b:"composition",t:"USES"}, {a:"limit_seq", b:"limit_func", t:"GENERALIZES"}, {a:"limit_func", b:"epsilon_delta", t:"DEFINES"}, {a:"limit_func", b:"limit_sum", t:"USES"}, {a:"limit_func", b:"limit_product", t:"USES"}, {a:"limit_func", b:"limit_compose", t:"USES"}, {a:"limit_func", b:"continuity", t:"DEFINES"}, {a:"slope_line", b:"secant", t:"USES"}, {a:"slope_line", b:"diff_quot", t:"USES"}, {a:"function", b:"secant", t:"USES"}, {a:"absolute_val", b:"continuity", t:"USES"}, // downstream from derivative {a:"derivative", b:"diff_implies_cont", t:"PROVES"}, {a:"derivative", b:"sum_rule", t:"PROVES"}, {a:"derivative", b:"product_rule", t:"PROVES"}, {a:"derivative", b:"chain_rule", t:"PROVES"}, {a:"limit_sum", b:"sum_rule", t:"USES"}, {a:"limit_product",b:"product_rule", t:"USES"}, {a:"limit_compose",b:"chain_rule", t:"USES"}, {a:"continuity", b:"diff_implies_cont", t:"USES"}, // far downstream {a:"diff_implies_cont", b:"mvt", t:"USES"}, {a:"derivative", b:"mvt", t:"USES"}, {a:"derivative", b:"l_hopital",t:"USES"}, {a:"derivative", b:"taylor", t:"USES"}, {a:"derivative", b:"newtons_method", t:"USES"}, ]; // grid: 12 columns wide, 8 ranks deep const COLS = 12, RANKS = 8; const W = 1100, H = 740; const PAD_X = 60, PAD_Y = 48; const xToPx = (x) => PAD_X + (x / (COLS - 1)) * (W - 2*PAD_X); const yToPx = (y) => PAD_Y + (y / (RANKS - 1)) * (H - 2*PAD_Y); const nodeById = Object.fromEntries(nodes.map(n => [n.id, n])); const isEdgeOn = (e) => { const map = { USES:"USES", DEFINES:"DEFINES", PROVES:"PROVES", GENERALIZES:"GENERALIZES", IS_EXAMPLE:"IS_EXAMPLE" }; return filterEdge[map[e.t]]; }; const edgeColor = (t) => { if (t === "DEFINES") return "var(--accent)"; if (t === "PROVES") return "var(--ink)"; if (t === "GENERALIZES") return "var(--ink-3)"; return "var(--ink-3)"; // USES }; const edgeDash = (t) => { if (t === "GENERALIZES") return "4 3"; if (t === "DEFINES") return undefined; if (t === "PROVES") return undefined; return undefined; }; return (