'use client';
import { useState, useEffect, useCallback } from 'react';
import { usePathname, useRouter } from 'next/navigation';
import { motion, AnimatePresence } from 'framer-motion';
import { getApiBaseUrl } from '@/lib/api';
import {
MessageSquare, Globe, BarChart3, Layers, Activity,
Zap, Shield, Terminal, Settings, Plus,
ChevronLeft, ChevronRight, Menu, X, Brain, Mic
} from 'lucide-react';
const navSections = [
{
label: 'Main',
items: [
{ path: '/', label: 'Chat', icon: MessageSquare },
{ path: '/intel', label: 'Intel', icon: Globe },
{ path: '/markets', label: 'Markets', icon: BarChart3 },
{ path: '/workspace', label: 'Workspace', icon: Brain },
{ path: '/pulse', label: 'Pulse', icon: Activity },
],
},
{
label: 'Guardian',
items: [
{ path: '/guardian/intake', label: 'Forensic Intake', icon: Plus },
{ path: '/guardian/journey', label: 'Journey Graph', icon: Activity },
{ path: '/guardian/dissonance', label: 'Deception Radar', icon: Mic },
],
},
{
label: 'System',
items: [
{ path: '/cases', label: 'Cases', icon: Layers },
{ path: '/simulation', label: 'Simulation', icon: Zap },
{ path: '/sentinel', label: 'Sentinel', icon: Shield },
{ path: '/safety', label: 'Safety Gateway', icon: Zap },
{ path: '/prompts', label: 'Prompts', icon: Terminal },
{ path: '/config', label: 'Config', icon: Settings },
],
},
];
function JanusOrbSmall({ pulse = false }: { pulse?: boolean }) {
return (
);
}
export default function AppShell({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
const router = useRouter();
const [collapsed, setCollapsed] = useState(false);
const [mobileOpen, setMobileOpen] = useState(false);
const [daemonStatus, setDaemonStatus] = useState(null);
const fetchStatus = useCallback(async () => {
const baseUrl = getApiBaseUrl();
try {
const r = await fetch(`${baseUrl}/daemon/status`);
if (r.ok) setDaemonStatus(await r.json());
} catch { /* silent */ }
}, []);
useEffect(() => {
fetchStatus();
const iv = setInterval(fetchStatus, 60000);
return () => clearInterval(iv);
}, [fetchStatus]);
useEffect(() => {
setMobileOpen(false);
}, [pathname]);
const isActive = (path: string) => {
if (path === '/') return pathname === '/';
return pathname?.startsWith(path);
};
const NavContent = ({ isMobile = false }: { isMobile?: boolean }) => (
<>
{/* Logo */}
{(!collapsed || isMobile) && (
JANUS
)}
{/* New Chat button */}
{/* Nav sections */}
{/* Status footer */}
{(!collapsed || isMobile) && (
{daemonStatus?.running ? 'System active' : 'Offline'}
{daemonStatus?.circadian && (
{daemonStatus.circadian.current_phase} phase
)}
)}
>
);
return (
{/* Desktop Sidebar */}
{/* Collapse toggle */}
{/* Mobile Header */}
JANUS
{/* Mobile Overlay */}
{mobileOpen && (
setMobileOpen(false)}
/>
)}
{/* Mobile Drawer */}
{mobileOpen && (
)}
{/* Main Content Area */}
{children}
);
}