This commit is contained in:
Aleksander Grygier 2026-04-02 13:57:59 +02:00
parent 5468fd03e3
commit 6ec8aa9c6e
7 changed files with 226 additions and 187 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -179,7 +179,7 @@
/>
</Sidebar.Header>
<Sidebar.Group class="mt-2 space-y-2 p-0 px-3">
<Sidebar.Group class="mt-2 h-[calc(100vh-21rem)] space-y-2 p-0 px-3">
{#if (filteredConversations.length > 0 && isSearchModeActive) || !isSearchModeActive}
<Sidebar.GroupLabel>
{isSearchModeActive ? 'Search results' : 'Conversations'}

View File

@ -0,0 +1,83 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { page } from '$app/state';
import { ActionIcon } from '$lib/components/app/actions';
import { McpLogo } from '$lib/components/app/mcp';
import { Database, Settings, Search, SquarePen } from '@lucide/svelte';
interface Props {
sidebarOpen: boolean;
onSearchClick: () => void;
}
let { sidebarOpen, onSearchClick }: Props = $props();
let isMcpActive = $derived(page.route.id === '/settings/mcp');
let isImportExportActive = $derived(page.route.id === '/settings/import-export');
let isSettingsActive = $derived(page.route.id === '/settings/chat');
</script>
<!-- Spacer to reserve space for icon strip in the flex layout -->
<div
class="hidden shrink-0 transition-[width] duration-200 ease-linear md:block {sidebarOpen
? 'w-0'
: 'w-[calc(var(--sidebar-width-icon)+1.5rem)]'}"
></div>
<!-- Desktop: icon strip, fixed position so it stays stationary and only fades -->
<aside
class="fixed top-0 bottom-0 left-0 z-10 hidden w-[calc(var(--sidebar-width-icon)+1.5rem)] flex-col items-center justify-between py-3 transition-opacity duration-200 ease-linear md:flex {sidebarOpen
? 'pointer-events-none opacity-0'
: 'opacity-100'}"
>
<div class="mt-12 flex flex-col items-center gap-1">
<ActionIcon
icon={SquarePen}
tooltip="New Chat"
size="lg"
iconSize="h-4 w-4"
class="h-9 w-9 rounded-full hover:bg-accent!"
onclick={() => goto('?new_chat=true#/')}
/>
<ActionIcon
icon={Search}
tooltip="Search"
size="lg"
iconSize="h-4 w-4"
class="h-9 w-9 rounded-full hover:bg-accent!"
onclick={onSearchClick}
/>
</div>
<div class="flex flex-col items-center gap-1">
<ActionIcon
icon={McpLogo}
tooltip="MCP Servers"
size="lg"
iconSize="h-4 w-4"
class="h-9 w-9 rounded-full hover:bg-accent! {isMcpActive
? 'bg-accent text-accent-foreground'
: ''}"
onclick={() => goto('#/settings/mcp')}
/>
<ActionIcon
icon={Database}
tooltip="Import / Export"
size="lg"
iconSize="h-4 w-4"
class="h-9 w-9 rounded-full hover:bg-accent! {isImportExportActive
? 'bg-accent text-accent-foreground'
: ''}"
onclick={() => goto('#/settings/import-export')}
/>
<ActionIcon
icon={Settings}
tooltip="Settings"
size="lg"
iconSize="h-4 w-4"
class="h-9 w-9 rounded-full hover:bg-accent! {isSettingsActive
? 'bg-accent text-accent-foreground'
: ''}"
onclick={() => goto('#/settings/chat')}
/>
</div>
</aside>

View File

@ -63,3 +63,11 @@ export { default as DropdownMenuSearchable } from './DropdownMenuSearchable.svel
* ```
*/
export { default as DropdownMenuActions } from './DropdownMenuActions.svelte';
/**
* **DesktopIconStrip** - Fixed icon strip for desktop sidebar
*
* Vertical icon strip shown on desktop when the sidebar is collapsed.
* Contains navigation shortcuts for new chat, search, MCP, import/export, and settings.
*/
export { default as DesktopIconStrip } from './DesktopIconStrip.svelte';

View File

@ -7,12 +7,10 @@
import {
ChatSidebar,
ChatSettings,
McpLogo,
DesktopIconStrip,
DialogConversationTitleUpdate,
DialogChatSettingsImportExport
} from '$lib/components/app';
import { Database, Settings, Search, SquarePen } from '@lucide/svelte';
import { Button } from '$lib/components/ui/button';
import { conversationsStore } from '$lib/stores/conversations.svelte';
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
import * as Tooltip from '$lib/components/ui/tooltip';
@ -46,9 +44,6 @@
let titleUpdateResolve: ((value: boolean) => void) | null = null;
let activePanel = $state<'chat' | 'settings' | 'mcp'>('chat');
let isMcpActive = $derived(page.route.id === '/settings/mcp');
let isImportExportActive = $derived(page.route.id === '/settings/import-export');
let isSettingsActive = $derived(page.route.id === '/settings/chat');
let isSettingsRoute = $derived(!!page.route.id?.startsWith('/settings'));
let chatSettingsRef: ChatSettings | undefined = $state();
let importExportDialogOpen = $state(false);
@ -255,62 +250,15 @@
{/if}
{#if isDesktop && !alwaysShowSidebarOnDesktop}
<!-- Desktop: icon strip, always rendered, transitions width/opacity -->
<aside
class="hidden shrink-0 flex-col items-center justify-between overflow-hidden py-3 transition-[width,opacity] duration-200 ease-linear md:flex {sidebarOpen
? 'pointer-events-none w-0 opacity-0'
: 'w-[calc(var(--sidebar-width-icon)+1.5rem)] opacity-100'}"
>
<div class="mt-12 flex flex-col items-center gap-1">
<Button variant="ghost" size="icon-lg" class="rounded-full" href="?new_chat=true#/">
<SquarePen class="h-4 w-4" />
<span class="sr-only">New Chat</span>
</Button>
<Button
variant="ghost"
size="icon-lg"
class="rounded-full"
onclick={() => {
if (chatSidebar?.activateSearchMode) {
chatSidebar.activateSearchMode();
}
sidebarOpen = true;
}}
>
<Search class="h-4 w-4" />
<span class="sr-only">Search</span>
</Button>
</div>
<div class="flex flex-col items-center gap-1">
<Button
variant="ghost"
size="icon-lg"
href="#/settings/mcp"
class="rounded-full {isMcpActive ? 'bg-accent text-accent-foreground' : ''}"
>
<McpLogo class="h-4 w-4" />
<span class="sr-only">MCP Servers</span>
</Button>
<Button
variant="ghost"
size="icon-lg"
href="#/settings/import-export"
class="rounded-full {isImportExportActive ? 'bg-accent text-accent-foreground' : ''}"
>
<Database class="h-4 w-4" />
<span class="sr-only">Import / Export</span>
</Button>
<Button
variant="ghost"
size="icon-lg"
href="#/settings/chat"
class="rounded-full {isSettingsActive ? 'bg-accent text-accent-foreground' : ''}"
>
<Settings class="h-4 w-4" />
<span class="sr-only">Settings</span>
</Button>
</div>
</aside>
<DesktopIconStrip
{sidebarOpen}
onSearchClick={() => {
if (chatSidebar?.activateSearchMode) {
chatSidebar.activateSearchMode();
}
sidebarOpen = true;
}}
/>
{/if}
<Sidebar.Inset class="flex flex-1 flex-col overflow-auto">