<?php
/* ======================================
   BACKEND: PHP (Schema + AI Context)
====================================== */
error_reporting(E_ERROR | E_PARSE); 
mysqli_report(MYSQLI_REPORT_OFF);

$DB_HOST = "localhost";
$DB_USER = "root";
$DB_PASS = "33comRxXMysql"; 

$bases = [];
$dbData = []; 
$relaciones = [];
$stats = ['dbs' => 0, 'tables' => 0, 'relations' => 0];
$errorMsg = "";

try {
    $mysqli = new mysqli($DB_HOST, $DB_USER, $DB_PASS);
    $mysqli->set_charset("utf8mb4");
    if ($mysqli->connect_errno) throw new Exception("Error: " . $mysqli->connect_error);

    // 1. Listar Bases de Datos
    $res = $mysqli->query("SHOW DATABASES");
    if($res) {
        while ($r = $res->fetch_row()) {
            if(!in_array($r[0], ['information_schema','mysql','performance_schema','sys','phpmyadmin'])) {
                $bases[] = $r[0];
            }
        }
        $stats['dbs'] = count($bases);
    }

    $dbSeleccionada = $_GET['db'] ?? '';
    
    if ($dbSeleccionada) {
        $mysqli->select_db($dbSeleccionada);

        // 2. Obtener Estructura
        $qCols = "SELECT TABLE_NAME, COLUMN_NAME, COLUMN_TYPE, COLUMN_KEY, IS_NULLABLE, COLUMN_DEFAULT, EXTRA 
                  FROM INFORMATION_SCHEMA.COLUMNS 
                  WHERE TABLE_SCHEMA = '$dbSeleccionada' 
                  ORDER BY TABLE_NAME, ORDINAL_POSITION";
        
        $resCols = $mysqli->query($qCols);
        
        $tempTables = [];
        while ($r = $resCols->fetch_assoc()) {
            $tempTables[$r['TABLE_NAME']]['columns'][] = [
                'name'    => $r['COLUMN_NAME'],
                'type'    => $r['COLUMN_TYPE'],
                'key'     => $r['COLUMN_KEY'],
                'null'    => $r['IS_NULLABLE'],
                'default' => $r['COLUMN_DEFAULT'],
                'extra'   => $r['EXTRA']
            ];
        }

        // 3. Contexto (Indices + Sample)
        foreach($tempTables as $tblName => $data) {
            $idxs = [];
            $resIdx = $mysqli->query("SHOW INDEX FROM `$tblName`");
            if($resIdx) {
                while($row = $resIdx->fetch_assoc()){
                    if($row['Key_name'] !== 'PRIMARY') { 
                        $idxs[] = ['name' => $row['Key_name'], 'col' => $row['Column_name'], 'unique' => ($row['Non_unique']==0)];
                    }
                }
            }
            $tempTables[$tblName]['indexes'] = $idxs;

            $sample = [];
            $resSample = $mysqli->query("SELECT * FROM `$tblName` LIMIT 1");
            if($resSample && $resSample->num_rows > 0) {
                $sample = $resSample->fetch_assoc();
                array_walk($sample, function(&$v) { if(is_string($v) && strlen($v) > 50) $v = substr($v,0,50)."..."; });
            }
            $tempTables[$tblName]['sample'] = $sample;
        }
        
        $dbData = $tempTables;
        $stats['tables'] = count($dbData);

        // 4. Relaciones
        $qRel = "SELECT TABLE_NAME, COLUMN_NAME, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME
                 FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE 
                 WHERE TABLE_SCHEMA = '$dbSeleccionada' 
                 AND REFERENCED_TABLE_NAME IS NOT NULL";
        $resRel = $mysqli->query($qRel);
        while ($r = $resRel->fetch_assoc()) {
            $relaciones[] = [
                'from_table' => $r['TABLE_NAME'],
                'from_col'   => $r['COLUMN_NAME'],
                'to_table'   => $r['REFERENCED_TABLE_NAME'],
                'to_col'     => $r['REFERENCED_COLUMN_NAME']
            ];
        }
        $stats['relations'] = count($relaciones);
    }
} catch (Exception $e) { $errorMsg = $e->getMessage(); }
?>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DB Architect AI - Pro</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script type="text/javascript" src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script>

