Skip to content

Commit b592954

Browse files
committed
feat: UI improvements
1 parent 93fb1dc commit b592954

File tree

2 files changed

+102
-61
lines changed

2 files changed

+102
-61
lines changed

tools/server/webui/src/lib/components/chat/ChatScreen.svelte

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
stopGeneration
1010
} from '$lib/stores/chat.svelte';
1111
import { onMount } from 'svelte';
12-
import { fly, slide } from 'svelte/transition';
12+
import { fade, fly, slide } from 'svelte/transition';
1313
import { Upload } from '@lucide/svelte';
1414
import type { ChatUploadedFile } from '$lib/types/chat.d.ts';
1515
import type { DatabaseMessageExtra } from '$lib/types/database.d.ts';
@@ -288,7 +288,7 @@
288288
>
289289
<ChatMessages class="mb-16 md:mb-24" messages={activeMessages()} />
290290

291-
<div class="sticky bottom-0 left-0 right-0 mt-auto" in:slide={{ duration: 400, axis: 'y' }}>
291+
<div class="sticky bottom-0 left-0 right-0 mt-auto" in:slide={{ duration: 150, axis: 'y' }}>
292292
<div class="conversation-chat-form rounded-t-3xl pb-4">
293293
<ChatForm
294294
isLoading={isLoading()}
@@ -313,20 +313,17 @@
313313
aria-label="Welcome screen with file drop zone"
314314
>
315315
<div class="w-full max-w-2xl px-4">
316-
<div class="mb-8 text-center" in:fly={{ y: -30, duration: 600 }}>
316+
<div class="mb-8 text-center" in:fade={{ duration: 300 }}>
317317
<h1 class="mb-2 text-3xl font-semibold tracking-tight">llama.cpp</h1>
318318

319319
<p class="text-muted-foreground text-lg">How can I help you today?</p>
320320
</div>
321321

322-
<div
323-
class="mb-6 flex justify-center"
324-
in:slide={{ duration: 500, delay: 300, axis: 'y' }}
325-
>
322+
<div class="mb-6 flex justify-center" in:fly={{ y: 10, duration: 300, delay: 200 }}>
326323
<ServerInfo />
327324
</div>
328325

329-
<div in:slide={{ duration: 600, delay: 500, axis: 'y' }}>
326+
<div in:fly={{ y: 10, duration: 250, delay: 300 }}>
330327
<ChatForm
331328
isLoading={isLoading()}
332329
showHelperText={true}

tools/server/webui/src/lib/components/chat/ChatSidebar/ChatSidebar.svelte

Lines changed: 97 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
33
import { Button } from '$lib/components/ui/button';
44
import { Input } from '$lib/components/ui/input';
5-
import { Plus, Search } from '@lucide/svelte';
5+
import { Plus, Search, SquarePen, X } from '@lucide/svelte';
66
import ChatSidebarConversationItem from '$lib/components/chat/ChatSidebar/ChatSidebarConversationItem.svelte';
77
import { conversations, deleteConversation } from '$lib/stores/chat.svelte';
88
import { goto } from '$app/navigation';
99
import { page } from '$app/state';
1010
import { useSidebar } from '$lib/components/ui/sidebar';
11+
import ScrollArea from '$lib/components/ui/scroll-area/scroll-area.svelte';
1112
1213
const sidebar = useSidebar();
1314
@@ -19,11 +20,22 @@
1920
2021
let currentChatId = $derived(page.params.id);
2122
let searchQuery = $state('');
22-
let filteredConversations = $derived(
23-
conversations().filter((conversation: { name: string }) =>
24-
conversation.name.toLowerCase().includes(searchQuery.toLowerCase())
25-
)
26-
);
23+
24+
let isSearchModeActive = $state(false);
25+
26+
let filteredConversations = $derived.by(() => {
27+
if (isSearchModeActive && searchQuery.trim().length > 0) {
28+
return conversations().filter((conversation: { name: string }) =>
29+
conversation.name.toLowerCase().includes(searchQuery.toLowerCase())
30+
);
31+
}
32+
33+
if (isSearchModeActive && searchQuery.trim().length === 0) {
34+
return [];
35+
}
36+
37+
return conversations();
38+
});
2739
2840
async function selectConversation(id: string) {
2941
await goto(`/chat/${id}`);
@@ -38,61 +50,93 @@
3850
}
3951
</script>
4052

41-
<Sidebar.Header class="px-0 pb-4">
42-
<div class="py-2">
43-
<a href="/" onclick={handleMobileSidebarItemClick}>
44-
<h1 class="text-xl font-semibold">llama.cpp</h1>
45-
</a>
46-
</div>
53+
<Sidebar.Header class="px-1 pb-2 pt-4">
54+
<a href="/" onclick={handleMobileSidebarItemClick}>
55+
<h1 class="inline-flex items-center gap-1 text-xl font-semibold">llama.cpp</h1>
56+
</a>
4757
</Sidebar.Header>
4858

49-
<div class="relative pb-4">
50-
<Search class="text-muted-foreground absolute left-2 top-2.5 h-4 w-4" />
51-
<Input bind:value={searchQuery} placeholder="Search conversations..." class="pl-8" />
52-
</div>
59+
<div class="space-y-0.5 py-4">
60+
{#if isSearchModeActive}
61+
<div class="relative">
62+
<Search class="text-muted-foreground absolute left-2 top-2.5 h-4 w-4" />
63+
64+
<Input bind:value={searchQuery} placeholder="Search conversations..." class="pl-8" />
65+
66+
<X
67+
class="cursor-pointertext-muted-foreground absolute right-2 top-2.5 h-4 w-4"
68+
onclick={() => (isSearchModeActive = false)}
69+
/>
70+
</div>
71+
{:else}
72+
<Button
73+
class="w-full justify-start gap-2"
74+
href="/?new_chat=true"
75+
variant="ghost"
76+
onclick={handleMobileSidebarItemClick}
77+
>
78+
<SquarePen class="h-4 w-4" />
5379

54-
<div class="pb-4">
55-
<Button
56-
href="/?new_chat=true"
57-
class="border-muted-foreground/25 hover:bg-accent hover:border-accent-foreground/25 w-full justify-start gap-2 rounded-lg border-2 border-dashed bg-transparent transition-colors"
58-
variant="ghost"
59-
onclick={handleMobileSidebarItemClick}
60-
>
61-
<Plus class="h-4 w-4" />
62-
63-
New Chat
64-
</Button>
80+
New chat
81+
</Button>
82+
83+
<Button
84+
class="w-full justify-start gap-2"
85+
variant="ghost"
86+
onclick={() => {
87+
isSearchModeActive = true;
88+
}}
89+
>
90+
<Search class="h-4 w-4" />
91+
92+
Search conversations
93+
</Button>
94+
{/if}
6595
</div>
6696

6797
<Sidebar.Group class="space-y-2 p-0">
68-
<Sidebar.GroupLabel>Conversations</Sidebar.GroupLabel>
98+
{#if (filteredConversations.length > 0 && isSearchModeActive) || !isSearchModeActive}
99+
<Sidebar.GroupLabel>
100+
{isSearchModeActive ? 'Search results' : 'Conversations'}
101+
</Sidebar.GroupLabel>
102+
{/if}
69103

70104
<Sidebar.GroupContent>
71-
<Sidebar.Menu class="space-y-0.5">
72-
{#each filteredConversations as conversation (conversation.id)}
73-
<Sidebar.MenuItem onclick={handleMobileSidebarItemClick}>
74-
<ChatSidebarConversationItem
75-
conversation={{
76-
id: conversation.id,
77-
name: conversation.name,
78-
lastModified: conversation.lastModified,
79-
currNode: conversation.currNode
80-
}}
81-
isActive={currentChatId === conversation.id}
82-
onSelect={selectConversation}
83-
onEdit={editConversation}
84-
onDelete={handleDeleteConversation}
85-
/>
86-
</Sidebar.MenuItem>
87-
{/each}
88-
89-
{#if filteredConversations.length === 0}
90-
<div class="px-2 py-4 text-center">
91-
<p class="text-muted-foreground text-sm">
92-
{searchQuery ? 'No conversations found' : 'No conversations yet'}
93-
</p>
94-
</div>
95-
{/if}
105+
<Sidebar.Menu>
106+
<ScrollArea
107+
class={!isSearchModeActive
108+
? 'h-[calc(100vh-16.5rem)]'
109+
: 'h-[calc(100vh-11.625rem)]'}
110+
>
111+
{#each filteredConversations as conversation (conversation.id)}
112+
<Sidebar.MenuItem class="mb-1" onclick={handleMobileSidebarItemClick}>
113+
<ChatSidebarConversationItem
114+
conversation={{
115+
id: conversation.id,
116+
name: conversation.name,
117+
lastModified: conversation.lastModified,
118+
currNode: conversation.currNode
119+
}}
120+
isActive={currentChatId === conversation.id}
121+
onSelect={selectConversation}
122+
onEdit={editConversation}
123+
onDelete={handleDeleteConversation}
124+
/>
125+
</Sidebar.MenuItem>
126+
{/each}
127+
128+
{#if filteredConversations.length === 0}
129+
<div class="px-2 py-4 text-center">
130+
<p class="text-muted-foreground text-sm">
131+
{searchQuery.length > 0
132+
? 'No results found'
133+
: isSearchModeActive
134+
? 'Start typing to see results'
135+
: 'No conversations yet'}
136+
</p>
137+
</div>
138+
{/if}
139+
</ScrollArea>
96140
</Sidebar.Menu>
97141
</Sidebar.GroupContent>
98142
</Sidebar.Group>

0 commit comments

Comments
 (0)