Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Build Status Dashboard</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet"> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet"> | |
| <script> | |
| tailwind.config = { | |
| theme: { | |
| extend: { | |
| fontFamily: { | |
| sans: ['Inter', 'sans-serif'], | |
| mono: ['JetBrains Mono', 'monospace'], | |
| }, | |
| animation: { | |
| 'pulse-slow': 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite', | |
| 'float': 'float 6s ease-in-out infinite', | |
| 'slide-in': 'slideIn 0.5s ease-out', | |
| }, | |
| keyframes: { | |
| float: { | |
| '0%, 100%': { transform: 'translateY(0)' }, | |
| '50%': { transform: 'translateY(-20px)' }, | |
| }, | |
| slideIn: { | |
| '0%': { transform: 'translateY(20px)', opacity: '0' }, | |
| '100%': { transform: 'translateY(0)', opacity: '1' }, | |
| } | |
| } | |
| } | |
| } | |
| } | |
| </script> | |
| <style> | |
| .glass { | |
| background: rgba(17, 24, 39, 0.7); | |
| backdrop-filter: blur(10px); | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| } | |
| .gradient-text { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| } | |
| .status-dot { | |
| box-shadow: 0 0 20px currentColor; | |
| } | |
| .card-hover { | |
| transition: all 0.3s ease; | |
| } | |
| .card-hover:hover { | |
| transform: translateY(-5px); | |
| box-shadow: 0 20px 40px rgba(0,0,0,0.3); | |
| } | |
| /* Custom scrollbar */ | |
| ::-webkit-scrollbar { | |
| width: 8px; | |
| height: 8px; | |
| } | |
| ::-webkit-scrollbar-track { | |
| background: #1a1a1a; | |
| } | |
| ::-webkit-scrollbar-thumb { | |
| background: #4a4a4a; | |
| border-radius: 4px; | |
| } | |
| ::-webkit-scrollbar-thumb:hover { | |
| background: #666; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-900 text-white min-h-screen overflow-x-hidden"> | |
| <!-- Animated Background --> | |
| <div class="fixed inset-0 overflow-hidden pointer-events-none"> | |
| <div class="absolute top-0 left-1/4 w-96 h-96 bg-purple-600/20 rounded-full blur-3xl animate-float"></div> | |
| <div class="absolute bottom-0 right-1/4 w-96 h-96 bg-blue-600/20 rounded-full blur-3xl animate-float" style="animation-delay: 3s;"></div> | |
| <div class="absolute top-1/2 left-1/2 w-96 h-96 bg-pink-600/10 rounded-full blur-3xl animate-float" style="animation-delay: 1.5s;"></div> | |
| </div> | |
| <!-- Navigation --> | |
| <nav class="fixed top-0 w-full z-50 glass"> | |
| <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> | |
| <div class="flex items-center justify-between h-16"> | |
| <div class="flex items-center space-x-3"> | |
| <div class="relative"> | |
| <div class="w-10 h-10 bg-gradient-to-br from-purple-500 to-blue-500 rounded-lg flex items-center justify-center"> | |
| <i class="fas fa-cube text-white text-lg"></i> | |
| </div> | |
| <div class="absolute -top-1 -right-1 w-3 h-3 bg-green-400 rounded-full status-dot animate-pulse"></div> | |
| </div> | |
| <div> | |
| <h1 class="text-xl font-bold gradient-text">BuildStatus</h1> | |
| <p class="text-xs text-gray-400">v2.4.1 • Production</p> | |
| </div> | |
| </div> | |
| <div class="hidden md:flex items-center space-x-6"> | |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="text-sm text-gray-400 hover:text-white transition-colors flex items-center gap-2"> | |
| <span>Built with anycoder</span> | |
| <i class="fas fa-external-link-alt text-xs"></i> | |
| </a> | |
| <button class="bg-gray-800 hover:bg-gray-700 px-4 py-2 rounded-lg text-sm transition-colors"> | |
| <i class="fas fa-bell mr-2"></i>Notifications | |
| </button> | |
| <div class="w-8 h-8 bg-gradient-to-br from-pink-500 to-orange-400 rounded-full flex items-center justify-center text-sm font-bold"> | |
| JD | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </nav> | |
| <!-- Main Content --> | |
| <main class="pt-24 pb-12 px-4 sm:px-6 lg:px-8 max-w-7xl mx-auto relative z-10"> | |
| <!-- Welcome Message --> | |
| <div class="mb-8 animate-slide-in"> | |
| <h2 class="text-3xl font-bold mb-2">Dashboard Overview</h2> | |
| <p class="text-gray-400">Real-time build monitoring and deployment status across all environments.</p> | |
| </div> | |
| <!-- Status Cards --> | |
| <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8"> | |
| <!-- Production --> | |
| <div class="glass rounded-xl p-6 card-hover animate-slide-in" style="animation-delay: 0.1s;"> | |
| <div class="flex items-center justify-between mb-4"> | |
| <div class="w-12 h-12 bg-green-500/20 rounded-lg flex items-center justify-center"> | |
| <i class="fas fa-rocket text-green-400 text-xl"></i> | |
| </div> | |
| <span class="px-3 py-1 bg-green-500/20 text-green-400 text-xs rounded-full font-medium">Healthy</span> | |
| </div> | |
| <h3 class="text-lg font-semibold mb-1">Production</h3> | |
| <p class="text-2xl font-bold text-green-400 mb-2">99.99%</p> | |
| <p class="text-sm text-gray-400">Uptime last 30 days</p> | |
| <div class="mt-4 flex items-center text-xs text-gray-500"> | |
| <i class="fas fa-clock mr-1"></i> | |
| Last deploy: 2 hours ago | |
| </div> | |
| </div> | |
| <!-- Staging --> | |
| <div class="glass rounded-xl p-6 card-hover animate-slide-in" style="animation-delay: 0.2s;"> | |
| <div class="flex items-center justify-between mb-4"> | |
| <div class="w-12 h-12 bg-blue-500/20 rounded-lg flex items-center justify-center"> | |
| <i class="fas fa-flask text-blue-400 text-xl"></i> | |
| </div> | |
| <span class="px-3 py-1 bg-blue-500/20 text-blue-400 text-xs rounded-full font-medium">Active</span> | |
| </div> | |
| <h3 class="text-lg font-semibold mb-1">Staging</h3> | |
| <p class="text-2xl font-bold text-blue-400 mb-2">v2.5.0-rc</p> | |
| <p class="text-sm text-gray-400">Release candidate</p> | |
| <div class="mt-4 flex items-center text-xs text-gray-500"> | |
| <i class="fas fa-code-branch mr-1"></i> | |
| Branch: feature/new-dashboard | |
| </div> | |
| </div> | |
| <!-- Development --> | |
| <div class="glass rounded-xl p-6 card-hover animate-slide-in" style="animation-delay: 0.3s;"> | |
| <div class="flex items-center justify-between mb-4"> | |
| <div class="w-12 h-12 bg-purple-500/20 rounded-lg flex items-center justify-center"> | |
| <i class="fas fa-code text-purple-400 text-xl"></i> | |
| </div> | |
| <span class="px-3 py-1 bg-purple-500/20 text-purple-400 text-xs rounded-full font-medium">Building</span> | |
| </div> | |
| <h3 class="text-lg font-semibold mb-1">Development</h3> | |
| <p class="text-2xl font-bold text-purple-400 mb-2">#4,291</p> | |
| <p class="text-sm text-gray-400">Build number</p> | |
| <div class="mt-4 w-full bg-gray-700 rounded-full h-2"> | |
| <div class="bg-purple-400 h-2 rounded-full animate-pulse" style="width: 75%"></div> | |
| </div> | |
| </div> | |
| <!-- Tests --> | |
| <div class="glass rounded-xl p-6 card-hover animate-slide-in" style="animation-delay: 0.4s;"> | |
| <div class="flex items-center justify-between mb-4"> | |
| <div class="w-12 h-12 bg-pink-500/20 rounded-lg flex items-center justify-center"> | |
| <i class="fas fa-vial text-pink-400 text-xl"></i> | |
| </div> | |
| <span class="px-3 py-1 bg-green-500/20 text-green-400 text-xs rounded-full font-medium">Passing</span> | |
| </div> | |
| <h3 class="text-lg font-semibold mb-1">Test Suite</h3> | |
| <p class="text-2xl font-bold text-green-400 mb-2">1,247</p> | |
| <p class="text-sm text-gray-400">Tests passing</p> | |
| <div class="mt-4 flex gap-2"> | |
| <div class="flex-1 bg-green-500/20 rounded p-2 text-center"> | |
| <p class="text-xs text-green-400">Unit</p> | |
| <p class="text-sm font-bold text-green-400">98%</p> | |
| </div> | |
| <div class="flex-1 bg-green-500/20 rounded p-2 text-center"> | |
| <p class="text-xs text-green-400">E2E</p> | |
| <p class="text-sm font-bold text-green-400">94%</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Main Dashboard Content --> | |
| <div class="grid grid-cols-1 lg:grid-cols-3 gap-6"> | |
| <!-- Build Pipeline --> | |
| <div class="lg:col-span-2 glass rounded-xl p-6"> | |
| <div class="flex items-center justify-between mb-6"> | |
| <h3 class="text-lg font-semibold flex items-center"> | |
| <i class="fas fa-stream text-blue-400 mr-2"></i> | |
| Build Pipeline | |
| </h3> | |
| <div class="flex gap-2"> | |
| <button class="px-3 py-1 text-xs bg-gray-800 hover:bg-gray-700 rounded-lg transition-colors"> | |
| <i class="fas fa-filter mr-1"></i>Filter | |
| </button> | |
| <button class="px-3 py-1 text-xs bg-blue-600 hover:bg-blue-500 rounded-lg transition-colors"> | |
| <i class="fas fa-play mr-1"></i>Trigger Build | |
| </button> | |
| </div> | |
| </div> | |
| <div class="space-y-4" id="pipeline-container"> | |
| <!-- Pipeline items will be injected here --> | |
| </div> | |
| </div> | |
| <!-- Real-time Metrics --> | |
| <div class="space-y-6"> | |
| <!-- Performance Chart --> | |
| <div class="glass rounded-xl p-6"> | |
| <h3 class="text-lg font-semibold mb-4 flex items-center"> | |
| <i class="fas fa-chart-line text-green-400 mr-2"></i> | |
| Response Time | |
| </h3> | |
| <div class="relative h-40"> | |
| <canvas id="responseChart" class="w-full h-full"></canvas> | |
| </div> | |
| <div class="mt-4 grid grid-cols-3 gap-4 text-center"> | |
| <div> | |
| <p class="text-2xl font-bold text-green-400">45ms</p> | |
| <p class="text-xs text-gray-400">Avg</p> | |
| </div> | |
| <div> | |
| <p class="text-2xl font-bold text-yellow-400">89ms</p> | |
| <p class="text-xs text-gray-400">P95</p> | |
| </div> | |
| <div> | |
| <p class="text-2xl font-bold text-red-400">156ms</p> | |
| <p class="text-xs text-gray-400">P99</p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Error Rate --> | |
| <div class="glass rounded-xl p-6"> | |
| <h3 class="text-lg font-semibold mb-4 flex items-center"> | |
| <i class="fas fa-exclamation-triangle text-yellow-400 mr-2"></i> | |
| Error Rate | |
| </h3> | |
| <div class="flex items-center justify-between mb-4"> | |
| <div class="text-4xl font-bold text-white">0.02%</div> | |
| <span class="px-3 py-1 bg-green-500/20 text-green-400 text-sm rounded-full"> | |
| <i class="fas fa-arrow-down mr-1"></i>12% | |
| </span> | |
| </div> | |
| <div class="space-y-3"> | |
| <div class="flex items-center justify-between text-sm"> | |
| <span class="text-gray-400">4xx Errors</span> | |
| <span class="text-green-400">0.01%</span> | |
| </div> | |
| <div class="flex items-center justify-between text-sm"> | |
| <span class="text-gray-400">5xx Errors</span> | |
| <span class="text-green-400">0.01%</span> | |
| </div> | |
| <div class="flex items-center justify-between text-sm"> | |
| <span class="text-gray-400">Timeout</span> | |
| <span class="text-green-400">0.00%</span> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Quick Actions --> | |
| <div class="glass rounded-xl p-6"> | |
| <h3 class="text-lg font-semibold mb-4">Quick Actions</h3> | |
| <div class="space-y-3"> | |
| <button class="w-full flex items-center justify-between p-3 bg-gray-800 hover:bg-gray-700 rounded-lg transition-colors group"> | |
| <div class="flex items-center"> | |
| <i class="fas fa-rocket text-blue-400 mr-3"></i> | |
| <span class="text-sm">Deploy to Production</span> | |
| </div> | |
| <i class="fas fa-chevron-right text-gray-500 group-hover:text-white transition-colors"></i> | |
| </button> | |
| <button class="w-full flex items-center justify-between p-3 bg-gray-800 hover:bg-gray-700 rounded-lg transition-colors group"> | |
| <div class="flex items-center"> | |
| <i class="fas fa-undo text-yellow-400 mr-3"></i> | |
| <span class="text-sm">Rollback Last Deploy</span> | |
| </div> | |
| <i class="fas fa-chevron-right text-gray-500 group-hover:text-white transition-colors"></i> | |
| </button> | |
| <button class="w-full flex items-center justify-between p-3 bg-gray-800 hover:bg-gray-700 rounded-lg transition-colors group"> | |
| <div class="flex items-center"> | |
| <i class="fas fa-bug text-red-400 mr-3"></i> | |
| <span class="text-sm">Create Incident</span> | |
| </div> | |
| <i class="fas fa-chevron-right text-gray-500 group-hover:text-white transition-colors"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Live Terminal --> | |
| <div class="mt-6 glass rounded-xl p-6"> | |
| <div class="flex items-center justify-between mb-4"> | |
| <h3 class="text-lg font-semibold flex items-center"> | |
| <i class="fas fa-terminal text-green-400 mr-2"></i> | |
| Live Build Logs | |
| </h3> | |
| <div class="flex items-center gap-3"> | |
| <span class="flex items-center text-xs text-green-400"> | |
| <span class="w-2 h-2 bg-green-400 rounded-full mr-2 animate-pulse"></span> | |
| Live | |
| </span> | |
| <button class="text-gray-400 hover:text-white transition-colors"> | |
| <i class="fas fa-expand"></i> | |
| </button> | |
| <button class="text-gray-400 hover:text-white transition-colors"> | |
| <i class="fas fa-download"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="bg-gray-950 rounded-lg p-4 font-mono text-sm overflow-hidden"> | |
| <div id="terminal-content" class="space-y-1 max-h-64 overflow-y-auto"> | |
| <!-- Terminal lines will be injected here --> | |
| </div> | |
| </div> | |
| </div> | |
| </main> | |
| <!-- Footer --> | |
| <footer class="border-t border-gray-800 mt-12 py-8"> | |
| <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> | |
| <div class="flex flex-col md:flex-row justify-between items-center"> | |
| <div class="flex items-center space-x-2 mb-4 md:mb-0"> | |
| <i class="fas fa-cube text-purple-400"></i> | |
| <span class="text-gray-400">BuildStatus Dashboard</span> | |
| </div> | |
| <div class="flex items-center space-x-6 text-sm text-gray-500"> | |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="hover:text-purple-400 transition-colors flex items-center gap-2"> | |
| <i class="fas fa-heart text-pink-400"></i> | |
| Built with anycoder | |
| </a> | |
| <span>•</span> | |
| <span>v2.4.1</span> | |
| <span>•</span> | |
| <span>© 2025</span> | |
| </div> | |
| </div> | |
| </div> | |
| </footer> | |
| <script> | |
| // Pipeline data | |
| const pipelineStages = [ | |
| { name: 'Source', status: 'success', duration: '45s', icon: 'fa-code-branch' }, | |
| { name: 'Build', status: 'success', duration: '2m 30s', icon: 'fa-cube' }, | |
| { name: 'Test', status: 'success', duration: '3m 15s', icon: 'fa-vial' }, | |
| { name: 'Security Scan', status: 'running', duration: '1m 20s', icon: 'fa-shield-alt' }, | |
| { name: 'Deploy Staging', status: 'pending', duration: '-', icon: 'fa-rocket' }, | |
| ]; | |
| // Terminal messages | |
| const terminalMessages = [ | |
| { text: '[12:34:56] INFO: Build pipeline initialized', type: 'info' }, | |
| { text: '[12:34:57] INFO: Checking out repository...', type: 'info' }, | |
| { text: '[12:34:58] SUCCESS: Repository cloned successfully', type: 'success' }, | |
| { text: '[12:35:00] INFO: Installing dependencies...', type: 'info' }, | |
| { text: '[12:35:15] INFO: Running npm ci', type: 'info' }, | |
| { text: '[12:35:45] SUCCESS: Dependencies installed', type: 'success' }, | |
| { text: '[12:35:46] INFO: Running build script...', type: 'info' }, | |
| { text: '[12:36:10] INFO: Optimizing assets...', type: 'info' }, | |
| { text: '[12:36:30] SUCCESS: Build completed successfully', type: 'success' }, | |
| { text: '[12:36:31] INFO: Running test suite...', type: 'info' }, | |
| { text: '[12:37:00] INFO: Executing 1,247 tests...', type: 'info' }, | |
| { text: '[12:37:45] SUCCESS: All tests passed (1,247/1,247)', type: 'success' }, | |
| { text: '[12:37:46] INFO: Running security scan...', type: 'info' }, | |
| { text: '[12:38:00] WARN: Low severity vulnerability found in dependency', type: 'warning' }, | |
| { text: '[12:38:05] INFO: Auto-patching vulnerability...', type: 'info' }, | |
| { text: '[12:38:10] SUCCESS: Vulnerability patched', type: 'success' }, | |
| { text: '[12:38:15] INFO: Preparing deployment to staging...', type: 'info' }, | |
| ]; | |
| // Render pipeline | |
| function renderPipeline() { | |
| const container = document.getElementById('pipeline-container'); | |
| container.innerHTML = pipelineStages.map((stage, index) => { | |
| const statusColors = { | |
| success: 'bg-green-500', | |
| running: 'bg-blue-500 animate-pulse', | |
| pending: 'bg-gray-600', | |
| failed: 'bg-red-500' | |
| }; | |
| const statusIcons = { | |
| success: 'fa-check', | |
| running: 'fa-spinner fa-spin', | |
| pending: 'fa-clock', | |
| failed: 'fa-times' | |
| }; | |
| return ` | |
| <div class="flex items-center p-4 bg-gray-800/50 rounded-lg hover:bg-gray-800 transition-colors cursor-pointer group"> | |
| <div class="flex items-center flex-1"> | |
| <div class="w-10 h-10 rounded-lg ${statusColors[stage.status]} flex items-center justify-center mr-4"> | |
| <i class="fas ${statusIcons[stage.status]} text-white"></i> | |
| </div> | |
| <div> | |
| <p class="font-medium text-white group-hover:text-blue-400 transition-colors">${stage.name}</p> | |
| <p class="text-sm text-gray-400">${stage.duration}</p> | |
| </div> | |
| </div> | |
| ${index < pipelineStages.length - 1 ? ` | |
| <div class="hidden md:block mx-4 text-gray-600"> | |
| <i class="fas fa-chevron-right"></i> | |
| </div> | |
| ` : ''} | |
| </div> | |
| `; | |
| }).join(''); | |
| } | |
| // Render terminal | |
| let terminalIndex = 0; | |
| function renderTerminal() { | |
| const container = document.getElementById('terminal-content'); | |
| function addLine() { | |
| if (terminalIndex >= terminalMessages.length) { | |
| terminalIndex = 0; // Loop | |
| } | |
| const msg = terminalMessages[terminalIndex]; | |
| const colors = { | |
| info: 'text-blue-400', | |
| success: 'text-green-400', | |
| warning: 'text-yellow-400', | |
| error: 'text-red-400' | |
| }; | |
| const line = document.createElement('div'); | |
| line.className = `font-mono text-xs ${colors[msg.type]} opacity-0 animate-slide-in`; | |
| line.style.animation = 'slideIn 0.3s ease-out forwards'; | |
| line.innerHTML = `<span class="text-gray-500">${msg.text}</span>`; | |
| container.appendChild(line); | |
| container.scrollTop = container.scrollHeight; | |
| // Remove old lines to keep performance | |
| if (container.children.length > 50) { | |
| container.removeChild(container.firstChild); | |
| } | |
| terminalIndex++; | |
| } | |
| // Add lines periodically | |
| setInterval(addLine, 800); | |
| // Add initial lines | |
| for (let i = 0; i < 10; i++) { | |
| setTimeout(addLine, i * 100); | |
| } | |
| } | |
| // Canvas Chart | |
| function initChart() { | |
| const canvas = document.getElementById('responseChart'); | |
| const ctx = canvas.getContext('2d'); | |
| // Set canvas size | |
| function resize() { | |
| canvas.width = canvas.offsetWidth; | |
| canvas.height = canvas.offsetHeight; | |
| } | |
| resize(); | |
| window.addEventListener('resize', resize); | |
| // Data points | |
| const points = []; | |
| const maxPoints = 50; | |
| for (let i = 0; i < maxPoints; i++) { | |
| points.push(40 + Math.random() * 20 + Math.sin(i * 0.2) * 10); | |
| } | |
| let offset = 0; | |
| function draw() { | |
| ctx.fillStyle = '#0a0a0a'; | |
| ctx.fillRect(0, 0, canvas.width, canvas.height); | |
| // Draw grid | |
| ctx.strokeStyle = '#1a1a1a'; | |
| ctx.lineWidth = 1; | |
| for (let i = 0; i < canvas.width; i += 40) { | |
| ctx.beginPath(); | |
| ctx.moveTo(i, 0); | |
| ctx.lineTo(i, canvas.height); | |
| ctx.stroke(); | |
| } | |
| for (let i = 0; i < canvas.height; i += 30) { | |
| ctx.beginPath(); | |
| ctx.moveTo(0, i); | |
| ctx.lineTo(canvas.width, i); | |
| ctx.stroke(); | |
| } | |
| // Draw line | |
| const gradient = ctx.createLinearGradient(0, 0, canvas.width, 0); | |
| gradient.addColorStop(0, '#10b981'); | |
| gradient.addColorStop(0.5, '#3b82f6'); | |
| gradient.addColorStop(1, '#8b5cf6'); | |
| ctx.strokeStyle = gradient; | |
| ctx.lineWidth = 2; | |
| ctx.beginPath(); | |
| const step = canvas.width / (maxPoints - 1); | |
| for (let i = 0; i < points.length; i++) { | |
| const x = i * step; | |
| const y = canvas.height - (points[i] / 100 * canvas.height); | |
| if (i === 0) { | |
| ctx.moveTo(x, y); | |
| } else { | |
| ctx.lineTo(x, y); | |
| } | |
| } | |
| ctx.stroke(); | |
| // Update data | |
| points.shift(); | |
| points.push(40 + Math.random() * 20 + Math.sin(offset) * 10); | |
| offset += 0.1; | |
| requestAnimationFrame(draw); | |
| } | |
| draw(); | |
| } | |
| // Initialize | |
| document.addEventListener('DOMContentLoaded', () => { | |
| renderPipeline(); | |
| renderTerminal(); | |
| initChart(); | |
| }); | |
| </script> | |
| </body> | |
| </html> |