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 <div
class="h-screen max-h-[100dvh] transition-width duration-200 ease-in-out {$showSidebar 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" : ''} w-full max-w-full flex flex-col"
id="channel-container" id="channel-container"
> >

View file

@ -2384,7 +2384,7 @@
<div <div
class="h-screen max-h-[100dvh] transition-width duration-200 ease-in-out {$showSidebar 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" : ' '} w-full max-w-full flex flex-col"
id="chat-container" id="chat-container"
> >

View file

@ -18,7 +18,7 @@
<div <div
bind:this={overlayElement} bind:this={overlayElement}
class="fixed {$showSidebar 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" : 'left-0'} fixed top-0 right-0 bottom-0 w-full h-full flex z-9999 touch-none pointer-events-none"
id="dropzone" id="dropzone"
role="region" role="region"

View file

@ -25,7 +25,8 @@
isApp, isApp,
models, models,
selectedFolder, selectedFolder,
WEBUI_NAME WEBUI_NAME,
sidebarWidth
} from '$lib/stores'; } from '$lib/stores';
import { onMount, getContext, tick, onDestroy } from 'svelte'; import { onMount, getContext, tick, onDestroy } from 'svelte';
@ -371,8 +372,56 @@
selectedChatId = null; 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 = []; let unsubscribers = [];
onMount(async () => { 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); await showSidebar.set(!$mobile ? localStorage.sidebar === 'true' : false);
unsubscribers = [ unsubscribers = [
@ -570,6 +619,17 @@
}} }}
/> />
<svelte:window
on:mousemove={(e) => {
if (!isResizing) return;
endX = e.clientX;
applyResize();
}}
on:mouseup={() => {
resizeEndHandler();
}}
/>
{#if !$mobile && !$showSidebar} {#if !$mobile && !$showSidebar}
<div <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" 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} data-state={$showSidebar}
> >
<div <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'}" : 'invisible'}"
> >
@ -1321,4 +1381,17 @@
</div> </div>
</div> </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} {/if}

View file

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

View file

@ -383,7 +383,7 @@
{:else} {:else}
<div <div
class="w-full flex-1 h-full flex items-center justify-center {$showSidebar 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" /> <Spinner className="size-5" />

View file

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

View file

@ -18,7 +18,7 @@
<div <div
class=" flex flex-col w-full h-screen max-h-[100dvh] transition-width duration-200 ease-in-out {$showSidebar 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" : ''} max-w-full"
> >
<nav class=" px-2.5 pt-1.5 backdrop-blur-xl w-full drag-region"> <nav class=" px-2.5 pt-1.5 backdrop-blur-xl w-full drag-region">

View file

@ -41,7 +41,7 @@
{#if loaded} {#if loaded}
<div <div
class=" flex flex-col w-full h-screen max-h-[100dvh] transition-width duration-200 ease-in-out {$showSidebar 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" : ''} max-w-full"
> >
<nav class=" px-2 pt-1.5 backdrop-blur-xl w-full drag-region"> <nav class=" px-2 pt-1.5 backdrop-blur-xl w-full drag-region">

View file

@ -20,7 +20,7 @@
{#if loaded} {#if loaded}
<div <div
id="note-container" 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} /> <NoteEditor id={$page.params.id} />
</div> </div>

View file

@ -18,7 +18,7 @@
<div <div
class=" flex flex-col w-full h-screen max-h-[100dvh] transition-width duration-200 ease-in-out {$showSidebar 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" : ''} max-w-full"
> >
<nav class=" px-2.5 pt-1.5 backdrop-blur-xl w-full drag-region"> <nav class=" px-2.5 pt-1.5 backdrop-blur-xl w-full drag-region">

View file

@ -52,7 +52,7 @@
{#if loaded} {#if loaded}
<div <div
class=" relative flex flex-col w-full h-screen max-h-[100dvh] transition-width duration-200 ease-in-out {$showSidebar 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" : ''} max-w-full"
> >
<nav class=" px-2.5 pt-1.5 backdrop-blur-xl drag-region"> <nav class=" px-2.5 pt-1.5 backdrop-blur-xl drag-region">