From ad5ecb5ff31c819c97919e94de5c7603b2f5c538 Mon Sep 17 00:00:00 2001 From: Storme-bit Date: Sat, 18 Apr 2026 21:21:05 -0700 Subject: [PATCH] chat client fixes --- .vscode/settings.json | 2 + packages/chat-client/src/App.jsx | 142 +++++++++--- .../src/components/AllChatsView.jsx | 11 +- .../src/components/AllProjectsView.jsx | 11 +- .../chat-client/src/components/ChatWindow.jsx | 74 +++++- .../chat-client/src/components/HomeView.jsx | 149 ++++++++++++ .../chat-client/src/components/MemoryView.jsx | 2 +- .../src/components/ProjectView.jsx | 2 +- .../src/components/SessionList.jsx | 215 ------------------ .../src/components/SettingsView.jsx | 60 +++-- 10 files changed, 378 insertions(+), 290 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 packages/chat-client/src/components/HomeView.jsx delete mode 100644 packages/chat-client/src/components/SessionList.jsx diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..7a73a41 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/packages/chat-client/src/App.jsx b/packages/chat-client/src/App.jsx index 2b11bea..f6fb1d6 100644 --- a/packages/chat-client/src/App.jsx +++ b/packages/chat-client/src/App.jsx @@ -1,8 +1,10 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import ChatWindow from './components/ChatWindow'; import InfoPanel from './components/InfoPanel'; import Sidebar from './components/Sidebar'; -import {v4 as uuidv4} from 'uuid'; +import HomeView from './components/HomeView'; +import { v4 as uuidv4 } from 'uuid'; +import { getModelProps } from './api/orchestration'; /*** View Panels*** */ import AllChatsView from './components/AllChatsView'; @@ -17,13 +19,30 @@ 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', +}; + export default function App() { - const [leftOpen, setLeftOpen] = useState(true); + const [leftOpen, setLeftOpen] = useState(false); // collapsed on home const [rightOpen, setRightOpen] = useState(false); const { models, selectedModel, setSelectedModel } = useModels(); - const [view, setView] = useState('chat') + const [view, setView] = useState('home'); + const [viewHistory, setViewHistory] = useState([]); const [activeProject, setActiveProject] = useState(null); - const {projects, refreshProjects} = useProjects(); + const { projects, refreshProjects } = useProjects(); + + // Lifted model props — available to header + SettingsView + const [modelProps, setModelProps] = useState(null); + useEffect(() => { + getModelProps().then(setModelProps).catch(() => {}); + }, []); const { sessions, @@ -46,17 +65,49 @@ export default function App() { lastModel, } = 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){ + 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) { + createSession(); + setViewHistory(prev => [...prev, 'home']); + setView('chat'); + setLeftOpen(true); + setTimeout(() => { + sendMessage(text, selectedModel, null); + }, 50); + } + async function handleNewProjectChat() { const newSession = { external_id: uuidv4(), @@ -64,70 +115,90 @@ export default function App() { isNew: true, project_id: activeProject?.id ?? null, }; - // Optimistically set active session then navigate setSessions(prev => [newSession, ...prev]); selectSession(newSession); - setView('chat'); - // After first message saves, project assignment will be written via updateSession + navigate('chat'); } + const canGoBack = view !== 'home'; + return ( -
+
setView('all-projects')} + onSelectSession={session => { selectSession(session); navigate('chat'); }} + onNewChat={() => { createSession(); navigate('chat'); }} + onNewProject={() => navigate('all-projects')} isOpen={leftOpen} onToggle={() => setLeftOpen(o => !o)} onSessionsChange={handleSessionsChange} - onNavigate={setView} + onNavigate={navigate} projects={projects} onProjectsChange={refreshProjects} onSelectProject={setActiveProject} /> + {view === 'home' && ( + + )} {view === 'chat' && ( - setRightOpen(o => !o)} - /> + setRightOpen(o => !o)} + onBack={goBack} + canGoBack={canGoBack} + loadedModel={modelProps?.modelAlias ?? null} + /> )} {view === 'all-chats' && ( {selectSession(session); setView('chat');}} - /> + onBack={goBack} + onSelectSession={session => { selectSession(session); navigate('chat'); }} + /> )} {view === 'all-projects' && ( - + )} - {view === 'settings' && } + {view === 'settings' && ( + + )} {view === 'project' && activeProject && ( )} - {view === 'memory' && } - + {view === 'memory' && ( + + )}
); diff --git a/packages/chat-client/src/components/AllChatsView.jsx b/packages/chat-client/src/components/AllChatsView.jsx index 845a15e..93f4e8d 100644 --- a/packages/chat-client/src/components/AllChatsView.jsx +++ b/packages/chat-client/src/components/AllChatsView.jsx @@ -4,7 +4,7 @@ import { API_DEFAULTS } from '../config/constants'; const PAGE_SIZE = API_DEFAULTS.PAGE_SIZE; -export default function AllChatsView({ onSelectSession }) { +export default function AllChatsView({ onSelectSession, onBack }) { const [sessions, setSessions] = useState([]); const [loading, setLoading] = useState(true); const [page, setPage] = useState(0); @@ -86,10 +86,11 @@ export default function AllChatsView({ onSelectSession }) {
{/* Header */} -
- - All Chats - +
+
+ + All Chats +
{selected.size > 0 && ( + All Projects +
+
+
+ {/* Back button */} + {canGoBack && ( + + )} + {/* Session name */} + + {activeSession ? (activeSession.name || activeSession.external_id) : 'New chat'} + +
+ +
+ {/* Loaded model pill */} + {modelLabel && ( + + {modelLabel} + + )} + {!modelLabel && ( + + No model loaded + + )} + +
{/* Message thread */} @@ -44,7 +99,7 @@ export default function ChatWindow({ messages, loadingHistory, streaming, onSend gap: '12px', }}>
-

Select a session or start a new chat

+

Start typing to begin

)} @@ -80,8 +135,7 @@ export default function ChatWindow({ messages, loadingHistory, streaming, onSend value={input} onChange={e => setInput(e.target.value)} onKeyDown={handleKeyDown} - disabled={!activeSession} - placeholder={activeSession ? 'Message NexusAI...' : 'Select a session to start chatting'} + placeholder="Message NexusAI..." rows={1} style={{ flex: 1, @@ -115,7 +169,7 @@ export default function ChatWindow({ messages, loadingHistory, streaming, onSend ) : (