File size: 5,189 Bytes
42da440 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | import React, { useEffect, useState } from 'react';
import { NavLink, Outlet, useLocation } from 'react-router-dom';
import { useNavigation } from '../context/NavigationContext';
import atkLogo from '../assets/atk-logo-notscuffed.png';
const animationStyles = `
@keyframes pulse-medium-white {
0%, 100% { background-color: rgba(255,255,255,0.1); }
50% { background-color: rgba(255,255,255,0.3); }
}
.animate-pulse-medium-white {
animation: pulse-medium-white 1.8s infinite;
}
`;
function Layout() {
const { highlightedNavLink, setHighlightedNavLink } = useNavigation();
const location = useLocation();
const [serverOnline, setServerOnline] = useState(false);
const [datagenServerOnline, setDatagenServerOnline] = useState(false);
// Check for server on port 8003
useEffect(() => {
const checkServerStatus = async () => {
try {
const baseUrl = window.location.hostname === '127.0.0.1' || window.location.hostname === 'localhost'
? 'http://127.0.0.1:8003'
: `${window.location.origin}/llm`;
const response = await fetch(`${baseUrl}/health`, {
method: 'GET',
headers: { 'Content-Type': 'application/json' }
});
const isOnline = response.ok;
setServerOnline(isOnline);
// Auto-highlight chat button when server comes online (but not if user is already on chat page)
if (isOnline && location.pathname !== '/chat') {
setHighlightedNavLink('/chat');
} else if (!isOnline && highlightedNavLink === '/chat') {
// Remove highlight when server goes offline
setHighlightedNavLink(null);
}
} catch (error) {
setServerOnline(false);
// Remove highlight when server is unreachable
if (highlightedNavLink === '/chat') {
setHighlightedNavLink(null);
}
}
};
// Initial check
checkServerStatus();
// Poll every 5 seconds
const interval = setInterval(checkServerStatus, 5000);
return () => clearInterval(interval);
}, [location.pathname, highlightedNavLink, setHighlightedNavLink]);
// Check for datagen inference server on port 8082
useEffect(() => {
const checkDatagenServerStatus = async () => {
try {
// Try OpenAI-compatible endpoint
const response = await fetch('http://127.0.0.1:8082/v1/models', {
method: 'GET',
headers: { 'Content-Type': 'application/json' }
});
setDatagenServerOnline(response.ok);
} catch (error) {
setDatagenServerOnline(false);
}
};
// Initial check
checkDatagenServerStatus();
// Poll every 5 seconds
const interval = setInterval(checkDatagenServerStatus, 5000);
return () => clearInterval(interval);
}, []);
const navLinkClass = (path) => ({ isActive }) => {
const base = 'px-4 py-2 rounded hover:bg-gray-700 transition-colors';
const active = isActive ? 'bg-blue-600 text-white font-bold' : 'text-gray-300';
const pulse = highlightedNavLink === path && !isActive ? 'animate-pulse-medium-white' : '';
return `${base} ${active} ${pulse}`;
};
return (
<div className="flex flex-col h-screen bg-gray-900 text-white">
<nav className="bg-gray-800 shadow-md p-4 flex items-center gap-4">
<NavLink to="/" className="mr-4 text-xl font-bold text-white hover:text-gray-300 flex items-center">
<img src={atkLogo} alt="ATK Logo" className="h-10 mr-2 bg-gray-100 p-1 rounded-md" />
</NavLink>
<NavLink to="/configs" className={navLinkClass('/configs')}>
Configs
</NavLink>
<NavLink to="/inputs" className={navLinkClass('/inputs')}>
Inputs
</NavLink>
<NavLink to="/outputs" className={navLinkClass('/outputs')}>
Outputs
</NavLink>
<NavLink to="/logs" className={navLinkClass('/logs')}>
Logs
</NavLink>
<NavLink to="/monitor" className={navLinkClass('/monitor')}>
Monitor
</NavLink>
<NavLink to="/chat" className={navLinkClass('/chat')}>
Chat
</NavLink>
{/* Datagen Inference Server Status */}
<div className="ml-auto flex flex-col items-end gap-1">
<div className="flex items-center gap-2">
<div className={`w-2 h-2 rounded-full ${
datagenServerOnline ? 'bg-green-500' : 'bg-red-500'
}`} />
<span className="text-xs text-gray-400">
Datagen Server
</span>
</div>
<span className="text-xs text-gray-500 text-right max-w-48">
{datagenServerOnline
? 'Online (Port 8082)'
: 'Offline - Start with local_* scripts (takes 1-2 minutes after project startup)'
}
</span>
</div>
{/* TODO: Add Settings/Boring Mode Toggle */}
</nav>
<main className="flex-grow overflow-auto">
<Outlet /> {/* Child routes will render here */}
</main>
<style>{animationStyles}</style>
</div>
);
}
export default Layout; |