Spaces:
Running
Running
| /** | |
| * 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 ? '#...' : '#...'); | |
| }; | |