changes
This commit is contained in:
+119
-123
@@ -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,106 +619,102 @@ 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) =>
|
||||||
<Group
|
editingChatId === chat.id ? (
|
||||||
key={chat.id}
|
// Inline editing mode
|
||||||
gap={0}
|
<Group
|
||||||
wrap="nowrap"
|
key={chat.id}
|
||||||
style={{
|
wrap="nowrap"
|
||||||
borderRadius: 'var(--mantine-radius-md)',
|
gap="xs"
|
||||||
backgroundColor:
|
px="sm"
|
||||||
activeChatId === chat.id
|
py={6}
|
||||||
? 'var(--mantine-color-default-hover)'
|
style={{ minWidth: 0 }}
|
||||||
: 'transparent',
|
>
|
||||||
transition: 'background-color 0.2s',
|
{chat.pinned ? (
|
||||||
}}
|
<IconPin size={16} stroke={1.5} style={{ minWidth: 16 }} />
|
||||||
>
|
) : (
|
||||||
{editingChatId === chat.id ? (
|
<IconMessage size={16} stroke={1.5} style={{ minWidth: 16 }} />
|
||||||
// Inline editing mode
|
)}
|
||||||
<Group wrap="nowrap" gap="xs" p="sm" style={{ flex: 1, minWidth: 0 }}>
|
<TextInput
|
||||||
{chat.pinned ? (
|
ref={editInputRef}
|
||||||
<IconPin size={18} color="gray" style={{ minWidth: 18 }} />
|
value={editingTitle}
|
||||||
) : (
|
onChange={(e) => setEditingTitle(e.currentTarget.value)}
|
||||||
<IconMessage size={18} color="gray" style={{ minWidth: 18 }} />
|
onBlur={saveRenamedChat}
|
||||||
)}
|
onKeyDown={handleRenameKeyDown}
|
||||||
<TextInput
|
size="xs"
|
||||||
ref={editInputRef}
|
style={{ flex: 1 }}
|
||||||
value={editingTitle}
|
/>
|
||||||
onChange={(e) => setEditingTitle(e.currentTarget.value)}
|
</Group>
|
||||||
onBlur={saveRenamedChat}
|
) : (
|
||||||
onKeyDown={handleRenameKeyDown}
|
// Normal display mode with NavLink
|
||||||
size="xs"
|
<Group key={chat.id} gap={0} wrap="nowrap">
|
||||||
variant="unstyled"
|
<NavLink
|
||||||
styles={{
|
component="button"
|
||||||
input: {
|
active={activeChatId === chat.id}
|
||||||
padding: 0,
|
color={primaryColor}
|
||||||
height: 'auto',
|
variant="subtle"
|
||||||
minHeight: 'unset',
|
label={chat.title}
|
||||||
fontSize: 'var(--mantine-font-size-sm)',
|
leftSection={
|
||||||
},
|
chat.pinned ? (
|
||||||
}}
|
<IconPin size={16} stroke={1.5} />
|
||||||
style={{ flex: 1 }}
|
|
||||||
/>
|
|
||||||
</Group>
|
|
||||||
) : (
|
|
||||||
// Normal display mode
|
|
||||||
<UnstyledButton
|
|
||||||
onClick={() => handleSelectChat(chat)}
|
|
||||||
p="sm"
|
|
||||||
style={{ flex: 1, minWidth: 0 }}
|
|
||||||
>
|
|
||||||
<Group wrap="nowrap" gap="xs">
|
|
||||||
{chat.pinned ? (
|
|
||||||
<IconPin size={18} color="gray" style={{ minWidth: 18 }} />
|
|
||||||
) : (
|
) : (
|
||||||
<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,
|
||||||
<Menu position="bottom-end" withArrow>
|
borderRadius: 'var(--mantine-radius-sm)',
|
||||||
<Menu.Target>
|
padding: '6px 10px',
|
||||||
<ActionIcon
|
},
|
||||||
variant="subtle"
|
label: {
|
||||||
color="gray"
|
overflow: 'hidden',
|
||||||
size="sm"
|
textOverflow: 'ellipsis',
|
||||||
mr="xs"
|
},
|
||||||
onClick={(e) => e.stopPropagation()}
|
}}
|
||||||
>
|
/>
|
||||||
<IconDotsVertical size={16} />
|
<Menu position="bottom-end" withArrow>
|
||||||
</ActionIcon>
|
<Menu.Target>
|
||||||
</Menu.Target>
|
<ActionIcon
|
||||||
<Menu.Dropdown>
|
variant="subtle"
|
||||||
<Menu.Item
|
color="gray"
|
||||||
leftSection={<IconPencil size={14} />}
|
size="sm"
|
||||||
onClick={() => handleRenameChat(chat.id)}
|
onClick={(e) => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
Rename
|
<IconDotsVertical size={14} />
|
||||||
</Menu.Item>
|
</ActionIcon>
|
||||||
<Menu.Item
|
</Menu.Target>
|
||||||
leftSection={<IconPin size={14} />}
|
<Menu.Dropdown>
|
||||||
onClick={() => handlePinChat(chat.id)}
|
<Menu.Item
|
||||||
>
|
leftSection={<IconPencil size={14} />}
|
||||||
{chat.pinned ? 'Unpin' : 'Pin'}
|
onClick={() => handleRenameChat(chat.id)}
|
||||||
</Menu.Item>
|
>
|
||||||
<Menu.Divider />
|
Rename
|
||||||
<Menu.Item
|
</Menu.Item>
|
||||||
color="red"
|
<Menu.Item
|
||||||
leftSection={<IconTrash size={14} />}
|
leftSection={<IconPin size={14} />}
|
||||||
onClick={() => handleRemoveChat(chat.id)}
|
onClick={() => handlePinChat(chat.id)}
|
||||||
>
|
>
|
||||||
Remove
|
{chat.pinned ? 'Unpin' : 'Pin'}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
</Menu.Dropdown>
|
<Menu.Divider />
|
||||||
</Menu>
|
<Menu.Item
|
||||||
</Group>
|
color="red"
|
||||||
))
|
leftSection={<IconTrash size={14} />}
|
||||||
|
onClick={() => handleRemoveChat(chat.id)}
|
||||||
|
>
|
||||||
|
Remove
|
||||||
|
</Menu.Item>
|
||||||
|
</Menu.Dropdown>
|
||||||
|
</Menu>
|
||||||
|
</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
|
{message.role === 'assistant' ? (
|
||||||
p="md"
|
// Assistant message - no bubble, just text aligned with avatar
|
||||||
radius="lg"
|
<div style={{ maxWidth: '85%', paddingTop: 2 }}>
|
||||||
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' ? (
|
|
||||||
<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>
|
||||||
|
|||||||
Reference in New Issue
Block a user