PHP: Graf drzewa generowany z artykułów na wiki

https://wiki.ostrowski.net.pl/mindmap.php

mindmap.php
<?php
header("Content-Type: text/html");
 
$pagesDir = __DIR__ . '/data/pages';
$excludedNamespaces = ['ns1','ns2']; 
// here you should put namespaces that should be excluded from being graphed by this script
 
function buildTree($dir, $namespace = '') {
    global $excludedNamespaces;
    $result = [];
 
    foreach (scandir($dir) as $file) {
        if ($file === '.' || $file === '..') continue;
 
        $path = $dir . '/' . $file;
        $fileWithoutExt = pathinfo($file, PATHINFO_FILENAME);
        $dokuwikiID = $namespace . $fileWithoutExt;
 
        if (is_dir($path)) {
            if (in_array($file, $excludedNamespaces)) continue;
 
            $children = buildTree($path, $namespace . $file . ':');
            if (!empty($children)) {
                $result[] = [
                    'name' => $file,
                    'id' => $namespace . $file,
                    'children' => $children
                ];
            }
        } elseif (pathinfo($file, PATHINFO_EXTENSION) === 'txt') {
            $result[] = [
                'name' => str_replace('_', ' ', $fileWithoutExt),
                'id' => $dokuwikiID
            ];
        }
    }
 
    return $result;
}
 
$treeData = [
    'name' => 'DokuWiki',
    'id' => '',
    'children' => buildTree($pagesDir)
];
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>DokuWiki Tree Map</title>
    <script src="https://d3js.org/d3.v7.min.js"></script>
    <style>
        html, body {
            margin: 0;
            padding: 0;
            overflow: hidden;
            height: 100%;
        }
        svg {
            width: 100vw;
            height: 100vh;
            cursor: grab;
            background-color: #f9f9f9;
        }
        .node circle {
            fill: #6baed6;
        }
        .node text {
            font: 32px sans-serif;
        }
        .link {
            fill: none;
            stroke: #999;
            stroke-opacity: 0.6;
            stroke-width: 1.5px;
        }
        a {
            fill: black;
            text-decoration: none;
        }
    </style>
</head>
<body>
Wanna read how it is made click here: <a href="">wiki.ostrowski.net.pl</a>
<svg></svg>
<script>
    const treeData = <?php echo json_encode($treeData); ?>;
 
    const width = 4000;
    const height = 3000;
 
    const svg = d3.select("svg");
    const g = svg.append("g").attr("transform", "translate(50,50)");
 
    svg.call(d3.zoom()
        .scaleExtent([0.1, 2])
        .on("zoom", (event) => {
            g.attr("transform", event.transform);
        }));
 
    const root = d3.hierarchy(treeData);
    const treeLayout = d3.tree().nodeSize([30, 600]);
    treeLayout(root);
 
    // Links
    g.selectAll(".link")
        .data(root.links())
        .enter().append("path")
        .attr("class", "link")
        .attr("d", d3.linkHorizontal()
            .x(d => d.y)
            .y(d => d.x));
 
    // Nodes
    const node = g.selectAll(".node")
        .data(root.descendants())
        .enter().append("g")
        .attr("class", "node")
        .attr("transform", d => `translate(${d.y},${d.x})`);
 
    node.append("circle").attr("r", 6);
 
    // Add clickable links to DokuWiki REPLACE THE URL
    node.append("a")
        .attr("xlink:href", d => d.data.id ? `<PUT YOUR WIKI URL HERE>/doku.php?id=${d.data.id}` : null)
        .attr("target", "_blank")  // open in new tab
        .append("text")
        .attr("dy", 4)
        .attr("x", d => d.children ? -12 : 12)
        .style("text-anchor", d => d.children ? "end" : "start")
        .text(d => d.data.name);
</script>
</body>
</html>