diff --git a/frontend/public/emily-avatar-200.png b/frontend/public/emily-avatar-200.png new file mode 100644 index 0000000..3a2f811 Binary files /dev/null and b/frontend/public/emily-avatar-200.png differ diff --git a/frontend/public/emily-avatar-48.png b/frontend/public/emily-avatar-48.png new file mode 100644 index 0000000..090c963 Binary files /dev/null and b/frontend/public/emily-avatar-48.png differ diff --git a/frontend/public/emily-avatar-96.png b/frontend/public/emily-avatar-96.png new file mode 100644 index 0000000..3d5fd0b Binary files /dev/null and b/frontend/public/emily-avatar-96.png differ diff --git a/frontend/public/emily-avatar.png b/frontend/public/emily-avatar.png new file mode 100644 index 0000000..5be5c38 Binary files /dev/null and b/frontend/public/emily-avatar.png differ diff --git a/frontend/src/App.css b/frontend/src/App.css index 1bbf268..b587414 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -497,11 +497,21 @@ body { .message-header { display: flex; - flex-direction: column; + flex-direction: row; + align-items: center; + flex-wrap: wrap; gap: 0.5rem; margin-bottom: 0.5rem; } +.emily-avatar { + width: 28px; + height: 28px; + border-radius: 50%; + object-fit: cover; + flex-shrink: 0; +} + .agent-name { font-size: 0.75rem; color: var(--muted-foreground); @@ -534,7 +544,10 @@ body { } .loading-animation { - text-align: center; + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; color: var(--muted-foreground); padding: 0.75rem; font-size: 0.875rem; @@ -904,6 +917,14 @@ body { margin-bottom: 1.5rem; } +.login-emily-avatar { + width: 96px; + height: 96px; + border-radius: 50%; + object-fit: cover; + margin-bottom: 0.5rem; +} + .login-card h1 { font-size: 2rem; color: #f8fafc; diff --git a/frontend/src/App.js b/frontend/src/App.js index 15b8a6e..e76489c 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -4,8 +4,9 @@ import "./App.css"; import { ThemeToggle } from "./components/ThemeToggle"; import { ResizableLayout } from "./components/ResizableLayout"; import { ReactComponent as EcoSeekLogo } from "./ecoseek-logo.svg"; +import emilyAvatar from "./emily-avatar.png"; import { useAuth } from "./contexts/AuthContext"; -import { chatCompletion, checkHealth, BROKER_URL, CHAT_URL, IS_LOCAL_EMILY } from "./api/broker"; +import { chatCompletion, checkHealth, checkRemoteHealth, BROKER_URL, CHAT_URL, IS_LOCAL_EMILY, HERMES_REMOTE_URL } from "./api/broker"; function LoginScreen({ onLogin }) { return ( @@ -13,6 +14,7 @@ function LoginScreen({ onLogin }) {
Meet Emily — your AI ecological research assistant
@@ -39,6 +41,7 @@ function App() { const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const [isOnline, setIsOnline] = useState(false); + const [remoteStatus, setRemoteStatus] = useState(null); const [expandedReasoning, setExpandedReasoning] = useState(new Set()); const messagesEndRef = useRef(null); @@ -49,14 +52,16 @@ function App() { } }, [handleCallback]); - // Health polling + // Health polling (local + remote) useEffect(() => { const poll = async () => { const ok = await checkHealth(); setIsOnline(ok); + const remote = await checkRemoteHealth(); + setRemoteStatus(remote); }; poll(); - const id = setInterval(poll, 10000); + const id = setInterval(poll, 15000); return () => clearInterval(id); }, []); @@ -237,7 +242,10 @@ function App() { >