Combines homeai-character (service status, character profiles, editor) and homeai-desktop (chat with voice I/O) into homeai-dashboard on port 5173. - 4-page sidebar layout: Dashboard, Chat, Characters, Editor - Merged Vite middleware: health checks, service restart, bridge proxy - Bridge upgraded to ThreadingHTTPServer (fixes LAN request queuing) - TTS strips emojis before synthesis - Updated start.sh with new launchd service names - Added preload-models to startup sequence Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
124 lines
6.3 KiB
JavaScript
124 lines
6.3 KiB
JavaScript
import { BrowserRouter, Routes, Route, NavLink } from 'react-router-dom';
|
|
import Dashboard from './pages/Dashboard';
|
|
import Chat from './pages/Chat';
|
|
import Characters from './pages/Characters';
|
|
import Editor from './pages/Editor';
|
|
|
|
function NavItem({ to, children, icon }) {
|
|
return (
|
|
<NavLink
|
|
to={to}
|
|
className={({ isActive }) =>
|
|
`flex items-center gap-3 px-4 py-2.5 rounded-lg text-sm font-medium transition-colors ${
|
|
isActive
|
|
? 'bg-gray-800 text-white'
|
|
: 'text-gray-400 hover:text-gray-200 hover:bg-gray-800/50'
|
|
}`
|
|
}
|
|
>
|
|
{icon}
|
|
<span>{children}</span>
|
|
</NavLink>
|
|
);
|
|
}
|
|
|
|
function Layout({ children }) {
|
|
return (
|
|
<div className="h-screen bg-gray-950 flex overflow-hidden">
|
|
{/* Sidebar */}
|
|
<aside className="w-64 bg-gray-900 border-r border-gray-800 flex flex-col shrink-0">
|
|
{/* Logo */}
|
|
<div className="px-6 py-5 border-b border-gray-800">
|
|
<div className="flex items-center gap-3">
|
|
<div className="w-9 h-9 rounded-lg bg-gradient-to-br from-indigo-500 to-purple-600 flex items-center justify-center">
|
|
<svg className="w-5 h-5 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
<path strokeLinecap="round" strokeLinejoin="round" d="M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" />
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<h1 className="text-lg font-bold text-white tracking-tight">HomeAI</h1>
|
|
<p className="text-xs text-gray-500">LINDBLUM</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Nav */}
|
|
<nav className="flex-1 px-3 py-4 space-y-1">
|
|
<NavItem
|
|
to="/"
|
|
icon={
|
|
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
|
|
<path strokeLinecap="round" strokeLinejoin="round" d="M3.75 6A2.25 2.25 0 016 3.75h2.25A2.25 2.25 0 0110.5 6v2.25a2.25 2.25 0 01-2.25 2.25H6a2.25 2.25 0 01-2.25-2.25V6zM3.75 15.75A2.25 2.25 0 016 13.5h2.25a2.25 2.25 0 012.25 2.25V18a2.25 2.25 0 01-2.25 2.25H6A2.25 2.25 0 013.75 18v-2.25zM13.5 6a2.25 2.25 0 012.25-2.25H18A2.25 2.25 0 0120.25 6v2.25A2.25 2.25 0 0118 10.5h-2.25a2.25 2.25 0 01-2.25-2.25V6zM13.5 15.75a2.25 2.25 0 012.25-2.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-2.25A2.25 2.25 0 0113.5 18v-2.25z" />
|
|
</svg>
|
|
}
|
|
>
|
|
Dashboard
|
|
</NavItem>
|
|
|
|
<NavItem
|
|
to="/chat"
|
|
icon={
|
|
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
|
|
<path strokeLinecap="round" strokeLinejoin="round" d="M8.625 12a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0H8.25m4.125 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0H12m4.125 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0h-.375M21 12c0 4.556-4.03 8.25-9 8.25a9.764 9.764 0 01-2.555-.337A5.972 5.972 0 015.41 20.97a5.969 5.969 0 01-.474-.065 4.48 4.48 0 00.978-2.025c.09-.457-.133-.901-.467-1.226C3.93 16.178 3 14.189 3 12c0-4.556 4.03-8.25 9-8.25s9 3.694 9 8.25z" />
|
|
</svg>
|
|
}
|
|
>
|
|
Chat
|
|
</NavItem>
|
|
|
|
<NavItem
|
|
to="/characters"
|
|
icon={
|
|
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
|
|
<path strokeLinecap="round" strokeLinejoin="round" d="M15.75 6a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0zM4.501 20.118a7.5 7.5 0 0114.998 0A17.933 17.933 0 0112 21.75c-2.676 0-5.216-.584-7.499-1.632z" />
|
|
</svg>
|
|
}
|
|
>
|
|
Characters
|
|
</NavItem>
|
|
|
|
<NavItem
|
|
to="/editor"
|
|
icon={
|
|
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
|
|
<path strokeLinecap="round" strokeLinejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z" />
|
|
<path strokeLinecap="round" strokeLinejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
|
</svg>
|
|
}
|
|
>
|
|
Editor
|
|
</NavItem>
|
|
</nav>
|
|
|
|
{/* Footer */}
|
|
<div className="px-6 py-4 border-t border-gray-800">
|
|
<p className="text-xs text-gray-600">HomeAI v0.1.0</p>
|
|
<p className="text-xs text-gray-700">Mac Mini M4 Pro</p>
|
|
</div>
|
|
</aside>
|
|
|
|
{/* Main content */}
|
|
<main className="flex-1 overflow-hidden flex flex-col">
|
|
{children}
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function App() {
|
|
return (
|
|
<BrowserRouter>
|
|
<Layout>
|
|
<Routes>
|
|
<Route path="/" element={<div className="flex-1 overflow-y-auto p-8"><div className="max-w-6xl mx-auto"><Dashboard /></div></div>} />
|
|
<Route path="/chat" element={<Chat />} />
|
|
<Route path="/characters" element={<div className="flex-1 overflow-y-auto p-8"><div className="max-w-6xl mx-auto"><Characters /></div></div>} />
|
|
<Route path="/editor" element={<div className="flex-1 overflow-y-auto p-8"><div className="max-w-6xl mx-auto"><Editor /></div></div>} />
|
|
</Routes>
|
|
</Layout>
|
|
</BrowserRouter>
|
|
);
|
|
}
|
|
|
|
export default App;
|