mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-09-20 01:31:31 +02:00
ui : track sidebar expanded state in a shared ui store
Move the desktop sidebar expanded/collapsed state out of deviceStore into a dedicated uiStore so the chat tab bar can react to it. Assisted-by: pi
This commit is contained in:
committed by
GitHub
parent
1ae4576fe3
commit
b59d8bfe9d
+26
-25
@@ -14,7 +14,7 @@
|
|||||||
import { useKeyboardShortcuts } from '$lib/hooks/use-keyboard-shortcuts.svelte';
|
import { useKeyboardShortcuts } from '$lib/hooks/use-keyboard-shortcuts.svelte';
|
||||||
import { useMarqueeSelection } from '$lib/hooks/use-marquee-selection.svelte';
|
import { useMarqueeSelection } from '$lib/hooks/use-marquee-selection.svelte';
|
||||||
import { RouterService } from '$lib/services/router.service';
|
import { RouterService } from '$lib/services/router.service';
|
||||||
import { chatStore, conversationsStore, deviceStore, settingsStore } from '$lib/stores';
|
import { chatStore, conversationsStore, deviceStore, settingsStore, uiStore } from '$lib/stores';
|
||||||
import { buildConversationTree } from '$lib/utils';
|
import { buildConversationTree } from '$lib/utils';
|
||||||
import { circIn } from 'svelte/easing';
|
import { circIn } from 'svelte/easing';
|
||||||
import { SvelteSet } from 'svelte/reactivity';
|
import { SvelteSet } from 'svelte/reactivity';
|
||||||
@@ -31,30 +31,29 @@
|
|||||||
toggleSidebar: () => toggleExpandedMode()
|
toggleSidebar: () => toggleExpandedMode()
|
||||||
});
|
});
|
||||||
|
|
||||||
let isExpandedMode = $state(false);
|
|
||||||
let hoveredTooltip = $state<string | null>(null);
|
let hoveredTooltip = $state<string | null>(null);
|
||||||
let logoHovered = $state(false);
|
let logoHovered = $state(false);
|
||||||
|
|
||||||
const isStripExpanded = $derived(isExpandedMode || hoveredTooltip !== null);
|
const isStripExpanded = $derived(uiStore.isSidebarExpanded || hoveredTooltip !== null);
|
||||||
const isOnMobile = $derived(deviceStore.isMobile);
|
const isOnMobile = $derived(deviceStore.isMobile);
|
||||||
const alwaysShowOnDesktop = $derived(settingsStore.config.alwaysShowSidebarOnDesktop as boolean);
|
const alwaysShowOnDesktop = $derived(settingsStore.config.alwaysShowSidebarOnDesktop as boolean);
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (alwaysShowOnDesktop && !isOnMobile) {
|
if (alwaysShowOnDesktop && !isOnMobile) {
|
||||||
isExpandedMode = true;
|
uiStore.isSidebarExpanded = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function toggleExpandedMode() {
|
function toggleExpandedMode() {
|
||||||
isExpandedMode = !isExpandedMode;
|
uiStore.isSidebarExpanded = !uiStore.isSidebarExpanded;
|
||||||
|
|
||||||
if (!isExpandedMode) {
|
if (!uiStore.isSidebarExpanded) {
|
||||||
hoveredTooltip = null;
|
hoveredTooltip = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (!isExpandedMode) {
|
if (!uiStore.isSidebarExpanded) {
|
||||||
isSearchModeActive = false;
|
isSearchModeActive = false;
|
||||||
searchQuery = '';
|
searchQuery = '';
|
||||||
|
|
||||||
@@ -66,7 +65,7 @@
|
|||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (deviceStore.isMobile && page.url.hash.includes(ROUTES.SEARCH)) {
|
if (deviceStore.isMobile && page.url.hash.includes(ROUTES.SEARCH)) {
|
||||||
isExpandedMode = false;
|
uiStore.isSidebarExpanded = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -294,7 +293,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
pendingCollapse = setTimeout(() => {
|
pendingCollapse = setTimeout(() => {
|
||||||
isExpandedMode = false;
|
uiStore.isSidebarExpanded = false;
|
||||||
pendingCollapse = null;
|
pendingCollapse = null;
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
@@ -314,7 +313,7 @@
|
|||||||
class={[
|
class={[
|
||||||
'fixed md:sticky top-2 left-2 md:left-0 md:ml-2 md:mt-2 pt-2 z-10 w-[calc(100dvw-1rem)]',
|
'fixed md:sticky top-2 left-2 md:left-0 md:ml-2 md:mt-2 pt-2 z-10 w-[calc(100dvw-1rem)]',
|
||||||
'md:h-[calc(100dvh-1.125rem)]',
|
'md:h-[calc(100dvh-1.125rem)]',
|
||||||
isExpandedMode &&
|
uiStore.isSidebarExpanded &&
|
||||||
(deviceStore.isStandalone
|
(deviceStore.isStandalone
|
||||||
? 'h-[calc(100dvh-2rem)]'
|
? 'h-[calc(100dvh-2rem)]'
|
||||||
: deviceStore.isIOSDevice
|
: deviceStore.isIOSDevice
|
||||||
@@ -323,9 +322,9 @@
|
|||||||
'rounded-3xl md:rounded-2xl',
|
'rounded-3xl md:rounded-2xl',
|
||||||
'flex flex-col justify-between',
|
'flex flex-col justify-between',
|
||||||
'md:transition-[width,padding] duration-200 ease-out',
|
'md:transition-[width,padding] duration-200 ease-out',
|
||||||
isStripExpanded && 'md:w-72 md:bg-muted/60 md:backdrop-blur-xl border-border shadow-md',
|
isStripExpanded && 'md:w-72 md:bg-muted/60 md:backdrop-blur-xl shadow-md',
|
||||||
!isStripExpanded && 'md:w-12',
|
!isStripExpanded && 'md:w-12',
|
||||||
isExpandedMode && 'is-expanded'
|
uiStore.isSidebarExpanded && 'is-expanded'
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<div class="px-2 flex items-center justify-between">
|
<div class="px-2 flex items-center justify-between">
|
||||||
@@ -337,24 +336,26 @@
|
|||||||
onmouseleave={() => (logoHovered = false)}
|
onmouseleave={() => (logoHovered = false)}
|
||||||
>
|
>
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
icon={!isExpandedMode && logoHovered && innerWidth > 768 ? PanelLeftOpen : Logo}
|
icon={!uiStore.isSidebarExpanded && logoHovered && innerWidth > 768
|
||||||
|
? PanelLeftOpen
|
||||||
|
: Logo}
|
||||||
size="lg"
|
size="lg"
|
||||||
iconSize="h-4.5 w-4.5 md:h-4 md:w-4"
|
iconSize="h-4.5 w-4.5 md:h-4 md:w-4"
|
||||||
class="{isExpandedMode
|
class="{uiStore.isSidebarExpanded
|
||||||
? 'bg-muted! md:bg-foreground/5!'
|
? 'bg-muted! md:bg-foreground/5!'
|
||||||
: 'bg-transparent!'} md:h-9 md:w-9 h-10 w-10 rounded-full md:hover:bg-foreground/10! pointer-events-auto"
|
: 'bg-transparent'} md:h-9 md:w-9 h-10 w-10 rounded-full md:hover:bg-foreground/10! pointer-events-auto"
|
||||||
href={isExpandedMode ? ROUTES.START : undefined}
|
href={uiStore.isSidebarExpanded ? ROUTES.START : undefined}
|
||||||
onclick={isExpandedMode ? undefined : toggleExpandedMode}
|
onclick={uiStore.isSidebarExpanded ? undefined : toggleExpandedMode}
|
||||||
tooltip={isExpandedMode ? undefined : 'Open Sidebar'}
|
tooltip={uiStore.isSidebarExpanded ? undefined : 'Open Sidebar'}
|
||||||
tooltipSide={TooltipSide.RIGHT}
|
tooltipSide={TooltipSide.RIGHT}
|
||||||
ariaLabel={isExpandedMode ? 'Go to start' : 'Expand navigation'}
|
ariaLabel={uiStore.isSidebarExpanded ? 'Go to start' : 'Expand navigation'}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if isOnMobile || (isExpandedMode && !alwaysShowOnDesktop)}
|
{#if isOnMobile || (uiStore.isSidebarExpanded && !alwaysShowOnDesktop)}
|
||||||
<div
|
<div
|
||||||
class="flex items-center transition-all duration-150 ease-out {deviceStore.isMobile &&
|
class="flex items-center transition-all duration-150 ease-out {deviceStore.isMobile &&
|
||||||
!isExpandedMode
|
!uiStore.isSidebarExpanded
|
||||||
? 'opacity-0 h-0!'
|
? 'opacity-0 h-0!'
|
||||||
: ''}"
|
: ''}"
|
||||||
in:fade={{ delay: 50, duration: 150, easing: circIn }}
|
in:fade={{ delay: 50, duration: 150, easing: circIn }}
|
||||||
@@ -377,12 +378,12 @@
|
|||||||
<div
|
<div
|
||||||
class="mt-2 flex min-h-0 flex-1 flex-col gap-4 md:gap-1 {deviceStore.isMobile
|
class="mt-2 flex min-h-0 flex-1 flex-col gap-4 md:gap-1 {deviceStore.isMobile
|
||||||
? 'transition-[opacity,height] duration-200 ease-out'
|
? 'transition-[opacity,height] duration-200 ease-out'
|
||||||
: ''} {deviceStore.isMobile && !isExpandedMode ? 'opacity-0 !h-0' : ''}"
|
: ''} {deviceStore.isMobile && !uiStore.isSidebarExpanded ? 'opacity-0 !h-0' : ''}"
|
||||||
in:fade={{ duration: 200 }}
|
in:fade={{ duration: 200 }}
|
||||||
out:fade={{ duration: 200 }}
|
out:fade={{ duration: 200 }}
|
||||||
>
|
>
|
||||||
<SidebarNavigationActions
|
<SidebarNavigationActions
|
||||||
isExpandedMode={innerWidth > 768 ? isExpandedMode : true}
|
isExpandedMode={innerWidth > 768 ? uiStore.isSidebarExpanded : true}
|
||||||
class="px-2"
|
class="px-2"
|
||||||
bind:isSearchModeActive
|
bind:isSearchModeActive
|
||||||
bind:searchQuery
|
bind:searchQuery
|
||||||
@@ -391,7 +392,7 @@
|
|||||||
searchQuery = '';
|
searchQuery = '';
|
||||||
}}
|
}}
|
||||||
onSearchClick={() => {
|
onSearchClick={() => {
|
||||||
isExpandedMode = true;
|
uiStore.isSidebarExpanded = true;
|
||||||
isSearchModeActive = true;
|
isSearchModeActive = true;
|
||||||
}}
|
}}
|
||||||
onNewChat={() => {
|
onNewChat={() => {
|
||||||
@@ -401,7 +402,7 @@
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{#if isExpandedMode || isOnMobile}
|
{#if uiStore.isSidebarExpanded || isOnMobile}
|
||||||
<div class="flex min-h-0 flex-1 flex-col overflow-y-auto">
|
<div class="flex min-h-0 flex-1 flex-col overflow-y-auto">
|
||||||
<SidebarNavigationConversationList
|
<SidebarNavigationConversationList
|
||||||
class="px-2"
|
class="px-2"
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ export { modelsStore } from './models/index.svelte';
|
|||||||
// SERVER
|
// SERVER
|
||||||
export { serverStore } from './server.svelte';
|
export { serverStore } from './server.svelte';
|
||||||
|
|
||||||
|
// UI / LAYOUT
|
||||||
|
export { uiStore } from './ui.svelte';
|
||||||
|
|
||||||
// SETTINGS / UI PREFERENCES
|
// SETTINGS / UI PREFERENCES
|
||||||
export { settingsStore } from './settings/index.svelte';
|
export { settingsStore } from './settings/index.svelte';
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* uiStore - Shared UI/layout state
|
||||||
|
*
|
||||||
|
* Holds cross-component UI state that does not belong to a single component
|
||||||
|
* (e.g. the desktop sidebar's expanded/collapsed state, which the sidebar
|
||||||
|
* controls and the chat tab bar reacts to).
|
||||||
|
*/
|
||||||
|
|
||||||
|
class UiStore {
|
||||||
|
/** Whether the desktop sidebar is expanded (open). */
|
||||||
|
isSidebarExpanded = $state(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const uiStore = new UiStore();
|
||||||
Reference in New Issue
Block a user