feat: Improve UI layout and navigation

- Increase logo size (48x48 desktop, 56x56 mobile) for better visibility
- Add logo as favicon
- Add logo to mobile header
- Move user menu to navigation bars (sidebar on desktop, bottom bar on mobile)
- Fix desktop chat layout - container structure prevents voice controls cutoff
- Fix mobile bottom bar - use icon-only ActionIcons instead of truncated text buttons
- Hide Create Node/New Conversation buttons on mobile to save header space
- Make fixed header and voice controls work properly with containers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-09 14:43:11 +00:00
parent 0b632a31eb
commit f0284ef813
74 changed files with 6996 additions and 629 deletions

View File

@@ -1,23 +1,19 @@
'use client';
import { useChat } from 'ai';
import { useChat } from '@ai-sdk/react';
import { Container, ScrollArea, Paper, Group, TextInput, Button, Stack, Text, Box } from '@mantine/core';
import { useEffect, useRef } from 'react';
import { useEffect, useRef, useState } from 'react';
import { MicrophoneRecorder } from './MicrophoneRecorder';
export function ChatInterface() {
const viewport = useRef<HTMLDivElement>(null);
const [input, setInput] = useState('');
const {
messages,
input,
handleInputChange,
handleSubmit,
setInput,
isLoading,
} = useChat({
api: '/api/chat',
});
sendMessage,
status,
} = useChat();
// Auto-scroll to bottom when new messages arrive
useEffect(() => {
@@ -57,7 +53,12 @@ export function ChatInterface() {
radius="md"
bg={message.role === 'user' ? 'dark.6' : 'dark.7'}
>
<Text size="sm">{message.content}</Text>
<Text size="sm">
{/* Extract text from parts */}
{('parts' in message && Array.isArray((message as any).parts))
? (message as any).parts.find((p: any) => p.type === 'text')?.text || ''
: (message as any).content || ''}
</Text>
</Paper>
</Box>
))}
@@ -65,16 +66,21 @@ export function ChatInterface() {
</ScrollArea>
{/* Input area */}
<form onSubmit={handleSubmit}>
<form onSubmit={(e) => {
e.preventDefault();
if (!input.trim() || status === 'submitted' || status === 'streaming') return;
sendMessage({ text: input });
setInput('');
}}>
<Paper withBorder p="sm" radius="xl">
<Group gap="xs">
<TextInput
value={input}
onChange={handleInputChange}
onChange={(e) => setInput(e.currentTarget.value)}
placeholder="Speak or type your thoughts..."
style={{ flex: 1 }}
variant="unstyled"
disabled={isLoading}
disabled={status === 'submitted' || status === 'streaming'}
/>
{/* Microphone Recorder */}
@@ -96,7 +102,7 @@ export function ChatInterface() {
}}
/>
<Button type="submit" radius="xl" loading={isLoading}>
<Button type="submit" radius="xl" loading={status === 'submitted' || status === 'streaming'}>
Send
</Button>
</Group>