feat: resizable sidebar

Co-Authored-By: ALiNew <42788336+sukjinkim@users.noreply.github.com>
This commit is contained in:
Timothy Jaeryang Baek 2025-12-10 23:54:36 -05:00
parent b9676cf36f
commit b364cf43d3
12 changed files with 87 additions and 12 deletions

View file

@ -290,7 +290,7 @@
<div
class="h-screen max-h-[100dvh] transition-width duration-200 ease-in-out {$showSidebar
? 'md:max-w-[calc(100%-260px)]'
? 'md:max-w-[calc(100%-var(--sidebar-width))]'
: ''} w-full max-w-full flex flex-col"
id="channel-container"
>

View file

@ -2384,7 +2384,7 @@
<div
class="h-screen max-h-[100dvh] transition-width duration-200 ease-in-out {$showSidebar
? ' md:max-w-[calc(100%-260px)]'
? ' md:max-w-[calc(100%-var(--sidebar-width))]'
: ' '} w-full max-w-full flex flex-col"
id="chat-container"
>

View file

@ -18,7 +18,7 @@
<div
bind:this={overlayElement}
class="fixed {$showSidebar
? 'left-0 md:left-[260px] md:w-[calc(100%-260px)]'
? 'left-0 md:left-[var(--sidebar-width)] md:w-[calc(100%-var(--sidebar-width))]'
: 'left-0'} fixed top-0 right-0 bottom-0 w-full h-full flex z-9999 touch-none pointer-events-none"
id="dropzone"
role="region"

View file

@ -25,7 +25,8 @@
isApp,
models,
selectedFolder,
WEBUI_NAME
WEBUI_NAME,
sidebarWidth
} from '$lib/stores';
import { onMount, getContext, tick, onDestroy } from 'svelte';
@ -371,8 +372,56 @@
selectedChatId = null;
};
const MIN_WIDTH = 220;
const MAX_WIDTH = 480;
let isResizing = false;
let startWidth = 0;
let startX = 0;
let endX = 0;
const resizeStartHandler = (e: MouseEvent) => {
if ($mobile) return;
isResizing = true;
startX = e.clientX;
startWidth = $sidebarWidth ?? 260;
document.body.style.userSelect = 'none';
};
const resizeEndHandler = () => {
if (!isResizing) return;
isResizing = false;
document.body.style.userSelect = '';
localStorage.setItem('sidebarWidth', String($sidebarWidth));
};
const applyResize = () => {
const dx = endX - startX;
const newSidebarWidth = Math.min(MAX_WIDTH, Math.max(MIN_WIDTH, startWidth + dx));
sidebarWidth.set(newSidebarWidth);
document.documentElement.style.setProperty('--sidebar-width', `${newSidebarWidth}px`);
};
let unsubscribers = [];
onMount(async () => {
try {
const width = Number(localStorage.getItem('sidebarWidth'));
if (!Number.isNaN(width) && width >= MIN_WIDTH && width <= MAX_WIDTH) {
sidebarWidth.set(width);
}
} catch {}
document.documentElement.style.setProperty('--sidebar-width', `${$sidebarWidth}px`);
sidebarWidth.subscribe((w) => {
document.documentElement.style.setProperty('--sidebar-width', `${w}px`);
});
await showSidebar.set(!$mobile ? localStorage.sidebar === 'true' : false);
unsubscribers = [
@ -570,6 +619,17 @@
}}
/>
<svelte:window
on:mousemove={(e) => {
if (!isResizing) return;
endX = e.clientX;
applyResize();
}}
on:mouseup={() => {
resizeEndHandler();
}}
/>
{#if !$mobile && !$showSidebar}
<div
class=" pt-[7px] pb-2 px-2 flex flex-col justify-between text-black dark:text-white hover:bg-gray-50/30 dark:hover:bg-gray-950/30 h-full z-10 transition-all border-e-[0.5px] border-gray-50 dark:border-gray-850/30"
@ -775,7 +835,7 @@
data-state={$showSidebar}
>
<div
class=" my-auto flex flex-col justify-between h-screen max-h-[100dvh] w-[260px] overflow-x-hidden scrollbar-hidden z-50 {$showSidebar
class=" my-auto flex flex-col justify-between h-screen max-h-[100dvh] w-[var(--sidebar-width)] overflow-x-hidden scrollbar-hidden z-50 {$showSidebar
? ''
: 'invisible'}"
>
@ -1321,4 +1381,17 @@
</div>
</div>
</div>
{#if !$mobile}
<div
class="relative flex items-center justify-center group border-l border-gray-50 dark:border-gray-850/30 hover:border-gray-200 dark:hover:border-gray-800 transition z-20"
id="sidebar-resizer"
on:mousedown={resizeStartHandler}
role="separator"
>
<div
class=" absolute -left-1.5 -right-1.5 -top-0 -bottom-0 z-20 cursor-col-resize bg-transparent"
/>
</div>
{/if}
{/if}

View file

@ -75,6 +75,8 @@ export const settings: Writable<Settings> = writable({});
export const audioQueue = writable(null);
export const sidebarWidth = writable(260);
export const showSidebar = writable(false);
export const showSearch = writable(false);
export const showSettings = writable(false);

View file

@ -383,7 +383,7 @@
{:else}
<div
class="w-full flex-1 h-full flex items-center justify-center {$showSidebar
? ' md:max-w-[calc(100%-260px)]'
? ' md:max-w-[calc(100%-var(--sidebar-width))]'
: ' '}"
>
<Spinner className="size-5" />

View file

@ -29,7 +29,7 @@
{#if loaded}
<div
class=" flex flex-col h-screen max-h-[100dvh] flex-1 transition-width duration-200 ease-in-out {$showSidebar
? 'md:max-w-[calc(100%-260px)]'
? 'md:max-w-[calc(100%-var(--sidebar-width))]'
: ' md:max-w-[calc(100%-49px)]'} w-full max-w-full"
>
<nav class=" px-2.5 pt-1.5 backdrop-blur-xl drag-region">

View file

@ -18,7 +18,7 @@
<div
class=" flex flex-col w-full h-screen max-h-[100dvh] transition-width duration-200 ease-in-out {$showSidebar
? 'md:max-w-[calc(100%-260px)]'
? 'md:max-w-[calc(100%-var(--sidebar-width))]'
: ''} max-w-full"
>
<nav class=" px-2.5 pt-1.5 backdrop-blur-xl w-full drag-region">

