This commit is contained in:
Zacharias-Brohn
2026-01-14 22:08:19 +01:00
parent 95d07eb00b
commit 7663a917da
+64 -68
View File
@@ -22,6 +22,7 @@ import {
Container, Container,
Group, Group,
Menu, Menu,
NavLink,
Paper, Paper,
ScrollArea, ScrollArea,
Select, Select,
@@ -31,7 +32,6 @@ import {
TextInputProps, TextInputProps,
Title, Title,
Tooltip, Tooltip,
UnstyledButton,
useMantineTheme, useMantineTheme,
} from '@mantine/core'; } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks'; import { useDisclosure } from '@mantine/hooks';
@@ -619,29 +619,23 @@ export default function ChatLayout() {
</Group> </Group>
<ScrollArea style={{ flex: 1, margin: '0 -10px' }} p="xs"> <ScrollArea style={{ flex: 1, margin: '0 -10px' }} p="xs">
<Stack gap="xs"> <Stack gap={2}>
{chats.length > 0 ? ( {chats.length > 0 ? (
chats.map((chat) => ( chats.map((chat) =>
editingChatId === chat.id ? (
// Inline editing mode
<Group <Group
key={chat.id} key={chat.id}
gap={0}
wrap="nowrap" wrap="nowrap"
style={{ gap="xs"
borderRadius: 'var(--mantine-radius-md)', px="sm"
backgroundColor: py={6}
activeChatId === chat.id style={{ minWidth: 0 }}
? 'var(--mantine-color-default-hover)'
: 'transparent',
transition: 'background-color 0.2s',
}}
> >
{editingChatId === chat.id ? (
// Inline editing mode
<Group wrap="nowrap" gap="xs" p="sm" style={{ flex: 1, minWidth: 0 }}>
{chat.pinned ? ( {chat.pinned ? (
<IconPin size={18} color="gray" style={{ minWidth: 18 }} /> <IconPin size={16} stroke={1.5} style={{ minWidth: 16 }} />
) : ( ) : (
<IconMessage size={18} color="gray" style={{ minWidth: 18 }} /> <IconMessage size={16} stroke={1.5} style={{ minWidth: 16 }} />
)} )}
<TextInput <TextInput
ref={editInputRef} ref={editInputRef}
@@ -650,48 +644,49 @@ export default function ChatLayout() {
onBlur={saveRenamedChat} onBlur={saveRenamedChat}
onKeyDown={handleRenameKeyDown} onKeyDown={handleRenameKeyDown}
size="xs" size="xs"
variant="unstyled"
styles={{
input: {
padding: 0,
height: 'auto',
minHeight: 'unset',
fontSize: 'var(--mantine-font-size-sm)',
},
}}
style={{ flex: 1 }} style={{ flex: 1 }}
/> />
</Group> </Group>
) : ( ) : (
// Normal display mode // Normal display mode with NavLink
<UnstyledButton <Group key={chat.id} gap={0} wrap="nowrap">
onClick={() => handleSelectChat(chat)} <NavLink
p="sm" component="button"
style={{ flex: 1, minWidth: 0 }} active={activeChatId === chat.id}
> color={primaryColor}
<Group wrap="nowrap" gap="xs"> variant="subtle"
{chat.pinned ? ( label={chat.title}
<IconPin size={18} color="gray" style={{ minWidth: 18 }} /> leftSection={
chat.pinned ? (
<IconPin size={16} stroke={1.5} />
) : ( ) : (
<IconMessage size={18} color="gray" style={{ minWidth: 18 }} /> <IconMessage size={16} stroke={1.5} />
)} )
<Text size="sm" truncate style={{ flex: 1 }}> }
{chat.title} onClick={() => handleSelectChat(chat)}
</Text> noWrap
</Group> styles={{
</UnstyledButton> root: {
)} flex: 1,
minWidth: 0,
borderRadius: 'var(--mantine-radius-sm)',
padding: '6px 10px',
},
label: {
overflow: 'hidden',
textOverflow: 'ellipsis',
},
}}
/>
<Menu position="bottom-end" withArrow> <Menu position="bottom-end" withArrow>
<Menu.Target> <Menu.Target>
<ActionIcon <ActionIcon
variant="subtle" variant="subtle"
color="gray" color="gray"
size="sm" size="sm"
mr="xs"
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
> >
<IconDotsVertical size={16} /> <IconDotsVertical size={14} />
</ActionIcon> </ActionIcon>
</Menu.Target> </Menu.Target>
<Menu.Dropdown> <Menu.Dropdown>
@@ -718,7 +713,8 @@ export default function ChatLayout() {
</Menu.Dropdown> </Menu.Dropdown>
</Menu> </Menu>
</Group> </Group>
)) )
)
) : ( ) : (
<Text size="sm" c="dimmed" ta="center" mt="xl"> <Text size="sm" c="dimmed" ta="center" mt="xl">
{isLoadingChats ? 'Loading...' : 'No saved chats'} {isLoadingChats ? 'Loading...' : 'No saved chats'}
@@ -744,49 +740,49 @@ export default function ChatLayout() {
onScrollPositionChange={handleScroll} onScrollPositionChange={handleScroll}
classNames={{ viewport: classes.chatScrollViewport }} classNames={{ viewport: classes.chatScrollViewport }}
> >
<Stack gap="xl" px="md" py="lg"> <Stack gap="md" px="md" py="lg">
{messages.map((message) => ( {messages.map((message) => (
<Group <Group
key={message.id} key={message.id}
justify={message.role === 'user' ? 'flex-end' : 'flex-start'} justify={message.role === 'user' ? 'flex-end' : 'flex-start'}
align="flex-start" align="flex-start"
wrap="nowrap" wrap="nowrap"
gap="sm"
> >
{message.role === 'assistant' && ( {message.role === 'assistant' && (
<Avatar radius="xl" color={primaryColor} variant="light"> <Avatar radius="xl" color={primaryColor} variant="light" size="sm">
<IconRobot size={20} /> <IconRobot size={16} />
</Avatar> </Avatar>
)} )}
<Paper
p="md"
radius="lg"
bg={
message.role === 'user'
? 'var(--mantine-color-default-hover)'
: 'transparent'
}
style={{
maxWidth: '80%',
borderTopLeftRadius: message.role === 'assistant' ? 0 : undefined,
borderTopRightRadius: message.role === 'user' ? 0 : undefined,
}}
>
{message.role === 'assistant' ? ( {message.role === 'assistant' ? (
// Assistant message - no bubble, just text aligned with avatar
<div style={{ maxWidth: '85%', paddingTop: 2 }}>
<MarkdownMessage <MarkdownMessage
content={message.content} content={message.content}
isStreaming={message.id === streamingMessageId} isStreaming={message.id === streamingMessageId}
/> />
</div>
) : ( ) : (
<Text size="sm" style={{ lineHeight: 1.6 }}> // User message - colored bubble
<Paper
py="xs"
px="md"
radius="lg"
bg={`var(--mantine-color-${primaryColor}-light)`}
style={{
maxWidth: '75%',
}}
>
<Text size="sm" style={{ lineHeight: 1.5 }}>
{message.content} {message.content}
</Text> </Text>
)}
</Paper> </Paper>
)}
{message.role === 'user' && ( {message.role === 'user' && (
<Avatar radius="xl" color="gray" variant="light"> <Avatar radius="xl" color={primaryColor} variant="light" size="sm">
<IconUser size={20} /> <IconUser size={16} />
</Avatar> </Avatar>
)} )}
</Group> </Group>