) : (
session.messages.map((msg, index) => (
@@ -164,44 +264,135 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
key={index}
style={{
display: 'flex',
- flexDirection: msg.role === 'user' ? 'row' : 'row',
- justifyContent: msg.role === 'user' ? 'flex-end' : 'flex-start'
+ justifyContent: msg.role === 'user' ? 'flex-end' : 'flex-start',
+ animation: 'fadeIn 0.3s ease-out'
}}
>
+
+ {/* Streaming message */}
+ {streamingContent && (
+
+
+
+ đ¤ Assistant
+
+
+
+ {streamingContent}
+
+
+
+ )}
+
+ {/* Loading indicator (non-streaming) */}
+ {isLoading && !streamingContent && (
+
+
Thinking...
)}
+
+ {/* Error message */}
{error && (
-
- Error: {error}
+
+ â ī¸ {error}
)}
@@ -211,24 +402,45 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
)
diff --git a/src/components/SessionDrawer.test.tsx b/src/components/SessionDrawer.test.tsx
index 7af5b4f..1014c51 100644
--- a/src/components/SessionDrawer.test.tsx
+++ b/src/components/SessionDrawer.test.tsx
@@ -12,7 +12,7 @@ describe('SessionDrawer', () => {
onSelectSession: vi.fn(),
onCreateSession: vi.fn(),
onDeleteSession: vi.fn(),
- config: { endpoint: '', systemPrompt: '', model: 'local-swarm' } as Config,
+ config: { endpoint: '', systemPrompt: '', model: 'local-swarm', streaming: false } as Config,
onUpdateConfig: vi.fn()
}
@@ -22,7 +22,7 @@ describe('SessionDrawer', () => {
it('should render when open', () => {
render(
)
- expect(screen.getByText(/Sessions/)).toBeInTheDocument()
+ expect(screen.getByText(/Your Sessions/)).toBeInTheDocument()
expect(screen.getByRole('button', { name: /New Session/i })).toBeInTheDocument()
})
@@ -71,11 +71,11 @@ describe('SessionDrawer', () => {
it('should call onUpdateConfig when save config clicked', () => {
render(
)
- const endpointInput = screen.getByPlaceholderText(/openai.com/i)
+ const endpointInput = screen.getByPlaceholderText(/192.168/i)
fireEvent.change(endpointInput, { target: { value: 'https://new.com' } })
fireEvent.click(screen.getByRole('button', { name: /Save Config/i }))
- expect(defaultProps.onUpdateConfig).toHaveBeenCalledWith({ endpoint: 'https://new.com', systemPrompt: '', model: 'local-swarm' })
+ expect(defaultProps.onUpdateConfig).toHaveBeenCalledWith({ endpoint: 'https://new.com', systemPrompt: '', model: 'local-swarm', streaming: false })
})
})
diff --git a/src/components/SessionDrawer.tsx b/src/components/SessionDrawer.tsx
index 3c5092f..784ae93 100644
--- a/src/components/SessionDrawer.tsx
+++ b/src/components/SessionDrawer.tsx
@@ -29,7 +29,7 @@ export function SessionDrawer({
}: SessionDrawerProps) {
const [localConfig, setLocalConfig] = useState
(config)
- const handleConfigChange = (field: keyof Config, value: string) => {
+ const handleConfigChange = (field: keyof Config, value: string | boolean) => {
setLocalConfig(prev => ({ ...prev, [field]: value }))
}
@@ -43,6 +43,15 @@ export function SessionDrawer({
}
}
+ const formatDate = (timestamp: number) => {
+ return new Date(timestamp).toLocaleDateString('en-US', {
+ month: 'short',
+ day: 'numeric',
+ hour: '2-digit',
+ minute: '2-digit'
+ })
+ }
+
return (
<>
{/* Overlay */}
@@ -54,7 +63,8 @@ export function SessionDrawer({
left: 0,
right: 0,
bottom: 0,
- backgroundColor: 'rgba(0,0,0,0.5)',
+ backgroundColor: 'rgba(0,0,0,0.6)',
+ backdropFilter: 'blur(4px)',
zIndex: 999
}}
onClick={onClose}
@@ -68,51 +78,90 @@ export function SessionDrawer({
top: 0,
left: 0,
bottom: 0,
- width: '300px',
+ width: '340px',
backgroundColor: 'var(--bg-secondary)',
borderRight: '1px solid var(--border)',
transform: isOpen ? 'translateX(0)' : 'translateX(-100%)',
- transition: 'transform 0.3s ease',
+ transition: 'transform 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
zIndex: 1000,
display: 'flex',
flexDirection: 'column',
- padding: '16px'
+ boxShadow: isOpen ? 'var(--shadow-lg)' : 'none'
}}
onKeyDown={handleKeyDown}
tabIndex={isOpen ? 0 : -1}
>
-
-
Sessions
+ {/* Header */}
+
+
+ đŦ Sessions
+
{/* Config Section */}
-
-
+
+
API Configuration
-
-