Files
nexusAI/packages/chat-client/src/App.jsx
2026-04-26 05:19:31 -07:00

233 lines
6.7 KiB
JavaScript

import React, { useState, useEffect } from 'react';
import ChatWindow from './components/ChatWindow';
import InfoPanel from './components/InfoPanel';
import Sidebar from './components/Sidebar';
import HomeView from './components/HomeView';
import { v4 as uuidv4 } from 'uuid';
import { getModelProps } from './api/orchestration';
/*** View Panels*** */
import AllChatsView from './components/AllChatsView';
import AllProjectsView from './components/AllProjectsView';
import SettingsView from './components/SettingsView';
import ProjectView from './components/ProjectView';
import MemoryView from './components/MemoryView';
import SummaryView from './components/SummaryView';
/**** useHooks **** */
import { useSession } from './hooks/useSession';
import { useChat } from './hooks/useChat';
import { useModels } from './hooks/useModels';
import { useProjects } from './hooks/useProjects';
// Views where back nav makes sense, and where they go back to
const BACK_MAP = {
'chat': 'home',
'all-chats': 'home',
'all-projects': 'home',
'settings': 'home',
'project': 'all-projects',
'memory': 'settings',
'summaries': 'chat',
};
export default function App() {
const [leftOpen, setLeftOpen] = useState(false); // collapsed on home
const [rightOpen, setRightOpen] = useState(false);
const { models, selectedModel, setSelectedModel } = useModels();
const [view, setView] = useState('home');
const [viewHistory, setViewHistory] = useState([]);
const [activeProject, setActiveProject] = useState(null);
const { projects, refreshProjects } = useProjects();
// Lifted model props — available to header + SettingsView
const [modelProps, setModelProps] = useState(null);
useEffect(() => {
getModelProps().then(setModelProps).catch(() => {});
}, []);
const {
sessions,
setSessions,
activeSession,
messages,
loadingHistory,
selectSession,
createSession,
refreshSessions,
appendMessage,
updateLastMessage,
} = useSession();
const {
sendMessage,
cancelStream,
streaming,
lastTokenCount,
lastModel,
useChat,
} = useChat({ activeSession, appendMessage, updateLastMessage, refreshSessions });
function navigate(nextView) {
setViewHistory(prev => [...prev, view]);
setView(nextView);
// Expand sidebar when leaving home
if (view === 'home') setLeftOpen(true);
}
function goBack() {
if (viewHistory.length > 0) {
const prev = viewHistory[viewHistory.length - 1];
setViewHistory(h => h.slice(0, -1));
setView(prev);
if (prev === 'home') setLeftOpen(false);
} else {
// Fallback to BACK_MAP
const dest = BACK_MAP[view] ?? 'home';
setView(dest);
if (dest === 'home') setLeftOpen(false);
}
}
function handleSendMessage(text) {
sendMessage(text, selectedModel, activeSession?.project_id ?? null);
}
function handleSessionsChange(deletedSession) {
if (deletedSession?.external_id === activeSession?.external_id) {
selectSession(null);
}
refreshSessions();
}
// Home: create session, navigate to chat, then send after a tick
function handleHomeSend(text) {
const newSession = createSession(); // ← capture the returned session
setViewHistory(prev => [...prev, 'home']);
setView('chat');
setLeftOpen(true);
sendMessage(text, selectedModel, null, newSession); // ← pass directly, no setTimeout needed
}
function handleNewProjectChat(text) {
const newSession = {
external_id: uuidv4(),
metadata: null,
isNew: true,
project_id: activeProject?.id ?? null,
};
setSessions(prev => [newSession, ...prev]);
selectSession(newSession);
setViewHistory(prev => [...prev, view]);
setView('chat');
setLeftOpen(true);
sendMessage(text, selectedModel, activeProject?.id ?? null, newSession); // ← direct, no timeout
}
const canGoBack = view !== 'home';
return (
<div style={{ display: 'flex', height: '100vh', overflow: 'hidden' }}>
<Sidebar
sessions={sessions}
activeSession={activeSession}
onSelectSession={session => { selectSession(session); navigate('chat'); }}
onNewChat={() => { createSession(); navigate('chat'); }}
onNewProject={() => navigate('all-projects')}
isOpen={leftOpen}
onToggle={() => setLeftOpen(o => !o)}
onSessionsChange={handleSessionsChange}
onNavigate={navigate}
projects={projects}
onProjectsChange={refreshProjects}
onSelectProject={setActiveProject}
/>
{view === 'home' && (
<HomeView
onSendMessage={handleHomeSend}
loadedModel={modelProps?.modelAlias ?? null}
/>
)}
{view === 'chat' && (
<ChatWindow
messages={messages}
loadingHistory={loadingHistory}
streaming={streaming}
activeSession={activeSession}
onSendMessage={handleSendMessage}
onCancel={cancelStream}
onTogglePanel={() => setRightOpen(o => !o)}
onBack={goBack}
canGoBack={canGoBack}
loadedModel={modelProps?.modelAlias ?? null}
summarising={summarising}
/>
)}
{view === 'all-chats' && (
<AllChatsView
onBack={goBack}
onSelectSession={session => { selectSession(session); navigate('chat'); }}
projects={projects}
/>
)}
{view === 'all-projects' && (
<AllProjectsView
onBack={goBack}
onProjectsChange={refreshProjects}
onSelectProject={setActiveProject}
onNavigate={navigate}
/>
)}
{view === 'settings' && (
<SettingsView
onNavigate={navigate}
onBack={goBack}
modelProps={modelProps}
/>
)}
{view === 'project' && activeProject && (
<ProjectView
project={activeProject}
onNavigate={navigate}
onBack={goBack}
onSelectSession={selectSession}
onNewProjectChat={handleNewProjectChat}
onProjectsChange={refreshProjects} // ← add
/>
)}
{view === 'memory' && (
<MemoryView
onNavigate={navigate}
onBack={goBack}
/>
)}
{view === 'summaries' && (
<SummaryView
activeSession={activeSession}
onBack={goBack}
/>
)}
<InfoPanel
isOpen={rightOpen}
onToggle={() => setRightOpen(o => !o)}
activeSession={activeSession}
models={models}
selectedModel={selectedModel}
onModelChange={setSelectedModel}
lastModel={lastModel}
lastTokenCount={lastTokenCount}
summarising={summarising}
onViewSummary={() => navigate('summaries')}
/>
</div>
);
}