View file

@ -41,7 +41,7 @@
{#if loaded}
<div
class=" flex flex-col w-full h-screen max-h-[100dvh] transition-width duration-200 ease-in-out {$showSidebar
? 'md:max-w-[calc(100%-260px)]'
? 'md:max-w-[calc(100%-var(--sidebar-width))]'
: ''} max-w-full"
>
<nav class=" px-2 pt-1.5 backdrop-blur-xl w-full drag-region">

View file

@ -20,7 +20,7 @@
{#if loaded}
<div
id="note-container"
class="w-full h-full {$showSidebar ? 'md:max-w-[calc(100%-260px)]' : ''}"
class="w-full h-full {$showSidebar ? 'md:max-w-[calc(100%-var(--sidebar-width))]' : ''}"
>
<NoteEditor id={$page.params.id} />
</div>

View file

@ -18,7 +18,7 @@
<div
class=" flex flex-col w-full h-screen max-h-[100dvh] transition-width duration-200 ease-in-out {$showSidebar
? 'md:max-w-[calc(100%-260px)]'
? 'md:max-w-[calc(100%-var(--sidebar-width))]'
: ''} max-w-full"
>
<nav class=" px-2.5 pt-1.5 backdrop-blur-xl w-full drag-region">

View file

@ -52,7 +52,7 @@
{#if loaded}
<div
class=" relative flex flex-col w-full h-screen max-h-[100dvh] transition-width duration-200 ease-in-out {$showSidebar
? 'md:max-w-[calc(100%-260px)]'
? 'md:max-w-[calc(100%-var(--sidebar-width))]'
: ''} max-w-full"
>
<nav class=" px-2.5 pt-1.5 backdrop-blur-xl drag-region">