mirror of
https://github.com/open-webui/open-webui.git
synced 2025-12-12 12:25:20 +00:00
feat: resizable sidebar
Co-Authored-By: ALiNew <42788336+sukjinkim@users.noreply.github.com>
This commit is contained in:
parent
b9676cf36f
commit
b364cf43d3
12 changed files with 87 additions and 12 deletions
|
|
@ -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"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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" />
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue