feat: WIP

This commit is contained in:
Aleksander Grygier 2026-04-01 02:11:01 +02:00
parent c3520f1e2c
commit 5acfc403bd
13 changed files with 231 additions and 198 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -18,7 +18,7 @@
<div style="display: contents">
<script>
{
__sveltekit_uoz4e7 = {
__sveltekit_1a51eho = {
base: new URL('.', location).pathname.slice(0, -1)
};

View File

@ -247,6 +247,8 @@
</Tooltip.Root>
{/if}
<DropdownMenu.Separator />
<Tooltip.Root delayDuration={TOOLTIP_DELAY_DURATION}>
<Tooltip.Trigger class="w-full">
<DropdownMenu.Item
@ -264,8 +266,6 @@
</Tooltip.Content>
</Tooltip.Root>
<DropdownMenu.Separator />
<DropdownMenu.Sub onOpenChange={handleToolsSubMenuOpen}>
<DropdownMenu.SubTrigger class="flex cursor-pointer items-center gap-2">
<PencilRuler class="h-4 w-4" />

View File

@ -26,7 +26,7 @@
import type { Component } from 'svelte';
interface Props {
class: string;
class?: string;
onSave?: () => void;
initialSection?: SettingsSectionTitle;
}

View File

@ -140,64 +140,66 @@
</script>
<div class="flex h-full flex-col">
<ScrollArea class="flex-1">
<Sidebar.Header class=" top-0 z-10 gap-4 bg-sidebar/50 p-3 pt-4 pb-2 backdrop-blur-lg md:sticky">
<a href="#/" onclick={handleMobileSidebarItemClick}>
<h1 class="inline-flex items-center gap-1 px-2 text-xl font-semibold">llama.cpp</h1>
</a>
<ScrollArea class="flex-1">
<Sidebar.Header
class=" top-0 z-10 gap-4 bg-sidebar/50 p-3 pt-4 pb-2 backdrop-blur-lg md:sticky"
>
<a href="#/" onclick={handleMobileSidebarItemClick}>
<h1 class="inline-flex items-center gap-1 px-2 text-xl font-semibold">llama.cpp</h1>
</a>
<ChatSidebarActions {handleMobileSidebarItemClick} bind:isSearchModeActive bind:searchQuery />
</Sidebar.Header>
<ChatSidebarActions {handleMobileSidebarItemClick} bind:isSearchModeActive bind:searchQuery />
</Sidebar.Header>
<Sidebar.Group class="mt-2 space-y-2 p-0 px-3">
{#if (filteredConversations.length > 0 && isSearchModeActive) || !isSearchModeActive}
<Sidebar.GroupLabel>
{isSearchModeActive ? 'Search results' : 'Conversations'}
</Sidebar.GroupLabel>
{/if}
<Sidebar.Group class="mt-2 space-y-2 p-0 px-3">
{#if (filteredConversations.length > 0 && isSearchModeActive) || !isSearchModeActive}
<Sidebar.GroupLabel>
{isSearchModeActive ? 'Search results' : 'Conversations'}
</Sidebar.GroupLabel>
{/if}
<Sidebar.GroupContent>
<Sidebar.Menu>
{#each conversationTree as { conversation, depth } (conversation.id)}
<Sidebar.MenuItem class="mb-1 p-0">
<ChatSidebarConversationItem
conversation={{
id: conversation.id,
name: conversation.name,
lastModified: conversation.lastModified,
currNode: conversation.currNode,
forkedFromConversationId: conversation.forkedFromConversationId
}}
{depth}
{handleMobileSidebarItemClick}
isActive={currentChatId === conversation.id}
onSelect={selectConversation}
onEdit={handleEditConversation}
onDelete={handleDeleteConversation}
onStop={handleStopGeneration}
/>
</Sidebar.MenuItem>
{/each}
<Sidebar.GroupContent>
<Sidebar.Menu>
{#each conversationTree as { conversation, depth } (conversation.id)}
<Sidebar.MenuItem class="mb-1 p-0">
<ChatSidebarConversationItem
conversation={{
id: conversation.id,
name: conversation.name,
lastModified: conversation.lastModified,
currNode: conversation.currNode,
forkedFromConversationId: conversation.forkedFromConversationId
}}
{depth}
{handleMobileSidebarItemClick}
isActive={currentChatId === conversation.id}
onSelect={selectConversation}
onEdit={handleEditConversation}
onDelete={handleDeleteConversation}
onStop={handleStopGeneration}
/>
</Sidebar.MenuItem>
{/each}
{#if conversationTree.length === 0}
<div class="px-2 py-4 text-center">
<p class="mb-4 p-4 text-sm text-muted-foreground">
{searchQuery.length > 0
? 'No results found'
: isSearchModeActive
? 'Start typing to see results'
: 'No conversations yet'}
</p>
</div>
{/if}
</Sidebar.Menu>
</Sidebar.GroupContent>
</Sidebar.Group>
</ScrollArea>
{#if conversationTree.length === 0}
<div class="px-2 py-4 text-center">
<p class="mb-4 p-4 text-sm text-muted-foreground">
{searchQuery.length > 0
? 'No results found'
: isSearchModeActive
? 'Start typing to see results'
: 'No conversations yet'}
</p>
</div>
{/if}
</Sidebar.Menu>
</Sidebar.GroupContent>
</Sidebar.Group>
</ScrollArea>
<Sidebar.Footer>
<ChatSidebarFooter />
</Sidebar.Footer>
<Sidebar.Footer>
<ChatSidebarFooter />
</Sidebar.Footer>
</div>
<DialogConfirmation

View File

@ -96,6 +96,5 @@
Import / Export
</div>
</Button>
{/if}
</div>

View File

@ -13,7 +13,9 @@
<div class="space-y-1 pt-0">
<Button
class="w-full justify-between px-2 backdrop-blur-none! hover:[&>kbd]:opacity-100 {isMcpActive ? 'bg-accent text-accent-foreground' : ''}"
class="w-full justify-between px-2 backdrop-blur-none! hover:[&>kbd]:opacity-100 {isMcpActive
? 'bg-accent text-accent-foreground'
: ''}"
onclick={() => {
mcpServersDialog.open();
}}
@ -27,7 +29,9 @@
</Button>
<Button
class="w-full justify-between px-2 backdrop-blur-none! hover:[&>kbd]:opacity-100 {isSettingsActive ? 'bg-accent text-accent-foreground' : ''}"
class="w-full justify-between px-2 backdrop-blur-none! hover:[&>kbd]:opacity-100 {isSettingsActive
? 'bg-accent text-accent-foreground'
: ''}"
onclick={() => {
chatSettingsDialog.open();
}}

View File

@ -4,12 +4,13 @@
import type { SettingsSectionTitle } from '$lib/constants';
interface Props {
class?: string;
onOpenChange?: (open: boolean) => void;
open?: boolean;
initialSection?: SettingsSectionTitle;
}
let { onOpenChange, open = false, initialSection }: Props = $props();
let { class: className = '', onOpenChange, open = false, initialSection }: Props = $props();
let chatSettingsRef: ChatSettings | undefined = $state();
@ -31,7 +32,7 @@
<Dialog.Root {open} onOpenChange={handleClose}>
<Dialog.Content
class="z-999999 flex h-[100dvh] max-h-[100dvh] min-h-[100dvh] max-w-4xl! flex-col gap-0 rounded-none
p-0 md:h-[64vh] md:max-h-[64vh] md:min-h-0 md:rounded-lg"
p-0 md:h-[64vh] md:max-h-[64vh] md:min-h-0 md:rounded-lg {className}"
>
<ChatSettings bind:this={chatSettingsRef} onSave={handleSave} {initialSection} />
</Dialog.Content>

View File

@ -8,11 +8,12 @@
import { McpServerCard, McpServerCardSkeleton, McpServerForm } from '$lib/components/app/mcp';
import { MCP_SERVER_ID_PREFIX } from '$lib/constants';
import { HealthCheckStatus } from '$lib/enums';
import McpLogo from './McpLogo.svelte';
interface Props {
class?: string;
}
let { class: className }: Props = $props();
let servers = $derived(mcpStore.getServersSorted());
@ -83,10 +84,12 @@
}
</script>
<div class="gap-5 grid md:space-y-4 {className}">
<div class="grid gap-5 md:space-y-4 {className}">
<div class="flex items-start justify-between gap-4">
<div>
<h4 class="text-base font-semibold">Manage Servers</h4>
<div class="flex items-center gap-2">
<McpLogo class="h-6 w-6" />
<h2 class="text-2xl font-semibold">MCP Servers</h2>
</div>
{#if !isAddingServer}

View File

@ -282,7 +282,7 @@
: 'text-muted-foreground',
isOpen ? 'text-foreground' : ''
)}
style="max-width: min(calc(100cqw - 9rem), 20rem)"
style="max-width: min(calc(100cqw - 9rem), 24rem)"
disabled={disabled || updating}
>
<Package class="h-3.5 w-3.5" />

View File

@ -22,7 +22,9 @@
data-slot="sidebar-trigger"
variant="ghost"
size="icon-lg"
class="rounded-full backdrop-blur-lg {className} {sidebar.open ? 'top-1.5' : 'top-0'} md:left-[14.5rem]"
class="rounded-full backdrop-blur-lg {className} {sidebar.open
? 'top-1.5'
: 'top-0'} md:left-[14.5rem]"
type="button"
onclick={(e) => {
onclick?.(e);
@ -31,9 +33,9 @@
{...restProps}
>
{#if sidebar.open}
<PanelLeftClose />
<PanelLeftClose />
{:else}
<PanelLeftIcon />
<PanelLeftIcon />
{/if}
<span class="sr-only">Toggle Sidebar</span>
</Button>

View File

@ -8,9 +8,12 @@
ChatSidebar,
ChatSettings,
McpServersSettings,
McpLogo,
DialogConversationTitleUpdate,
DialogChatSettingsImportExport
} from '$lib/components/app';
import { Settings } from '@lucide/svelte';
import { Button } from '$lib/components/ui/button';
import { isLoading } from '$lib/stores/chat.svelte';
import { conversationsStore, activeMessages } from '$lib/stores/conversations.svelte';
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
@ -291,16 +294,37 @@
/>
{/if}
{#if !sidebarOpen}
<div class="absolute bottom-0 left-0 z-[900] flex flex-col gap-1 p-2">
<Button
variant="ghost"
size="icon"
class="h-8 w-8 {activePanel === 'mcp' ? 'bg-accent text-accent-foreground' : ''}"
onclick={() => (activePanel = activePanel === 'mcp' ? 'chat' : 'mcp')}
>
<McpLogo class="h-4 w-4" />
</Button>
<Button
variant="ghost"
size="icon"
class="h-8 w-8 {activePanel === 'settings' ? 'bg-accent text-accent-foreground' : ''}"
onclick={() => (activePanel = activePanel === 'settings' ? 'chat' : 'settings')}
>
<Settings class="h-4 w-4" />
</Button>
</div>
{/if}
<Sidebar.Inset class="flex flex-1 flex-col overflow-hidden">
{#if activePanel === 'settings'}
<ChatSettings
bind:this={chatSettingsRef}
onSave={() => (activePanel = 'chat')}
initialSection={chatSettingsInitialSection}
class="p-8! mx-auto h-full"
class="mx-auto h-full p-8!"
/>
{:else if activePanel === 'mcp'}
<McpServersSettings class="w-full mx-auto p-8! md:translate-x-1.5" />
<McpServersSettings class="mx-auto w-full p-8! md:translate-x-1.5" />
{:else}
{@render children?.()}
{/if}