Revamp Web UI: Added Tailwind CSS CDN, cyberpunk glassmorphism layout, glowing neon visualizers, and enhanced telemetry gauges

This commit is contained in:
drjones
2026-08-13 06:46:03 -07:00
parent 1ea2a5dc91
commit 25a8af8d4e
3 changed files with 420 additions and 322 deletions

View File

@@ -1,89 +1,104 @@
/* Cyberpunk Glassmorphism Design Token System */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* Cyberpunk Design System & Styling Rules */
:root {
--bg-dark: #07090E;
--panel-bg: rgba(15, 23, 42, 0.75);
--cyan-glow: #00F0FF;
--violet-glow: #8A2BE2;
--emerald-glow: #00FF9D;
--amber-glow: #FFB800;
--rose-glow: #FF3366;
}
body {
background-color: var(--bg-dark);
background-image:
radial-gradient(at 0% 0%, rgba(0, 240, 255, 0.08) 0px, transparent 50%),
radial-gradient(at 100% 0%, rgba(138, 43, 226, 0.08) 0px, transparent 50%),
radial-gradient(at 50% 100%, rgba(0, 255, 157, 0.04) 0px, transparent 50%);
font-family: 'Inter', sans-serif;
color: #F8FAFC;
}
/* Glassmorphism Panel Component */
.glass-panel {
background: rgba(15, 23, 42, 0.7);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
/* Cyber Card Glassmorphism */
.cyber-card {
background: rgba(13, 18, 30, 0.85);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.08);
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37);
box-shadow: 0 10px 30px -10px rgba(0, 0, 0, 0.5);
transition: all 0.3s ease;
}
.glass-panel:hover {
border-color: rgba(0, 240, 255, 0.2);
.cyber-card:hover {
border-color: rgba(0, 240, 255, 0.25);
box-shadow: 0 15px 35px -10px rgba(0, 240, 255, 0.15);
}
/* Buttons */
.btn {
@apply px-4 py-2 rounded-xl text-xs font-bold font-mono tracking-wider transition-all duration-200 flex items-center gap-2 cursor-pointer border shadow-lg;
/* Neon Buttons */
.btn-neon {
padding: 0.5rem 1rem;
border-radius: 0.75rem;
font-size: 0.75rem;
font-weight: 800;
font-family: 'Fira Code', monospace;
letter-spacing: 0.05em;
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
display: inline-flex;
items-center: center;
gap: 0.5rem;
cursor: pointer;
border: 1px solid transparent;
user-select: none;
}
.btn-emerald {
background: linear-gradient(135deg, rgba(0, 255, 157, 0.2), rgba(0, 200, 120, 0.1));
border-color: rgba(0, 255, 157, 0.4);
color: #00FF9D;
box-shadow: 0 0 15px rgba(0, 255, 157, 0.15);
}
.btn-emerald:hover {
.btn-emerald:hover:not(:disabled) {
background: rgba(0, 255, 157, 0.3);
box-shadow: 0 0 20px rgba(0, 255, 157, 0.4);
box-shadow: 0 0 25px rgba(0, 255, 157, 0.5);
transform: translateY(-1px);
}
.btn-rose {
background: linear-gradient(135deg, rgba(255, 51, 102, 0.2), rgba(200, 40, 80, 0.1));
border-color: rgba(255, 51, 102, 0.4);
color: #FF3366;
box-shadow: 0 0 15px rgba(255, 51, 102, 0.15);
}
.btn-rose:hover {
.btn-rose:hover:not(:disabled) {
background: rgba(255, 51, 102, 0.3);
box-shadow: 0 0 20px rgba(255, 51, 102, 0.4);
box-shadow: 0 0 25px rgba(255, 51, 102, 0.5);
transform: translateY(-1px);
}
.btn-ghost {
background: rgba(30, 41, 59, 0.6);
background: rgba(30, 41, 59, 0.7);
border-color: rgba(255, 255, 255, 0.1);
color: #94A3B8;
}
.btn-ghost:hover {
background: rgba(51, 65, 85, 0.8);
color: #FFF;
.btn-ghost:hover:not(:disabled) {
background: rgba(51, 65, 85, 0.9);
color: #FFFFFF;
border-color: rgba(0, 240, 255, 0.3);
}
.btn-purple {
background: linear-gradient(135deg, rgba(138, 43, 226, 0.25), rgba(100, 30, 180, 0.15));
border-color: rgba(138, 43, 226, 0.5);
color: #C084FC;
box-shadow: 0 0 15px rgba(138, 43, 226, 0.15);
}
.btn-purple:hover {
.btn-purple:hover:not(:disabled) {
background: rgba(138, 43, 226, 0.35);
box-shadow: 0 0 20px rgba(138, 43, 226, 0.4);
box-shadow: 0 0 25px rgba(138, 43, 226, 0.5);
transform: translateY(-1px);
}
.btn-neon:disabled {
opacity: 0.4;
cursor: not-allowed;
transform: none !important;
box-shadow: none !important;
}
/* Status Badges */
.status-badge {
@apply px-3 py-1 rounded-full text-[11px] font-bold font-mono tracking-widest border uppercase inline-flex items-center gap-1.5;
padding: 0.25rem 0.75rem;
border-radius: 9999px;
font-size: 0.6875rem;
font-weight: 800;
font-family: 'Fira Code', monospace;
letter-spacing: 0.05em;
border: 1px solid transparent;
display: inline-flex;
align-items: center;
gap: 0.375rem;
text-transform: uppercase;
}
.status-idle {
@@ -96,12 +111,13 @@ body {
background: rgba(0, 240, 255, 0.15);
border-color: rgba(0, 240, 255, 0.4);
color: #00F0FF;
animation: pulse-glow 2s infinite;
box-shadow: 0 0 15px rgba(0, 240, 255, 0.3);
animation: badge-pulse 2s infinite;
}
@keyframes pulse-glow {
0%, 100% { box-shadow: 0 0 10px rgba(0, 240, 255, 0.3); }
50% { box-shadow: 0 0 25px rgba(0, 240, 255, 0.7); }
@keyframes badge-pulse {
0%, 100% { box-shadow: 0 0 10px rgba(0, 240, 255, 0.2); }
50% { box-shadow: 0 0 25px rgba(0, 240, 255, 0.6); }
}
@keyframes spin-reverse {
@@ -112,25 +128,35 @@ body {
animation: spin-reverse 3s linear infinite;
}
/* Scrollbar Customization */
/* Custom Scrollbar */
.custom-scrollbar::-webkit-scrollbar {
width: 6px;
height: 6px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: rgba(15, 23, 42, 0.6);
background: rgba(7, 9, 14, 0.8);
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: rgba(51, 65, 85, 0.8);
border-radius: 3px;
border-radius: 4px;
}
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
background: rgba(0, 240, 255, 0.5);
background: rgba(0, 240, 255, 0.6);
}
/* Terminal Log Badges */
/* Terminal Log Stream Elements */
.log-line {
@apply font-mono text-xs flex items-start gap-2 py-0.5 border-b border-slate-900/40 hover:bg-slate-900/50 px-1 rounded transition;
font-family: 'Fira Code', monospace;
font-size: 0.75rem;
display: flex;
align-items: flex-start;
gap: 0.5rem;
padding: 0.25rem 0.5rem;
border-radius: 0.375rem;
transition: background 0.15s ease;
}
.log-line:hover {
background: rgba(30, 41, 59, 0.4);
}
.log-timestamp {
color: #64748B;
@@ -143,8 +169,19 @@ body {
.badge-error { background: rgba(255, 51, 102, 0.15); color: #FF3366; border: 1px solid rgba(255, 51, 102, 0.3); }
.log-filter-btn {
@apply px-2 py-0.5 rounded text-[10px] font-bold text-slate-400 hover:text-white transition;
padding: 0.2rem 0.5rem;
border-radius: 0.375rem;
font-size: 0.625rem;
font-weight: 700;
font-family: 'Fira Code', monospace;
color: #94A3B8;
transition: all 0.15s ease;
}
.log-filter-btn:hover {
color: #FFFFFF;
}
.log-filter-btn.active {
@apply bg-cyan-500/20 text-cyan-400 border border-cyan-500/40;
background: rgba(0, 240, 255, 0.2);
color: #00F0FF;
border: 1px solid rgba(0, 240, 255, 0.4);
}

View File

@@ -1,300 +1,338 @@
<!DOCTYPE html>
<html lang="en">
<html lang="en" class="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI-Trainer | Unsloth MoE & GRPO Control Center</title>
<!-- Google Fonts & FontAwesome / Lucide -->
<!-- Google Fonts: Outfit, Inter, Fira Code -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500;600;700&family=Inter:wght@300;400;500;600;700;800;900&family=Outfit:wght@400;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<!-- FontAwesome Icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<!-- Tailwind CSS CDN -->
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
darkMode: 'class',
theme: {
extend: {
colors: {
cyberDark: '#07090E',
cyberCard: 'rgba(13, 18, 30, 0.85)',
cyberBorder: 'rgba(255, 255, 255, 0.08)',
neonCyan: '#00F0FF',
neonViolet: '#8A2BE2',
neonEmerald: '#00FF9D',
neonAmber: '#FFB800',
neonRose: '#FF3366'
},
fontFamily: {
sans: ['Inter', 'sans-serif'],
outfit: ['Outfit', 'sans-serif'],
mono: ['Fira Code', 'monospace']
}
}
}
}
</script>
<!-- Chart.js CDN -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<!-- Custom Styles -->
<link rel="stylesheet" href="css/style.css">
</head>
<body class="bg-dark text-light antialiased overflow-x-hidden">
<body class="bg-cyberDark text-slate-100 font-sans antialiased overflow-x-hidden min-h-screen flex flex-col">
<!-- Loading Screen Overlay -->
<div id="loadingOverlay" class="fixed inset-0 z-50 bg-black/90 backdrop-blur-md flex flex-col items-center justify-center transition-opacity duration-500 opacity-0 pointer-events-none">
<!-- Fullscreen Animated Loading Overlay -->
<div id="loadingOverlay" class="fixed inset-0 z-50 bg-cyberDark/95 backdrop-blur-xl flex flex-col items-center justify-center transition-all duration-500 opacity-0 pointer-events-none">
<div class="relative flex items-center justify-center">
<div class="w-24 h-24 border-4 border-cyan-500/20 border-t-cyan-400 rounded-full animate-spin"></div>
<div class="absolute w-16 h-16 border-4 border-violet-500/20 border-b-violet-400 rounded-full animate-spin-reverse"></div>
<div class="w-28 h-28 border-4 border-cyan-500/20 border-t-cyan-400 rounded-full animate-spin"></div>
<div class="absolute w-20 h-20 border-4 border-violet-500/20 border-b-violet-400 rounded-full animate-spin-reverse"></div>
<div class="absolute w-12 h-12 border-4 border-emerald-500/20 border-t-emerald-400 rounded-full animate-spin"></div>
<i class="fa-solid fa-brain text-2xl text-cyan-400 animate-pulse"></i>
</div>
<h3 class="mt-6 text-xl font-bold tracking-wider text-cyan-400 uppercase font-outfit" id="loadingTitle">Initializing Neural Environment...</h3>
<p class="text-sm text-slate-400 mt-2 font-mono" id="loadingSubtext">Loading Unsloth Triton Kernels & MoE Router Maps</p>
<h3 class="mt-8 text-2xl font-black tracking-widest text-transparent bg-clip-text bg-gradient-to-r from-cyan-400 via-violet-400 to-emerald-400 uppercase font-outfit" id="loadingTitle">INITIALIZING NEURAL HARNESS...</h3>
<p class="text-xs text-slate-400 mt-2 font-mono" id="loadingSubtext">Loading Unsloth Triton Kernels & MoE Layer Router Maps</p>
</div>
<!-- Main Container Layout -->
<div class="min-h-screen flex flex-col">
<!-- Top Navigation & Status Bar -->
<header class="border-b border-slate-800 bg-slate-950/80 backdrop-blur-xl sticky top-0 z-40 px-6 py-3 flex flex-wrap items-center justify-between gap-4">
<div class="flex items-center gap-4">
<div class="w-10 h-10 rounded-xl bg-gradient-to-br from-cyan-500 via-violet-600 to-fuchsia-600 p-[1px] shadow-lg shadow-cyan-500/20">
<div class="w-full h-full bg-slate-950 rounded-[11px] flex items-center justify-center">
<i class="fa-solid fa-microchip text-cyan-400 text-lg"></i>
</div>
<!-- Top Navigation Bar -->
<header class="border-b border-cyberBorder bg-slate-950/80 backdrop-blur-2xl sticky top-0 z-40 px-6 py-3.5 flex flex-wrap items-center justify-between gap-4">
<!-- Left: Brand Logo & Title -->
<div class="flex items-center gap-4">
<div class="relative group cursor-pointer">
<div class="absolute -inset-0.5 bg-gradient-to-r from-cyan-500 via-violet-600 to-emerald-500 rounded-xl blur opacity-60 group-hover:opacity-100 transition duration-300"></div>
<div class="relative w-11 h-11 bg-slate-950 rounded-xl flex items-center justify-center border border-white/10">
<i class="fa-solid fa-bolt text-cyan-400 text-xl group-hover:scale-110 transition duration-300"></i>
</div>
</div>
<div>
<div class="flex items-center gap-2.5">
<h1 class="text-xl font-black tracking-tight font-outfit text-white">AI-TRAINER</h1>
<span class="px-2.5 py-0.5 text-[10px] font-black tracking-widest bg-cyan-500/10 border border-cyan-500/30 text-cyan-400 rounded-full uppercase shadow-[0_0_10px_rgba(0,240,255,0.2)]">UNSLOTH GRPO MOE</span>
</div>
<p class="text-xs text-slate-400 font-mono">Proxify-ADB Autonomous Fleet Router & MoE Weight Shedder</p>
</div>
</div>
<!-- Middle: Live Telemetry Gauges -->
<div class="flex items-center gap-5 bg-slate-900/80 border border-cyberBorder px-5 py-2 rounded-2xl text-xs font-mono shadow-inner">
<div class="flex items-center gap-2.5">
<div class="w-2 h-2 rounded-full bg-cyan-400 animate-pulse"></div>
<span class="text-slate-400">GPU VRAM:</span>
<strong class="text-cyan-300" id="gaugeVram">6.2 / 16.0 GB</strong>
</div>
<div class="w-px h-4 bg-slate-800"></div>
<div class="flex items-center gap-2.5">
<div class="w-2 h-2 rounded-full bg-violet-400 animate-pulse"></div>
<span class="text-slate-400">MoE Stream RAM:</span>
<strong class="text-violet-300" id="gaugeRam">34.5 / 64.0 GB</strong>
</div>
<div class="w-px h-4 bg-slate-800"></div>
<div class="flex items-center gap-2.5">
<div class="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"></div>
<span class="text-slate-400">ADB Fleet:</span>
<strong class="text-emerald-400" id="gaugeAdb">14 / 14 Connected</strong>
</div>
</div>
<!-- Right: Header Controls -->
<div class="flex items-center gap-3">
<button id="btnStartPipeline" class="btn-neon btn-emerald">
<i class="fa-solid fa-play"></i> RUN PIPELINE
</button>
<button id="btnStopPipeline" class="btn-neon btn-rose" disabled>
<i class="fa-solid fa-square"></i> STOP
</button>
<button id="btnOpenKnobs" class="btn-neon btn-ghost">
<i class="fa-solid fa-sliders"></i> TUNING KNOBS
</button>
<button id="btnSyncGitea" class="btn-neon btn-purple">
<i class="fa-solid fa-code-branch"></i> PUSH TO GITEA
</button>
</div>
</header>
<!-- Main Content Layout -->
<main class="flex-1 p-6 space-y-6 max-w-[1920px] mx-auto w-full pb-80">
<!-- Pipeline Canvas Stage Section -->
<section class="cyber-card p-6 rounded-2xl relative overflow-hidden">
<div class="flex flex-wrap items-center justify-between gap-4 mb-4">
<div>
<div class="flex items-center gap-2">
<h1 class="text-lg font-black tracking-tight font-outfit text-white">AI-TRAINER</h1>
<span class="px-2 py-0.5 text-[10px] font-extrabold tracking-wider bg-cyan-500/10 border border-cyan-500/30 text-cyan-400 rounded-full uppercase">UNSLOTH GRPO MOE</span>
<h2 class="text-lg font-black font-outfit text-white flex items-center gap-2.5">
<i class="fa-solid fa-diagram-project text-cyan-400"></i>
AUTONOMOUS UN SLOTH GRPO PIPELINE MAP
</h2>
<p class="text-xs text-slate-400">Real-time dynamic expert pruning, hard execution sandbox evaluation, and GGUF quant deployment</p>
</div>
<div class="flex items-center gap-4 text-xs font-mono">
<span class="status-badge status-idle" id="pipelineStatusBadge">STATUS: IDLE</span>
<div class="flex items-center gap-2 bg-slate-900/90 px-3 py-1 rounded-xl border border-slate-800">
<span class="text-slate-400">Overall Progress:</span>
<strong class="text-cyan-400 text-sm" id="overallProgressText">0%</strong>
</div>
<p class="text-xs text-slate-400 font-mono">Proxify-ADB Fleet Orchestrator & MoE Pruner</p>
</div>
</div>
<!-- Live Telemetry Quick Gauges -->
<div class="flex items-center gap-6 bg-slate-900/60 border border-slate-800 px-4 py-1.5 rounded-xl text-xs font-mono">
<div class="flex items-center gap-2">
<i class="fa-solid fa-memory text-cyan-400"></i>
<span class="text-slate-400">VRAM:</span>
<span class="font-bold text-cyan-300" id="gaugeVram">6.2 / 16.0 GB</span>
</div>
<div class="w-px h-4 bg-slate-800"></div>
<div class="flex items-center gap-2">
<i class="fa-solid fa-server text-violet-400"></i>
<span class="text-slate-400">RAM:</span>
<span class="font-bold text-violet-300" id="gaugeRam">34.5 / 64.0 GB</span>
</div>
<div class="w-px h-4 bg-slate-800"></div>
<div class="flex items-center gap-2">
<i class="fa-solid fa-[#00FF9D] fa-mobile-screen-button text-emerald-400"></i>
<span class="text-slate-400">ADB Fleet:</span>
<span class="font-bold text-emerald-400" id="gaugeAdb">14 / 14 Online</span>
</div>
<!-- Interactive Canvas Graph -->
<div class="relative w-full h-[240px] bg-slate-950/80 rounded-xl border border-slate-800/80 overflow-hidden shadow-2xl">
<canvas id="pipelineCanvas" class="w-full h-full block"></canvas>
</div>
</section>
<!-- Action Button Group -->
<div class="flex items-center gap-3">
<button id="btnStartPipeline" class="btn btn-emerald">
<i class="fa-solid fa-play"></i> Run Pipeline
</button>
<button id="btnStopPipeline" class="btn btn-rose" disabled>
<i class="fa-solid fa-square"></i> Stop
</button>
<button id="btnOpenKnobs" class="btn btn-ghost">
<i class="fa-solid fa-sliders"></i> Tuning Knobs
</button>
<button id="btnSyncGitea" class="btn btn-purple">
<i class="fa-solid fa-code-branch"></i> Push to Gitea
</button>
</div>
</header>
<!-- Telemetry & Controls Grid -->
<div class="grid grid-cols-1 lg:grid-cols-12 gap-6">
<!-- Main Workspace Grid -->
<main class="flex-1 p-6 space-y-6 max-w-[1920px] mx-auto w-full pb-72">
<!-- Column 1: Charts & Expert Distributions (7 cols) -->
<div class="lg:col-span-7 space-y-6">
<!-- Visual Pipeline Flow Canvas Container -->
<section class="glass-panel p-6 rounded-2xl relative overflow-hidden">
<div class="flex items-center justify-between mb-4">
<div>
<h2 class="text-base font-bold font-outfit text-white flex items-center gap-2">
<i class="fa-solid fa-diagram-project text-cyan-400"></i>
AUTONOMOUS FINE-TUNING PIPELINE VISUALIZER
</h2>
<p class="text-xs text-slate-400">Interactive execution path with dynamic layer pruning & execution rewards</p>
</div>
<div class="flex items-center gap-3 text-xs font-mono">
<span class="status-badge status-idle" id="pipelineStatusBadge">STATUS: IDLE</span>
<span class="text-slate-400">Progress: <strong class="text-white" id="overallProgressText">0%</strong></span>
</div>
</div>
<!-- Interactive Canvas Pipeline Graph -->
<div class="relative w-full h-[220px] bg-slate-950/60 rounded-xl border border-slate-800/80 overflow-hidden">
<canvas id="pipelineCanvas" class="w-full h-full block"></canvas>
</div>
</section>
<!-- Dashboard Telemetry Grid (2 Columns) -->
<div class="grid grid-cols-1 lg:grid-cols-12 gap-6">
<!-- Column 1: GRPO Reward Convergence Chart & MoE Distribution (7 cols) -->
<div class="lg:col-span-7 space-y-6">
<!-- GRPO Reward Convergence Card -->
<div class="glass-panel p-5 rounded-2xl">
<div class="flex items-center justify-between mb-4">
<!-- GRPO Reward Convergence Card -->
<div class="cyber-card p-5 rounded-2xl">
<div class="flex items-center justify-between mb-4">
<div>
<h3 class="text-sm font-bold font-outfit text-white flex items-center gap-2">
<i class="fa-solid fa-chart-line text-emerald-400"></i>
UNSLOTH GRPO HARD EXECUTION REWARD CONVERGENCE
</h3>
<span class="text-xs font-mono text-emerald-400 bg-emerald-500/10 px-2 py-0.5 rounded border border-emerald-500/20">
Step <span id="grpoStepVal">0</span> / 300
</span>
</div>
<div class="h-[220px] w-full">
<canvas id="grpoChart"></canvas>
<p class="text-[11px] text-slate-400">Multi-vector reward curve ($R_{exec} = +3.0$, $R_{anti-hesit} = +2.0$, Safety = $-10.0$)</p>
</div>
<span class="text-xs font-mono text-emerald-400 bg-emerald-500/10 px-3 py-1 rounded-xl border border-emerald-500/30 font-bold">
Step <span id="grpoStepVal">0</span> / 300
</span>
</div>
<div class="h-[220px] w-full">
<canvas id="grpoChart"></canvas>
</div>
</div>
<!-- MoE Expert Pruning Distribution Card -->
<div class="glass-panel p-5 rounded-2xl">
<div class="flex items-center justify-between mb-4">
<!-- MoE Expert Pruning Distribution Card -->
<div class="cyber-card p-5 rounded-2xl">
<div class="flex items-center justify-between mb-4">
<div>
<h3 class="text-sm font-bold font-outfit text-white flex items-center gap-2">
<i class="fa-solid fa-scissors text-amber-400"></i>
DOMAIN MoE EXPERT RETENTION VS PRUNED TRIVIA EXPERTS
</h3>
<span class="text-xs font-mono text-amber-400 bg-amber-500/10 px-2 py-0.5 rounded border border-amber-500/20">
Retained: <strong id="retainedExpertsVal">64</strong> / 256
</span>
</div>
<div class="h-[180px] w-full">
<canvas id="moeChart"></canvas>
<p class="text-[11px] text-slate-400">Dropping ~68.75% dormant botany/history experts across 61 layers</p>
</div>
<span class="text-xs font-mono text-amber-400 bg-amber-500/10 px-3 py-1 rounded-xl border border-amber-500/30 font-bold">
Retained: <span id="retainedExpertsVal">64</span> / 256
</span>
</div>
<div class="h-[180px] w-full">
<canvas id="moeChart"></canvas>
</div>
</div>
<!-- Column 2: Fleet Topology & Script Code Inspector (5 cols) -->
<div class="lg:col-span-5 space-y-6">
</div>
<!-- Fleet & Proxmox Bridge Network Status Card -->
<div class="glass-panel p-5 rounded-2xl">
<h3 class="text-sm font-bold font-outfit text-white flex items-center gap-2 mb-3">
<!-- Column 2: Fleet Topology & Script Code Inspector (5 cols) -->
<div class="lg:col-span-5 space-y-6">
<!-- Proxmox Fleet Topology Card -->
<div class="cyber-card p-5 rounded-2xl">
<div class="flex items-center justify-between mb-3">
<h3 class="text-sm font-bold font-outfit text-white flex items-center gap-2">
<i class="fa-solid fa-network-wired text-cyan-400"></i>
10.30.20.0/24 PROXMOX FLEET TOPOLOGY
</h3>
<span class="text-[10px] font-mono text-cyan-400 bg-cyan-500/10 px-2 py-0.5 rounded border border-cyan-500/30">vmbr1 Isolated</span>
</div>
<div class="grid grid-cols-2 gap-3 text-xs font-mono mb-4">
<div class="bg-slate-900/80 border border-slate-800 p-3 rounded-xl">
<span class="text-slate-400 block text-[11px]">Control Gateway</span>
<strong class="text-cyan-300 text-sm">10.30.20.1</strong>
<div class="mt-1 text-[10px] text-slate-500">vmbr1 (Isolated Bridge)</div>
</div>
<div class="bg-slate-900/80 border border-slate-800 p-3 rounded-xl">
<span class="text-slate-400 block text-[11px]">DHCP Pool</span>
<strong class="text-violet-300 text-sm">.100 - .254</strong>
<div class="mt-1 text-[10px] text-slate-500">dnsmasq 12h Leases</div>
</div>
<div class="grid grid-cols-2 gap-3 text-xs font-mono mb-4">
<div class="bg-slate-950/80 border border-slate-800 p-3 rounded-xl">
<span class="text-slate-400 block text-[10px]">Gateway Control Node</span>
<strong class="text-cyan-300 text-sm">10.30.20.1</strong>
<div class="mt-1 text-[10px] text-slate-500">Port 5555 / REST API</div>
</div>
<!-- Active VM Fleet Node Grid -->
<div class="space-y-2 max-h-[180px] overflow-y-auto custom-scrollbar pr-1" id="fleetNodeList">
<!-- Dynamically Populated Fleet Cards -->
<div class="bg-slate-950/80 border border-slate-800 p-3 rounded-xl">
<span class="text-slate-400 block text-[10px]">Dynamic DHCP Pool</span>
<strong class="text-violet-300 text-sm">.100 ──► .254</strong>
<div class="mt-1 text-[10px] text-slate-500">dnsmasq 12h Leases</div>
</div>
</div>
<!-- Python Script Viewer & Sandbox Card -->
<div class="glass-panel p-5 rounded-2xl flex flex-col h-[280px]">
<div class="flex items-center justify-between mb-3">
<h3 class="text-sm font-bold font-outfit text-white flex items-center gap-2">
<i class="fa-brands fa-python text-yellow-400"></i>
PIPELINE SCRIPT INSPECTOR
</h3>
<select id="scriptSelector" class="bg-slate-900 border border-slate-800 text-xs font-mono text-cyan-400 px-2 py-1 rounded">
<option value="harvester.py">harvester.py</option>
<option value="prune_moe.py">prune_moe.py</option>
<option value="harness_env.py">harness_env.py</option>
<option value="train_grpo.py">train_grpo.py</option>
<option value="deploy.py">deploy.py</option>
<option value="state_eye.py">state_eye.py</option>
</select>
</div>
<pre class="flex-1 bg-slate-950 p-3 rounded-xl border border-slate-900 text-xs font-mono text-slate-300 overflow-auto custom-scrollbar leading-relaxed" id="scriptCodeView"># Loading script content...</pre>
<!-- Active VM Nodes Grid -->
<div class="space-y-2 max-h-[190px] overflow-y-auto custom-scrollbar pr-1" id="fleetNodeList">
<!-- Dynamically Rendered Cards -->
</div>
</div>
</div>
</main>
<!-- Bottom Collapsible Rolling Cyber Terminal -->
<div id="terminalContainer" class="fixed bottom-0 left-0 right-0 z-30 bg-slate-950/95 border-t border-slate-800/90 shadow-2xl backdrop-blur-2xl transition-all duration-300">
<!-- Terminal Controls & Header Bar -->
<div class="px-6 py-2.5 bg-slate-900/90 border-b border-slate-800 flex items-center justify-between gap-4 text-xs font-mono">
<div class="flex items-center gap-3">
<button id="btnToggleTerminal" class="text-slate-400 hover:text-white transition">
<i class="fa-solid fa-chevron-down" id="terminalChevron"></i>
</button>
<span class="font-bold text-cyan-400 flex items-center gap-2">
<i class="fa-solid fa-terminal"></i> LIVE LOG STREAM
</span>
<span class="bg-slate-800 px-2 py-0.5 rounded text-[10px] text-slate-400" id="logCountBadge">0 Logs</span>
</div>
<!-- Filter & Search Controls -->
<div class="flex items-center gap-3">
<div class="flex items-center gap-1 bg-slate-950 px-2 py-1 rounded border border-slate-800">
<span class="text-slate-500 text-[10px]">Filter:</span>
<button class="log-filter-btn active" data-filter="ALL">ALL</button>
<button class="log-filter-btn" data-filter="INFO">INFO</button>
<button class="log-filter-btn" data-filter="HARNESS">HARNESS</button>
<button class="log-filter-btn" data-filter="REWARD">REWARD</button>
<button class="log-filter-btn" data-filter="WARN">WARN</button>
<!-- Python Script Viewer Card -->
<div class="cyber-card p-5 rounded-2xl flex flex-col h-[280px]">
<div class="flex items-center justify-between mb-3">
<h3 class="text-sm font-bold font-outfit text-white flex items-center gap-2">
<i class="fa-brands fa-python text-yellow-400"></i>
PIPELINE SCRIPT INSPECTOR
</h3>
<select id="scriptSelector" class="bg-slate-950 border border-slate-800 text-xs font-mono text-cyan-400 px-3 py-1 rounded-xl focus:outline-none focus:border-cyan-500">
<option value="harvester.py">harvester.py</option>
<option value="prune_moe.py">prune_moe.py</option>
<option value="harness_env.py">harness_env.py</option>
<option value="train_grpo.py">train_grpo.py</option>
<option value="deploy.py">deploy.py</option>
<option value="state_eye.py">state_eye.py</option>
</select>
</div>
<button id="btnAutoScroll" class="px-2 py-1 rounded bg-cyan-500/10 text-cyan-400 border border-cyan-500/30 text-[11px] font-bold">
<i class="fa-solid fa-angles-down"></i> Auto-Scroll
</button>
<button id="btnClearLogs" class="px-2 py-1 rounded bg-slate-800 text-slate-400 hover:text-white text-[11px]">
<i class="fa-solid fa-trash-can"></i> Clear
</button>
<pre class="flex-1 bg-slate-950 p-4 rounded-xl border border-slate-900 text-xs font-mono text-slate-300 overflow-auto custom-scrollbar leading-relaxed" id="scriptCodeView"># Select a script to view source code...</pre>
</div>
</div>
<!-- Log Output Scroll Container -->
<div id="terminalBody" class="h-56 p-4 font-mono text-xs overflow-y-auto space-y-1 custom-scrollbar bg-slate-950/90 select-text">
<!-- Log Lines Streamed Here -->
</div>
</div>
<!-- Tuning Knobs Modal -->
<div id="knobsModal" class="fixed inset-0 z-50 bg-black/80 backdrop-blur-md flex items-center justify-center p-4 opacity-0 pointer-events-none transition-opacity duration-300">
<div class="glass-panel max-w-lg w-full p-6 rounded-2xl border border-slate-800 space-y-6">
<div class="flex items-center justify-between border-b border-slate-800 pb-3">
<h3 class="text-lg font-bold font-outfit text-white flex items-center gap-2">
<i class="fa-solid fa-sliders text-cyan-400"></i> TUNING KNOBS & REWARD WEIGHTS
</h3>
<button id="btnCloseKnobs" class="text-slate-400 hover:text-white"><i class="fa-solid fa-xmark text-lg"></i></button>
</main>
<!-- Sticky Collapsible Cyber Terminal Log Console at Bottom -->
<div id="terminalContainer" class="fixed bottom-0 left-0 right-0 z-30 bg-slate-950/95 border-t border-slate-800 shadow-2xl backdrop-blur-2xl transition-all duration-300">
<!-- Terminal Header & Action Bar -->
<div class="px-6 py-2.5 bg-slate-900/90 border-b border-slate-800 flex flex-wrap items-center justify-between gap-4 text-xs font-mono">
<div class="flex items-center gap-3">
<button id="btnToggleTerminal" class="w-7 h-7 rounded-lg bg-slate-800 hover:bg-slate-700 text-slate-300 hover:text-white flex items-center justify-center transition">
<i class="fa-solid fa-chevron-down text-xs" id="terminalChevron"></i>
</button>
<span class="font-bold text-cyan-400 flex items-center gap-2 text-sm">
<i class="fa-solid fa-terminal"></i> LIVE LOG STREAM CONSOLE
</span>
<span class="bg-cyan-500/10 border border-cyan-500/30 text-cyan-400 px-2.5 py-0.5 rounded-full text-[10px] font-bold" id="logCountBadge">0 Logs</span>
</div>
<!-- Filter Buttons & Controls -->
<div class="flex items-center gap-3">
<div class="flex items-center gap-1 bg-slate-950 px-2 py-1 rounded-xl border border-slate-800">
<span class="text-slate-500 text-[10px] px-1 font-bold">LEVEL:</span>
<button class="log-filter-btn active" data-filter="ALL">ALL</button>
<button class="log-filter-btn" data-filter="INFO">INFO</button>
<button class="log-filter-btn" data-filter="HARNESS">HARNESS</button>
<button class="log-filter-btn" data-filter="REWARD">REWARD</button>
<button class="log-filter-btn" data-filter="WARN">WARN</button>
</div>
<div class="space-y-4 text-xs font-mono">
<div>
<label class="flex justify-between text-slate-300 mb-1">
<span>Retained MoE Experts per Layer:</span>
<strong class="text-cyan-400" id="knobExpertsVal">64 / 256</strong>
</label>
<input type="range" id="knobExperts" min="16" max="128" step="16" value="64" class="w-full accent-cyan-400">
</div>
<div>
<label class="flex justify-between text-slate-300 mb-1">
<span>Hard Execution Reward Weight ($w_1$):</span>
<strong class="text-emerald-400" id="knobExecVal">3.0</strong>
</label>
<input type="range" id="knobExec" min="1.0" max="5.0" step="0.5" value="3.0" class="w-full accent-emerald-400">
</div>
<div>
<label class="flex justify-between text-slate-300 mb-1">
<span>Anti-Hesitation Penalty Weight ($w_2$):</span>
<strong class="text-amber-400" id="knobHesitVal">2.0</strong>
</label>
<input type="range" id="knobHesit" min="0.5" max="4.0" step="0.5" value="2.0" class="w-full accent-amber-400">
</div>
<div>
<label class="flex justify-between text-slate-300 mb-1">
<span>Target VRAM Footprint:</span>
<strong class="text-violet-400">16.0 GB (NVIDIA CUDA)</strong>
</label>
</div>
</div>
<div class="flex justify-end gap-3 pt-3 border-t border-slate-800">
<button id="btnSaveKnobs" class="btn btn-emerald w-full">Save & Apply Parameters</button>
</div>
<button id="btnAutoScroll" class="px-3 py-1 rounded-xl bg-cyan-500/20 text-cyan-400 border border-cyan-500/40 text-[11px] font-bold hover:bg-cyan-500/30 transition">
<i class="fa-solid fa-angles-down"></i> Auto-Scroll
</button>
<button id="btnClearLogs" class="px-3 py-1 rounded-xl bg-slate-800 text-slate-400 hover:text-white text-[11px] border border-slate-700 transition">
<i class="fa-solid fa-trash-can"></i> Clear
</button>
</div>
</div>
<!-- Rolling Terminal Stream Body -->
<div id="terminalBody" class="h-60 p-4 font-mono text-xs overflow-y-auto space-y-1 custom-scrollbar bg-slate-950 select-text">
<!-- Live Stream Logs Added Here -->
</div>
</div>
<!-- Tuning Knobs Modal -->
<div id="knobsModal" class="fixed inset-0 z-50 bg-black/80 backdrop-blur-xl flex items-center justify-center p-4 opacity-0 pointer-events-none transition-opacity duration-300">
<div class="cyber-card max-w-lg w-full p-6 rounded-2xl border border-slate-800 space-y-6 shadow-2xl">
<div class="flex items-center justify-between border-b border-slate-800 pb-3">
<h3 class="text-lg font-black font-outfit text-white flex items-center gap-2">
<i class="fa-solid fa-sliders text-cyan-400"></i> TUNING KNOBS & REWARD WEIGHTS
</h3>
<button id="btnCloseKnobs" class="text-slate-400 hover:text-white text-lg"><i class="fa-solid fa-xmark"></i></button>
</div>
<div class="space-y-5 text-xs font-mono">
<div>
<label class="flex justify-between text-slate-300 mb-1.5">
<span>Retained MoE Experts per Layer:</span>
<strong class="text-cyan-400 text-sm" id="knobExpertsVal">64 / 256</strong>
</label>
<input type="range" id="knobExperts" min="16" max="128" step="16" value="64" class="w-full accent-cyan-400">
</div>
<div>
<label class="flex justify-between text-slate-300 mb-1.5">
<span>Hard Execution Reward Weight ($w_1$):</span>
<strong class="text-emerald-400 text-sm" id="knobExecVal">3.0</strong>
</label>
<input type="range" id="knobExec" min="1.0" max="5.0" step="0.5" value="3.0" class="w-full accent-emerald-400">
</div>
<div>
<label class="flex justify-between text-slate-300 mb-1.5">
<span>Anti-Hesitation Penalty Weight ($w_2$):</span>
<strong class="text-amber-400 text-sm" id="knobHesitVal">2.0</strong>
</label>
<input type="range" id="knobHesit" min="0.5" max="4.0" step="0.5" value="2.0" class="w-full accent-amber-400">
</div>
<div class="bg-slate-950 p-3 rounded-xl border border-slate-800">
<span class="text-slate-400 block text-[11px]">Hardware Memory Target:</span>
<strong class="text-violet-300">16.0 GB VRAM + 64.0 GB System RAM Streaming</strong>
</div>
</div>
<div class="flex justify-end gap-3 pt-3 border-t border-slate-800">
<button id="btnSaveKnobs" class="btn-neon btn-emerald w-full justify-center">SAVE & APPLY PARAMETERS</button>
</div>
</div>
</div>
<!-- Scripts -->

View File

@@ -1,20 +1,22 @@
/**
* pipeline.js - Interactive Canvas Pipeline Visualizer
* Renders nodes, dynamic connections, glowing particle pulses, and interactive states.
* pipeline.js - Interactive HTML5 Canvas Pipeline Visualizer
* Renders glowing neon nodes, animated pulse particles, laser beams, and stage cards.
*/
class PipelineVisualizer {
constructor(canvasId) {
this.canvas = document.getElementById(canvasId);
this.ctx = this.canvas.getContext('2d');
this.nodes = [
{ id: 'harvester', name: '1. Config Harvester', icon: '⚡', x: 0.12, y: 0.5, status: 'idle', desc: 'Scrapes /etc/dnsmasq.d & ADB nodes' },
{ id: 'pruner', name: '2. MoE Expert Pruner', icon: '✂️', x: 0.28, y: 0.5, status: 'idle', desc: 'Drops 70% dormant trivia experts (256->64)' },
{ id: 'harness', name: '3. Execution Gym', icon: '🛡️', x: 0.44, y: 0.5, status: 'idle', desc: 'Command safety sandbox & STDERR check' },
{ id: 'grpo', name: '4. Unsloth GRPO', icon: '🧠', x: 0.60, y: 0.5, status: 'idle', desc: 'Hard execution & anti-hesitation rewards' },
{ id: 'export', name: '5. GGUF Quantizer', icon: '📦', x: 0.76, y: 0.5, status: 'idle', desc: 'Dual-offload Q4_K_M GGUF packaging' },
{ id: 'telemetry', name: '6. State Eye Daemon', icon: '👁️', x: 0.90, y: 0.5, status: 'idle', desc: '10.30.20.1 live state context injector' }
{ id: 'harvester', name: '1. Config Harvester', icon: '⚡', x: 0.10, y: 0.5, status: 'idle', color: '#00F0FF', desc: 'Scrapes /etc/dnsmasq.d & ADB nodes' },
{ id: 'pruner', name: '2. MoE Expert Pruner', icon: '✂️', x: 0.26, y: 0.5, status: 'idle', color: '#FFB800', desc: 'Drops 70% dormant trivia experts (256->64)' },
{ id: 'harness', name: '3. Execution Gym', icon: '🛡️', x: 0.42, y: 0.5, status: 'idle', color: '#8A2BE2', desc: 'Command safety sandbox & STDERR check' },
{ id: 'grpo', name: '4. Unsloth GRPO', icon: '🧠', x: 0.58, y: 0.5, status: 'idle', color: '#00FF9D', desc: 'Hard execution & anti-hesitation rewards' },
{ id: 'export', name: '5. GGUF Quantizer', icon: '📦', x: 0.74, y: 0.5, status: 'idle', color: '#C084FC', desc: 'Dual-offload Q4_K_M GGUF packaging' },
{ id: 'telemetry', name: '6. State Eye Daemon', icon: '👁️', x: 0.90, y: 0.5, status: 'idle', color: '#00F0FF', desc: '10.30.20.1 live state context injector' }
];
this.particles = [];
this.activeStage = null;
this.animId = null;
@@ -27,17 +29,20 @@ class PipelineVisualizer {
resize() {
const parent = this.canvas.parentElement;
this.canvas.width = parent.clientWidth;
this.canvas.height = parent.clientHeight;
if (parent) {
this.canvas.width = parent.clientWidth;
this.canvas.height = parent.clientHeight;
}
}
initParticles() {
this.particles = [];
for (let i = 0; i < 20; i++) {
for (let i = 0; i < 25; i++) {
this.particles.push({
progress: Math.random(),
speed: 0.003 + Math.random() * 0.004,
size: 3 + Math.random() * 2
size: 3 + Math.random() * 2,
alpha: 0.5 + Math.random() * 0.5
});
}
}
@@ -74,7 +79,7 @@ class PipelineVisualizer {
const h = this.canvas.height;
this.ctx.clearRect(0, 0, w, h);
// 1. Draw Connecting Lines
// 1. Draw Connecting Laser Beams
for (let i = 0; i < this.nodes.length - 1; i++) {
const n1 = this.nodes[i];
const n2 = this.nodes[i + 1];
@@ -85,17 +90,31 @@ class PipelineVisualizer {
const isPassed = this.activeStage && this.getStageIndex(n1.id) <= this.getStageIndex(this.activeStage);
// Base Beam
this.ctx.beginPath();
this.ctx.moveTo(x1, y1);
this.ctx.lineTo(x2, y2);
this.ctx.strokeStyle = isPassed ? 'rgba(0, 240, 255, 0.6)' : 'rgba(51, 65, 85, 0.5)';
this.ctx.lineWidth = isPassed ? 3 : 2;
this.ctx.setLineDash(isPassed ? [] : [5, 5]);
this.ctx.strokeStyle = isPassed ? 'rgba(0, 240, 255, 0.4)' : 'rgba(30, 41, 59, 0.6)';
this.ctx.lineWidth = isPassed ? 4 : 2;
if (!isPassed) this.ctx.setLineDash([6, 6]);
this.ctx.stroke();
this.ctx.setLineDash([]);
// Neon Glow Line
if (isPassed) {
this.ctx.beginPath();
this.ctx.moveTo(x1, y1);
this.ctx.lineTo(x2, y2);
this.ctx.strokeStyle = 'rgba(0, 240, 255, 0.8)';
this.ctx.lineWidth = 2;
this.ctx.shadowColor = '#00F0FF';
this.ctx.shadowBlur = 15;
this.ctx.stroke();
this.ctx.shadowBlur = 0;
}
}
// 2. Draw Moving Data Particles
// 2. Draw Animated Particles on Active Segment
if (this.activeStage) {
this.particles.forEach(p => {
p.progress += p.speed;
@@ -114,40 +133,44 @@ class PipelineVisualizer {
this.ctx.beginPath();
this.ctx.arc(px, py, p.size, 0, Math.PI * 2);
this.ctx.fillStyle = '#00F0FF';
this.ctx.shadowColor = '#00F0FF';
this.ctx.shadowBlur = 10;
this.ctx.fillStyle = '#00FF9D';
this.ctx.shadowColor = '#00FF9D';
this.ctx.shadowBlur = 12;
this.ctx.fill();
this.ctx.shadowBlur = 0;
}
});
}
// 3. Draw Nodes
// 3. Draw Nodes & Cards
this.nodes.forEach(node => {
const nx = node.x * w;
const ny = node.y * h;
// Glow effect for active/completed nodes
// Pulsing outer aura ring for active node
if (node.status === 'running') {
const pulse = 34 + Math.sin(Date.now() * 0.006) * 6;
this.ctx.beginPath();
this.ctx.arc(nx, ny, 32, 0, Math.PI * 2);
this.ctx.arc(nx, ny, pulse, 0, Math.PI * 2);
this.ctx.fillStyle = 'rgba(0, 240, 255, 0.15)';
this.ctx.strokeStyle = 'rgba(0, 240, 255, 0.6)';
this.ctx.lineWidth = 2;
this.ctx.shadowColor = '#00F0FF';
this.ctx.shadowBlur = 20;
this.ctx.shadowBlur = 25;
this.ctx.fill();
this.ctx.stroke();
this.ctx.shadowBlur = 0;
}
// Outer Circle
// Outer Node Circle
this.ctx.beginPath();
this.ctx.arc(nx, ny, 24, 0, Math.PI * 2);
this.ctx.arc(nx, ny, 26, 0, Math.PI * 2);
if (node.status === 'running') {
this.ctx.fillStyle = '#0F172A';
this.ctx.strokeStyle = '#00F0FF';
this.ctx.fillStyle = '#090D16';
this.ctx.strokeStyle = node.color;
this.ctx.lineWidth = 3;
} else if (node.status === 'completed') {
this.ctx.fillStyle = 'rgba(0, 255, 157, 0.2)';
this.ctx.fillStyle = 'rgba(0, 255, 157, 0.15)';
this.ctx.strokeStyle = '#00FF9D';
this.ctx.lineWidth = 2;
} else {
@@ -158,8 +181,8 @@ class PipelineVisualizer {
this.ctx.fill();
this.ctx.stroke();
// Icon
this.ctx.font = '16px sans-serif';
// Node Icon Emoji
this.ctx.font = '18px sans-serif';
this.ctx.textAlign = 'center';
this.ctx.textBaseline = 'middle';
this.ctx.fillText(node.icon, nx, ny);
@@ -167,12 +190,12 @@ class PipelineVisualizer {
// Node Name Label
this.ctx.font = 'bold 11px Outfit, sans-serif';
this.ctx.fillStyle = node.status === 'running' ? '#00F0FF' : node.status === 'completed' ? '#00FF9D' : '#94A3B8';
this.ctx.fillText(node.name, nx, ny + 42);
this.ctx.fillText(node.name, nx, ny + 46);
// Status Badge
// Status Badge Sub-label
this.ctx.font = '9px Fira Code, monospace';
this.ctx.fillStyle = '#64748B';
this.ctx.fillText(node.status.toUpperCase(), nx, ny + 56);
this.ctx.fillStyle = node.status === 'running' ? '#00FF9D' : '#64748B';
this.ctx.fillText(node.status.toUpperCase(), nx, ny + 62);
});
}