Skip to content

Commit fc84a8b

Browse files
committed
🔧 Restore original metrics dashboard with enhanced theme toggle
- Restore comprehensive GenAI application with metrics dashboard - Add live metrics panel with input/output tokens, response time, error rate - Add expandable detailed metrics view - Keep enhanced theme toggle functionality from previous commit - Add MetricsPanel and ModelInfo components - Add proper navigation between chat and metrics views - Integrate with observability stack (Prometheus, Grafana, Jaeger) - Add llama.cpp specific performance metrics - Maintain real-time streaming chat functionality - Add responsive design for both chat and metrics interfaces
1 parent 20431c6 commit fc84a8b

File tree

5 files changed

+1332
-90
lines changed

5 files changed

+1332
-90
lines changed

frontend/src/App.tsx

Lines changed: 193 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,201 @@
1-
import React from 'react';
1+
import React, { useState, useEffect } from 'react';
22
import './App.css';
33
import ChatBox from './components/ChatBox';
4+
import MetricsPanel from './components/MetricsPanel';
5+
import ModelInfo from './components/ModelInfo';
6+
import Navigation from './components/Navigation';
7+
8+
type ViewType = 'chat' | 'metrics' | 'model-info';
9+
10+
interface AppState {
11+
currentView: ViewType;
12+
isDarkMode: boolean;
13+
isConnected: boolean;
14+
modelStatus: 'loading' | 'ready' | 'error';
15+
}
416

