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;