<style>
    :root { --sidebar-w: 300px; --accent: #6366f1; }
    body { background-color: #f8fafc; font-family: 'Inter', sans-serif; height: 100vh; overflow: hidden; margin:0; }
    
    .sidebar {
        width: var(--sidebar-w); height: 100%; position: fixed; left: 0; top: 0;
        background: #fff; border-right: 1px solid #e2e8f0; z-index: 20;
        padding: 20px; display: flex; flex-direction: column;
        box-shadow: 4px 0 24px rgba(0,0,0,0.02);
    }
    
    .canvas-area {
        margin-left: var(--sidebar-w); height: 100%; position: relative;
        background-color: #f1f5f9;
        background-image: radial-gradient(#cbd5e1 1px, transparent 1px);
        background-size: 20px 20px;
    }

    .table-list-container {
        flex-grow: 1;
        overflow-y: auto;
        margin-top: 15px;
        padding-right: 5px;
    }

    .table-item {
        padding: 8px 12px;
        border-radius: 8px;
        margin-bottom: 4px;
        cursor: pointer;
        transition: all 0.2s;
        display: flex;
        justify-content: space-between;
        align-items: center;
        border: 1px solid transparent;
    }

    .table-item:hover { background: #f1f5f9; border-color: #e2e8f0; }
    .table-item .name { font-size: 13px; font-weight: 500; color: #475569; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

    .control-panel {
        position: absolute; top: 20px; right: 20px; width: 300px;
        background: rgba(255, 255, 255, 0.95);
        backdrop-filter: blur(12px);
        border: 1px solid rgba(255,255,255,0.5);
        border-radius: 16px; padding: 15px;
        box-shadow: 0 15px 35px -5px rgba(0,0,0,0.1);
        z-index: 10;
    }
    
    #mynetwork { width: 100%; height: 100%; outline: none; }
    .json-container { background: #1e293b; color: #a5b3ce; font-family: 'Fira Code', monospace; font-size: 11px; border-radius: 8px; }

    /* Custom scrollbar */
    ::-webkit-scrollbar { width: 6px; }
    ::-webkit-scrollbar-track { background: transparent; }
    ::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 10px; }

    @media (max-width: 768px) {
        .sidebar { transform: translateX(-100%); transition: transform 0.3s; z-index: 1050; }
        .sidebar.show { transform: translateX(0); }
        .canvas-area { margin-left: 0; }
        .mobile-btn { display: block !important; position: absolute; top: 10px; left: 10px; z-index: 100; }
    }
    .mobile-btn { display: none; }
</style>
</head>
<body>

<button class="btn btn-primary rounded-circle shadow mobile-btn" onclick="document.querySelector('.sidebar').classList.toggle('show')">
    <i class="fa-solid fa-bars"></i>
</button>

<div class="sidebar">
    <div class="d-flex align-items-center gap-2 mb-4">
        <div class="bg-primary bg-gradient text-white rounded p-2 shadow-sm"><i class="fa-solid fa-database"></i></div>
        <div><h6 class="m-0 fw-bold text-dark">Data Architect</h6></div>
        <button class="btn-close ms-auto d-md-none" onclick="document.querySelector('.sidebar').classList.remove('show')"></button>
    </div>

    <div class="bg-light border rounded p-2 mb-3 d-flex justify-content-between text-center small">
        <div><strong><?= $stats['dbs'] ?></strong><br>DBs</div>
        <div class="border-start border-end px-2"><strong><?= $stats['tables'] ?></strong><br>Tabs</div>
        <div><strong><?= $stats['relations'] ?></strong><br>Rels</div>
    </div>

    <form id="dbForm">
        <label class="small text-secondary fw-bold mb-1">CONEXIÓN</label>
        <select name="db" class="form-select form-select-sm mb-3 shadow-sm" onchange="this.form.submit()">
            <option value="">-- Seleccionar Base --</option>
            <?php foreach($bases as $b): ?>
                <option value="<?= $b ?>" <?= ($b == $dbSeleccionada) ? 'selected' : '' ?>><?= $b ?></option>
            <?php endforeach; ?>
        </select>
    </form>

    <?php if($dbSeleccionada): ?>
    <label class="small text-secondary fw-bold mb-2">TABLAS</label>
    <div class="table-list-container">
        <?php foreach($dbData as $tName => $tData): ?>
            <div class="table-item" id="list-item-<?= $tName ?>">
                <span class="name" onclick="focusTable('<?= $tName ?>')">
                    <i class="fa-solid fa-table me-2 opacity-50"></i><?= $tName ?>
                </span>
                <div class="form-check form-switch m-0 p-0" style="min-height: auto;">
                    <input class="form-check-input ms-0" type="checkbox" role="switch" 
                           id="sw-side-<?= $tName ?>" 
                           onchange="toggleContext('<?= $tName ?>')">
                </div>
            </div>
        <?php endforeach; ?>
    </div>
    <?php endif; ?>
</div>

<div class="canvas-area">
    <?php if($dbSeleccionada): ?>
        <div id="mynetwork"></div>

        <div class="control-panel">
            <button class="btn btn-primary btn-sm w-100 mb-3 shadow-sm d-flex justify-content-between align-items-center" type="button" data-bs-toggle="offcanvas" data-bs-target="#offcanvasContext">
                <span><i class="fa-solid fa-layer-group me-1"></i> Contexto IA</span>
                <span class="badge bg-white text-primary rounded-pill" id="btnContextCount">0</span>
            </button>

            <div class="input-group input-group-sm mb-3">
                <span class="input-group-text bg-white border-end-0"><i class="fa-solid fa-search"></i></span>
                <input type="text" id="searchInput" class="form-control border-start-0" placeholder="Buscar tabla..." onkeyup="searchNode(this.value)">
            </div>

            <label class="small text-secondary fw-bold mb-2">MODO VISUAL</label>
            <div class="btn-group w-100 mb-3">
                <input type="radio" class="btn-check" name="vmode" id="vmPhysics" checked onclick="setMode('physics')">
                <label class="btn btn-outline-secondary btn-sm" for="vmPhysics">Física</label>
                
                <input type="radio" class="btn-check" name="vmode" id="vmGrid" onclick="setMode('grid')">
                <label class="btn btn-outline-secondary btn-sm" for="vmGrid">Cuadrícula</label>
            </div>

            <label class="small text-muted d-flex justify-content-between">
                <span>Separación</span>
                <span id="gravVal">10</span>
            </label>
            <input type="range" class="form-range mb-3" min="0" max="1000" step="10" value="10" id="rngGrav" oninput="updatePhysics()">

            <div class="form-check form-switch mb-2">
                <input class="form-check-input" type="checkbox" id="chkLabels" checked onchange="toggleLabels()">
                <label class="form-check-label small">Ver relaciones</label>
            </div>
            
            <button class="btn btn-outline-primary btn-sm w-100" onclick="network.fit({animation:true})">
                <i class="fa-solid fa-compress me-1"></i> Centrar Todo
            </button>
        </div>
    <?php else: ?>
        <div class="d-flex h-100 align-items-center justify-content-center flex-column text-muted opacity-50">
            <i class="fa-solid fa-database fa-4x mb-4"></i>
            <h4>Selecciona una conexión</h4>
        </div>
    <?php endif; ?>
</div>

<div class="modal fade" id="infoModal" tabindex="-1">
    <div class="modal-dialog modal-lg modal-dialog-centered">
        <div class="modal-content border-0 shadow-lg">
            <div class="modal-header py-2 bg-primary text-white">
                <h5 class="modal-title fs-6" id="modalTitle">TABLA</h5>
                <div class="form-check form-switch m-0 ms-auto me-3 d-flex align-items-center gap-2">
                    <input class="form-check-input" type="checkbox" role="switch" id="chkModal" onchange="toggleFromModal()">
                    <label class="form-check-label small fw-bold text-white" for="chkModal">Seleccionar Contexto</label>
                </div>
                <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
            </div>
            <div class="modal-body p-0">
                <ul class="nav nav-tabs px-3 pt-2 bg-light" id="myTab">
                    <li class="nav-item"><button class="nav-link active" data-bs-toggle="tab" data-bs-target="#tab-struct">Estructura</button></li>
                    <li class="nav-item"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#tab-json">JSON Contexto</button></li>
                </ul>
                <div class="tab-content">
                    <div class="tab-pane fade show active" id="tab-struct">
                        <div class="table-responsive" style="max-height: 400px;">
                            <table class="table table-striped small mb-0">
                                <thead class="sticky-top bg-white"><tr><th class="ps-4">Campo</th><th>Tipo</th><th>Key</th><th>Extra</th></tr></thead>
                                <tbody id="modalBody"></tbody>
                            </table>
                        </div>
                    </div>
                    <div class="tab-pane fade" id="tab-json">
                        <div class="position-relative">
                            <button class="btn btn-xs btn-light border position-absolute top-0 end-0 m-2" onclick="copyJson()"><i class="fa-solid fa-copy"></i></button>
                            <pre class="json-container mb-0 p-3" id="jsonCode"></pre>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

<div class="offcanvas offcanvas-end" data-bs-scroll="true" tabindex="-1" id="offcanvasContext">
    <div class="offcanvas-header bg-dark text-white border-bottom border-secondary">
        <h5 class="offcanvas-title fs-6"><i class="fa-solid fa-robot me-2"></i>Contexto IA Generado</h5>
        <button type="button" class="btn-close btn-close-white" data-bs-dismiss="offcanvas"></button>
    </div>
    <div class="offcanvas-body bg-dark text-light p-0 d-flex flex-column">
        <div class="p-2 bg-secondary bg-opacity-25 d-flex justify-content-between align-items-center border-bottom border-secondary">
            <span class="small fw-bold text-info" id="contextCount">0 tablas</span>
            <button class="btn btn-sm btn-outline-light" onclick="copyContextAll()">Copiar Todo</button>
        </div>
        <pre id="finalContextJson" class="m-0 p-3 flex-fill json-container" style="overflow:auto;"></pre>
    </div>
</div>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>

<?php if($dbSeleccionada): ?>
<script>
    const rawData = <?php echo json_encode($dbData, JSON_UNESCAPED_UNICODE); ?>;
    const rawRels = <?php echo json_encode($relaciones); ?>;
    
    const CONFIG = {
        gridGap: 15,
        springLength: 100,
        baseGravity: -10
    };

    let contextData = {}; 
    let currentTable = null; 
    let currentMode = 'physics';
    const svgSwitchSize = 24;

    function createSVG(tableName, isSelected) {
        const tData = rawData[tableName];
        const columns = tData.columns || [];
        
        let maxLen = tableName.length * 10; 
        columns.forEach(c => {
            const rowLen = (c.name.length + c.type.length) * 7 + 50;
            if(rowLen > maxLen) maxLen = rowLen;
        });
        const width = Math.min(Math.max(maxLen, 200), 350);
        const headerHeight = 40;
        const rowHeight = 26;
        const height = headerHeight + (columns.length * rowHeight) + 6;
        
        const swX = width - svgSwitchSize - 10;
        const swY = (headerHeight - svgSwitchSize) / 2;
        const swFill = isSelected ? '#10b981' : 'rgba(255,255,255,0.2)';
        const swStroke = isSelected ? '#059669' : 'rgba(255,255,255,0.4)';
        const swKnob = isSelected ? 'white' : '#e2e8f0';

        let rows = "";
        columns.forEach((col, i) => {
            const y = headerHeight + (i * rowHeight);
            const bg = i%2===0 ? '#ffffff' : '#f8fafc';
            let icon = "🔹"; let color = "#64748b"; let w = "normal";
            if(col.key === 'PRI') { icon = "🔑"; color = "#db2777"; w = "bold"; }
            else if(col.key === 'MUL') { icon = "🔗"; color = "#2563eb"; }
            rows += `<rect x="1" y="${y}" width="${width-2}" height="${rowHeight}" fill="${bg}"/>
                     <text x="10" y="${y+17}" font-family="Arial" font-size="11" fill="${color}" font-weight="${w}">${icon} ${col.name}</text>
                     <text x="${width-10}" y="${y+17}" font-family="Arial" font-size="10" fill="#94a3b8" text-anchor="end">${col.type.replace(/\(.*\)/,'')}</text>`;
        });

        const svgXml = `
        <svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}">
            <rect x="0" y="0" width="${width}" height="${height}" rx="8" fill="white" stroke="#cbd5e1" stroke-width="1"/>
            <path d="M0 8 Q0 0 8 0 L${width-8} 0 Q${width} 0 ${width} 8 L${width} ${headerHeight} L0 ${headerHeight} Z" fill="#6366f1"/>
            <text x="12" y="25" font-family="Arial" font-size="13" font-weight="bold" fill="white">${tableName.toUpperCase()}</text>
            <g transform="translate(${swX}, ${swY})">
                <rect x="0" y="0" width="${svgSwitchSize}" height="${svgSwitchSize}" rx="6" fill="${swFill}" stroke="${swStroke}" stroke-width="1"/>
                <path d="M4 11 L9 16 L18 6" stroke="${swKnob}" stroke-width="2.5" fill="none" transform="scale(0.8) translate(4,4)" />
            </g>
            ${rows}
        </svg>`;

        return {
            url: "data:image/svg+xml;charset=utf-8," + encodeURIComponent(svgXml),
            w: width, h: height,
            swRegion: { x: swX, y: swY, size: svgSwitchSize }
        };
    }

    const nodes = new vis.DataSet();
    const edges = new vis.DataSet();
    const idToName = {}; const nameToId = {};
    let idCount = 1;

    for(const t in rawData) {
        idToName[idCount] = t;
        nameToId[t.toLowerCase()] = idCount;
        const s = createSVG(t, false);
        nodes.add({ 
            id: idCount, shape: 'image', image: s.url, label: ' ',
            _info: { w: s.w, h: s.h, sw: s.swRegion } 
        });
        idCount++;
    }

    rawRels.forEach(r => {
        const from = nameToId[r.from_table.toLowerCase()];
        const to = nameToId[r.to_table.toLowerCase()];
        if(from && to) {
            edges.add({ from, to, arrows:'to', color:{color:'#94a3b8'}, dashes:true });
        }
    });

    const container = document.getElementById('mynetwork');
    const network = new vis.Network(container, { nodes, edges }, {
        physics: {
            solver: 'forceAtlas2Based',
            forceAtlas2Based: { gravitationalConstant: -50, springLength: 100, damping: 0.4 }
        },
        interaction: { hover: true, tooltipDelay: 200 }
    });

    // Sincronizar switches de la barra lateral al inicio
    function syncSidebarSwitches() {
        for(const t in rawData) {
            const sw = document.getElementById('sw-side-'+t);
            if(sw) sw.checked = !!contextData[t];
        }
    }

    network.on("click", function(params) {
        if(params.nodes.length > 0) {
            const nodeId = params.nodes[0];
            const tableName = idToName[nodeId];
            const nodeData = nodes.get(nodeId);
            const clickPos = params.pointer.canvas;
            const nodePos = network.getPositions([nodeId])[nodeId];
            
            const nodeLeft = nodePos.x - (nodeData._info.w / 2);
            const nodeTop = nodePos.y - (nodeData._info.h / 2);
            const swAbsX = nodeLeft + nodeData._info.sw.x;
            const swAbsY = nodeTop + nodeData._info.sw.y;
            const swSize = nodeData._info.sw.size;

            if(clickPos.x >= swAbsX && clickPos.x <= swAbsX + swSize &&
               clickPos.y >= swAbsY && clickPos.y <= swAbsY + swSize) {
                toggleContext(tableName, nodeId);
            } else {
                openModal(tableName);
            }
        }
    });

    window.focusTable = function(t) {
        const id = nameToId[t.toLowerCase()];
        if(id) {
            network.selectNodes([id]);
            network.focus(id, { scale: 1.2, animation: true });
        }
    }

    function toggleContext(t, id=null) {
        if(!id) id = nameToId[t.toLowerCase()];
        
        if(contextData[t]) delete contextData[t];
        else contextData[t] = buildDeepSchema(t);
        
        const isSel = !!contextData[t];
        const s = createSVG(t, isSel);
        nodes.update({ id: id, image: s.url });
        
        // Sincronizar checkbox lateral
        const sw = document.getElementById('sw-side-'+t);
        if(sw) sw.checked = isSel;

        renderSidebar();
    }

    function buildDeepSchema(t) {
        const info = rawData[t];
        return {
            table: t,
            columns: info.columns.map(c => ({ name: c.name, type: c.type, key: c.key, null: c.null, default: c.default })),
            indexes: info.indexes || [],
            sample_row: info.sample || {} 
        };
    }

    function renderSidebar() {
        const count = Object.keys(contextData).length;
        document.getElementById('contextCount').innerText = count + " tablas";
        document.getElementById('btnContextCount').innerText = count;
        
        if(count > 0) {
            const finalObj = { 
                instruction: "Analiza el esquema de base de datos. Usa 'sample_row' para entender el formato de los datos.",
                schema: Object.values(contextData) 
            };
            document.getElementById('finalContextJson').innerText = JSON.stringify(finalObj, null, 2);
        } else {
            document.getElementById('finalContextJson').innerText = "// Selecciona tablas usando los switches...";
        }
    }

    const infoModal = new bootstrap.Modal(document.getElementById('infoModal'));
    function openModal(t) {
        currentTable = t;
        document.getElementById('modalTitle').innerText = t.toUpperCase();
        document.getElementById('chkModal').checked = !!contextData[t];
        let html = "";
        rawData[t].columns.forEach(c => {
            html += `<tr><td class="ps-4 fw-bold">${c.name}</td><td class="text-primary small">${c.type}</td><td class="small">${c.key}</td><td class="small text-muted">${c.extra}</td></tr>`;
        });
        document.getElementById('modalBody').innerHTML = html;
        document.getElementById('jsonCode').innerText = JSON.stringify(buildDeepSchema(t), null, 2);
        infoModal.show();
    }

    window.toggleFromModal = function() { if(currentTable) toggleContext(currentTable); };

    window.searchNode = function(val) {
        val = val.toLowerCase();
        const k = Object.keys(nameToId).find(n => n.includes(val));
        if(k) { network.selectNodes([nameToId[k]]); network.focus(nameToId[k], { scale: 1.2, animation: true }); }
    };

    window.setMode = function(mode) {
        currentMode = mode;
        if(mode === 'grid') {
            applyGridLayout();
        } else {
            updatePhysics(); 
        }
    }

    function applyGridLayout() {
        const canvasWidth = container.offsetWidth;
        const allNodes = nodes.get();
        allNodes.sort((a,b) => idToName[a.id].localeCompare(idToName[b.id]));
        
        let curX = -(canvasWidth/2) + 50;
        let curY = -200;
        let rowMaxH = 0;
        const gap = CONFIG.gridGap;

        network.setOptions({ physics: { enabled: false } });

        allNodes.forEach(node => {
            const w = node._info.w;
            const h = node._info.h;
            if(curX + w > (canvasWidth/2) - 50) {
                curX = -(canvasWidth/2) + 50;
                curY += rowMaxH + gap;
                rowMaxH = 0;
            }
            network.moveNode(node.id, curX + (w/2), curY + (h/2));
            if(h > rowMaxH) rowMaxH = h;
            curX += w + gap;
        });
        network.fit({ animation: true });
    }

    window.updatePhysics = function() {
        if(currentMode === 'grid') return;
        const val = parseInt(document.getElementById('rngGrav').value);
        document.getElementById('gravVal').innerText = val;
        const grav = CONFIG.baseGravity - val; 
        network.setOptions({ 
            physics: { enabled: true, forceAtlas2Based: { gravitationalConstant: grav, springLength: CONFIG.springLength } } 
        });
    }

    window.copyContextAll = function() {
        const text = document.getElementById('finalContextJson').innerText;
        navigator.clipboard.writeText(text).then(() => alert("¡Contexto copiado!"));
    }

    window.copyJson = function() {
        navigator.clipboard.writeText(document.getElementById('jsonCode').innerText).then(() => alert("¡JSON de tabla copiado!"));
    }
    
    window.toggleLabels = function() {
        const show = document.getElementById('chkLabels').checked;
        edges.update(edges.get().map(e => ({ id: e.id, hidden: !show })));
    }
</script>
<?php endif; ?>
</body>
</html>