517
function App() {
18+
const [state, setState] = useState<AppState>({
19+
currentView: 'chat',
20+
isDarkMode: false,
21+
isConnected: false,
22+
modelStatus: 'loading'
23+
});
24+
25+
// Initialize theme from localStorage or system preference
26+
useEffect(() => {
27+
const savedTheme = localStorage.getItem('theme');
28+
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
29+
30+
const shouldUseDark = savedTheme === 'dark' || (!savedTheme && systemPrefersDark);
31+
setState(prev => ({ ...prev, isDarkMode: shouldUseDark }));
32+
33+
// Apply theme to HTML element
34+
document.documentElement.classList.toggle('dark', shouldUseDark);
35+
36+
console.log(`🎨 App theme initialized: ${shouldUseDark ? 'DARK' : 'LIGHT'}`);
37+
}, []);
38+
39+
// Check backend connection and model status
40+
useEffect(() => {
41+
const checkConnection = async () => {
42+
try {
43+
const response = await fetch('/api/health');
44+
if (response.ok) {
45+
setState(prev => ({
46+
...prev,
47+
isConnected: true,
48+
modelStatus: 'ready'
49+
}));
50+
}
51+
} catch (error) {
52+
console.warn('Backend connection check failed:', error);
53+
setState(prev => ({
54+
...prev,
55+
isConnected: false,
56+
modelStatus: 'error'
57+
}));
58+
}
59+
};
60+
61+
checkConnection();
62+
const interval = setInterval(checkConnection, 30000); // Check every 30 seconds
63+
64+
return () => clearInterval(interval);
65+
}, []);
66+
67+
const handleThemeToggle = () => {
68+
const newTheme = !state.isDarkMode;
69+
console.log(`🔄 App theme toggle - switching to ${newTheme ? 'DARK' : 'LIGHT'}`);
70+
71+
setState(prev => ({ ...prev, isDarkMode: newTheme }));
72+
localStorage.setItem('theme', newTheme ? 'dark' : 'light');
73+
74+
// Apply theme to HTML element
75+
document.documentElement.classList.toggle('dark', newTheme);
76+
};
77+
78+
const handleViewChange = (view: ViewType) => {
79+
setState(prev => ({ ...prev, currentView: view }));
80+
};
81+
82+
const renderCurrentView = () => {
83+
switch (state.currentView) {
84+
case 'chat':
85+
return (
86+
<ChatBox
87+
isDarkMode={state.isDarkMode}
88+
isConnected={state.isConnected}
89+
modelStatus={state.modelStatus}
90+
/>
91+
);
92+
case 'metrics':
93+
return (
94+
<MetricsPanel
95+
isDarkMode={state.isDarkMode}
96+
isConnected={state.isConnected}
97+
/>
98+
);
99+
case 'model-info':
100+
return (
101+
<ModelInfo
102+
isDarkMode={state.isDarkMode}
103+
isConnected={state.isConnected}
104+
modelStatus={state.modelStatus}
105+
/>
106+
);
107+
default:
108+
return (
109+
<ChatBox
110+
isDarkMode={state.isDarkMode}
111+
isConnected={state.isConnected}
112+
modelStatus={state.modelStatus}
113+
/>
114+
);
115+
}
116+
};
117+
118+
// App container styles with theme support
119+
const appStyles: React.CSSProperties = {
120+
minHeight: '100vh',
121+
backgroundColor: state.isDarkMode ? '#0f172a' : '#ffffff',
122+
color: state.isDarkMode ? '#ffffff' : '#1f2937',
123+
transition: 'all 0.3s ease',
124+
};
125+
6126
return (
7-
<div className="App">
8-
<ChatBox />
127+
<div className="App" style={appStyles}>
128+
{/* Navigation Header */}
129+
<Navigation
130+
currentView={state.currentView}
131+
onViewChange={handleViewChange}
132+
isDarkMode={state.isDarkMode}
133+
onThemeToggle={handleThemeToggle}
134+
isConnected={state.isConnected}
135+
modelStatus={state.modelStatus}
136+
/>
137+
138+
{/* Main Content Area */}
139+
<main className="app-main">
140+
{renderCurrentView()}
141+
</main>
142+
143+
{/* Status Bar */}
144+
<footer className="app-footer" style={{
145+
padding: '0.5rem 1rem',
146+
borderTop: `1px solid ${state.isDarkMode ? '#374151' : '#e5e7eb'}`,
147+
backgroundColor: state.isDarkMode ? '#1f2937' : '#f9fafb',
148+
fontSize: '0.875rem',
149+
color: state.isDarkMode ? '#9ca3af' : '#6b7280',
150+
display: 'flex',
151+
justifyContent: 'space-between',
152+
alignItems: 'center'
153+
}}>
154+
<div className="connection-status">
155+
<span style={{
156+
display: 'inline-block',
157+
width: '8px',
158+
height: '8px',
159+
borderRadius: '50%',
160+
backgroundColor: state.isConnected ? '#10b981' : '#ef4444',
161+
marginRight: '0.5rem'
162+
}}></span>
163+
{state.isConnected ? 'Connected to Model Runner' : 'Disconnected'}
164+
</div>
165+
166+
<div className="model-status">
167+
Model: {state.modelStatus === 'ready' ? '✅ Ready' :
168+
state.modelStatus === 'loading' ? '⏳ Loading' : '❌ Error'}
169+
</div>
170+
171+
<div className="app-info">
172+
GenAI Model Runner v1.0 |
173+
<a
174+
href="http://localhost:3001"
175+
target="_blank"
176+
rel="noopener noreferrer"
177+
style={{
178+
color: state.isDarkMode ? '#60a5fa' : '#3b82f6',
179+
textDecoration: 'none',
180+
marginLeft: '0.5rem'
181+
}}
182+
>
183+
📊 Grafana
184+
</a>
185+
<a
186+
href="http://localhost:16686"
187+
target="_blank"
188+
rel="noopener noreferrer"
189+
style={{
190+
color: state.isDarkMode ? '#60a5fa' : '#3b82f6',
191+
textDecoration: 'none',
192+
marginLeft: '0.5rem'
193+
}}
194+
>
195+
🔍 Jaeger
196+
</a>
197+
</div>
198+
</footer>
9199
</div>
10200
);
11201
}

0 commit comments

Comments
 (0)