/** * Theme Management */ const themeToggleBtn = document.getElementById('theme-toggle'); const htmlElement = document.documentElement; const sunIcon = document.getElementById('sun-icon'); const moonIcon = document.getElementById('moon-icon'); // Initialize Theme function initTheme() { const savedTheme = localStorage.getItem('theme'); const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; // Default to dark if no save, or if saved is dark const isDark = savedTheme === 'dark' || (!savedTheme && systemPrefersDark); applyTheme(isDark ? 'dark' : 'light'); } function applyTheme(theme) { htmlElement.setAttribute('data-theme', theme); localStorage.setItem('theme', theme); // Toggle Icons if (theme === 'dark') { sunIcon.style.display = 'block'; moonIcon.style.display = 'none'; } else { sunIcon.style.display = 'none'; moonIcon.style.display = 'block'; } // Trigger graph update if it exists (to recolor canvas/svg) if (window.updateGraphTheme) { window.updateGraphTheme(); } } themeToggleBtn.addEventListener('click', () => { const currentTheme = htmlElement.getAttribute('data-theme'); const newTheme = currentTheme === 'dark' ? 'light' : 'dark'; applyTheme(newTheme); }); // Initialize on load initTheme(); /** * Graph Visualization (Mock D3 Implementation for Demonstration) * Replaces or Augments existing logic */ const graphContainer = document.getElementById('graph-container'); const width = graphContainer.clientWidth; const height = graphContainer.clientHeight; // Clear loading text graphContainer.innerHTML = ''; // Create SVG const svg = d3.select("#graph-container") .append("svg") .attr("width", "100%") .attr("height", "100%") .call(d3.zoom().on("zoom", (event) => { g.attr("transform", event.transform); })); const g = svg.append("g"); // Sample Data const graphData = { nodes: [ { id: "Root", group: 1 }, { id: "Config", group: 2 }, { id: "Assets", group: 2 }, { id: "Theme", group: 2 }, { id: "Scripts", group: 3 }, { id: "Styles", group: 3 } ], links: [ { source: "Root", target: "Config" }, { source: "Root", target: "Assets" }, { source: "Assets", target: "Theme" }, { source: "Config", target: "Scripts" }, { source: "Config", target: "Styles" } ] }; // Simulation Setup const simulation = d3.forceSimulation(graphData.nodes) .force("link", d3.forceLink(graphData.links).id(d => d.id).distance(100)) .force("charge", d3.forceManyBody().strength(-300)) .force("center", d3.forceCenter(width / 2, height / 2)); // Elements const link = g.append("g") .attr("class", "links") .selectAll("line") .data(graphData.links) .enter().append("line"); const node = g.append("g") .attr("class", "nodes") .selectAll("circle") .data(graphData.nodes) .enter().append("circle") .attr("r", 8) .call(d3.drag() .on("start", dragstarted) .on("drag", dragged) .on("end", dragended)); const labels = g.append("g") .attr("class", "labels") .selectAll("text") .data(graphData.nodes) .enter().append("text") .attr("dx", 12) .attr("dy", ".35em") .text(d => d.id); // Sidebar population const nodeList = document.getElementById('node-list'); graphData.nodes.forEach(n => { const div = document.createElement('div'); div.className = 'node-list-item'; div.textContent = `> ${n.id}`; // Terminal style prefix div.addEventListener('click', () => { // Zoom to node logic could go here console.log(`Clicked ${n.id}`); }); nodeList.appendChild(div); }); // Simulation Tick simulation.on("tick", () => { link .attr("x1", d => d.source.x) .attr("y1", d => d.source.y) .attr("x2", d => d.target.x) .attr("y2", d => d.target.y); node .attr("cx", d => d.x) .attr("cy", d => d.y); labels .attr("x", d => d.x) .attr("y", d => d.y); }); // Drag functions function dragstarted(event, d) { if (!event.active) simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; } function dragged(event, d) { d.fx = event.x; d.fy = event.y; } function dragended(event, d) { if (!event.active) simulation.alphaTarget(0); d.fx = null; d.fy = null; } // Window Resize Handling window.addEventListener('resize', () => { simulation.force("center", d3.forceCenter(graphContainer.clientWidth / 2, graphContainer.clientHeight / 2)); simulation.alpha(0.3).restart(); }); // Expose update function for theme changes (CSS vars handle most, but sometimes D3 needs explicit updates) window.updateGraphTheme = function() { // Current CSS handles colors via variables, but if complex logic needed: // const isDark = htmlElement.getAttribute('data-theme') === 'dark'; // link.style('stroke', isDark ? '#...' : '#...'); };