mirror of
https://github.com/open-webui/open-webui
synced 2025-06-26 18:26:48 +00:00
Merge branch 'dev' into fix/share-chat-reactive-loop
This commit is contained in:
@@ -38,7 +38,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col mt-0.5 w-full">
|
||||
<div class="flex flex-col w-full items-center md:items-start">
|
||||
{#each selectedModels as selectedModel, selectedModelIdx}
|
||||
<div class="flex w-full max-w-fit">
|
||||
<div class="overflow-hidden w-full">
|
||||
@@ -109,7 +109,7 @@
|
||||
</div>
|
||||
|
||||
{#if showSetDefault}
|
||||
<div class="text-left mt-0.5 ml-1 text-[0.7rem] text-gray-500">
|
||||
<div class="hidden md:absolute text-left mt-0.5 ml-1 text-[0.7rem] text-gray-500">
|
||||
<button on:click={saveDefaultModel}> {$i18n.t('Set as default')}</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
import { cancelOllamaRequest, deleteModel, getOllamaVersion, pullModel } from '$lib/apis/ollama';
|
||||
|
||||
import { user, MODEL_DOWNLOAD_POOL, models } from '$lib/stores';
|
||||
import { user, MODEL_DOWNLOAD_POOL, models, mobile } from '$lib/stores';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { capitalizeFirstLetter, getModels, splitStream } from '$lib/utils';
|
||||
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
||||
@@ -201,10 +201,11 @@
|
||||
<ChevronDown className=" self-center ml-2 size-3" strokeWidth="2.5" />
|
||||
</div>
|
||||
</DropdownMenu.Trigger>
|
||||
|
||||
<DropdownMenu.Content
|
||||
class=" z-40 {className} max-w-[calc(100vw-1rem)] justify-start rounded-lg bg-white dark:bg-gray-900 dark:text-white shadow-lg border border-gray-300/30 dark:border-gray-700/50 outline-none "
|
||||
class=" z-40 {className} max-w-[calc(100vw-1rem)] justify-start rounded-xl bg-white dark:bg-gray-850 dark:text-white shadow-lg border border-gray-300/30 dark:border-gray-700/50 outline-none "
|
||||
transition={flyAndScale}
|
||||
side={'bottom-start'}
|
||||
side={$mobile ? 'bottom' : 'bottom-start'}
|
||||
sideOffset={4}
|
||||
>
|
||||
<slot>
|
||||
@@ -228,7 +229,7 @@
|
||||
{#each filteredItems as item}
|
||||
<button
|
||||
aria-label="model-item"
|
||||
class="flex w-full text-left font-medium line-clamp-1 select-none items-center rounded-button py-2 pl-3 pr-1.5 text-sm text-gray-700 dark:text-gray-100 outline-none transition-all duration-75 hover:bg-gray-100 dark:hover:bg-gray-850 rounded-lg cursor-pointer data-[highlighted]:bg-muted"
|
||||
class="flex w-full text-left font-medium line-clamp-1 select-none items-center rounded-button py-2 pl-3 pr-1.5 text-sm text-gray-700 dark:text-gray-100 outline-none transition-all duration-75 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg cursor-pointer data-[highlighted]:bg-muted"
|
||||
on:click={() => {
|
||||
value = item.value;
|
||||
|
||||
@@ -312,7 +313,7 @@
|
||||
|
||||
{#if !(searchValue.trim() in $MODEL_DOWNLOAD_POOL) && searchValue && ollamaVersion && $user.role === 'admin'}
|
||||
<button
|
||||
class="flex w-full font-medium line-clamp-1 select-none items-center rounded-button py-2 pl-3 pr-1.5 text-sm text-gray-700 dark:text-gray-100 outline-none transition-all duration-75 hover:bg-gray-100 dark:hover:bg-gray-850 rounded-lg cursor-pointer data-[highlighted]:bg-muted"
|
||||
class="flex w-full font-medium line-clamp-1 select-none items-center rounded-button py-2 pl-3 pr-1.5 text-sm text-gray-700 dark:text-gray-100 outline-none transition-all duration-75 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg cursor-pointer data-[highlighted]:bg-muted"
|
||||
on:click={() => {
|
||||
pullModelHandler();
|
||||
}}
|
||||
|
||||
@@ -2,7 +2,16 @@
|
||||
import { getContext } from 'svelte';
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
||||
import { WEBUI_NAME, chatId, modelfiles, settings, showSettings } from '$lib/stores';
|
||||
import {
|
||||
WEBUI_NAME,
|
||||
chatId,
|
||||
mobile,
|
||||
modelfiles,
|
||||
settings,
|
||||
showSettings,
|
||||
showSidebar,
|
||||
user
|
||||
} from '$lib/stores';
|
||||
|
||||
import { slide } from 'svelte/transition';
|
||||
import ShareChatModal from '../chat/ShareChatModal.svelte';
|
||||
@@ -10,6 +19,7 @@
|
||||
import Tooltip from '../common/Tooltip.svelte';
|
||||
import Menu from './Navbar/Menu.svelte';
|
||||
import { page } from '$app/stores';
|
||||
import UserMenu from './Sidebar/UserMenu.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
@@ -28,8 +38,34 @@
|
||||
|
||||
<ShareChatModal bind:show={showShareChatModal} chatId={$chatId} />
|
||||
<nav id="nav" class=" sticky py-2.5 top-0 flex flex-row justify-center z-30">
|
||||
<div class=" flex max-w-full w-full mx-auto px-5 pt-0.5 md:px-[1.3rem]">
|
||||
<div class=" flex max-w-full w-full mx-auto px-5 pt-0.5 md:px-[1rem]">
|
||||
<div class="flex items-center w-full max-w-full">
|
||||
<div class="{$showSidebar ? 'md:hidden' : ''} mr-3 self-start flex flex-none items-center">
|
||||
<button
|
||||
id="sidebar-toggle-button"
|
||||
class="cursor-pointer px-2 py-2 flex rounded-xl hover:bg-gray-100 dark:hover:bg-gray-850 transition"
|
||||
on:click={() => {
|
||||
showSidebar.set(!$showSidebar);
|
||||
}}
|
||||
>
|
||||
<div class=" m-auto self-center">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="2"
|
||||
stroke="currentColor"
|
||||
class="size-5"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25H12"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex-1 overflow-hidden max-w-full">
|
||||
{#if showModelSelector}
|
||||
<ModelSelector bind:selectedModels showSetDefault={!shareEnabled} />
|
||||
@@ -37,12 +73,12 @@
|
||||
</div>
|
||||
|
||||
<div class="self-start flex flex-none items-center">
|
||||
<div class="md:hidden flex self-center w-[1px] h-5 mx-2 bg-gray-300 dark:bg-stone-700" />
|
||||
<!-- <div class="md:hidden flex self-center w-[1px] h-5 mx-2 bg-gray-300 dark:bg-stone-700" /> -->
|
||||
|
||||
{#if !shareEnabled}
|
||||
<Tooltip content={$i18n.t('Settings')}>
|
||||
<button
|
||||
class="cursor-pointer p-1.5 flex dark:hover:bg-gray-700 rounded-full transition"
|
||||
class="hidden md:flex cursor-pointer px-2 py-2 rounded-xl hover:bg-gray-100 dark:hover:bg-gray-850 transition"
|
||||
id="open-settings-button"
|
||||
on:click={async () => {
|
||||
await showSettings.set(!$showSettings);
|
||||
@@ -81,9 +117,9 @@
|
||||
}}
|
||||
>
|
||||
<button
|
||||
class="cursor-pointer p-1.5 flex dark:hover:bg-gray-700 rounded-full transition"
|
||||
class="hidden md:flex cursor-pointer px-2 py-2 rounded-xl hover:bg-gray-100 dark:hover:bg-gray-850 transition"
|
||||
id="chat-context-menu-button"
|
||||
>
|
||||
>
|
||||
<div class=" m-auto self-center">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -106,7 +142,9 @@
|
||||
<Tooltip content={$i18n.t('New Chat')}>
|
||||
<button
|
||||
id="new-chat-button"
|
||||
class=" cursor-pointer p-1.5 flex dark:hover:bg-gray-700 rounded-full transition"
|
||||
class=" flex {$showSidebar
|
||||
? 'md:hidden'
|
||||
: ''} cursor-pointer px-2 py-2 rounded-xl hover:bg-gray-100 dark:hover:bg-gray-850 transition"
|
||||
on:click={() => {
|
||||
initNewChat();
|
||||
}}
|
||||
@@ -128,6 +166,29 @@
|
||||
</div>
|
||||
</button>
|
||||
</Tooltip>
|
||||
|
||||
{#if !$mobile && $user !== undefined}
|
||||
<UserMenu
|
||||
role={$user.role}
|
||||
on:show={(e) => {
|
||||
if (e.detail === 'archived-chat') {
|
||||
// showArchivedChatsModal = true;
|
||||
}
|
||||
}}
|
||||
>
|
||||
<button
|
||||
class=" flex rounded-xl p-1.5 w-full hover:bg-gray-100 dark:hover:bg-gray-850 transition"
|
||||
>
|
||||
<div class=" self-center">
|
||||
<img
|
||||
src={$user.profile_image_url}
|
||||
class=" size-6 object-cover rounded-full"
|
||||
alt="User profile"
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
</UserMenu>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -76,14 +76,14 @@
|
||||
|
||||
<div slot="content">
|
||||
<DropdownMenu.Content
|
||||
class="w-full max-w-[200px] rounded-lg px-1 py-1.5 border border-gray-300/30 dark:border-gray-700/50 z-50 bg-white dark:bg-gray-900 dark:text-white shadow-lg"
|
||||
class="w-full max-w-[200px] rounded-xl px-1 py-1.5 border border-gray-300/30 dark:border-gray-700/50 z-50 bg-white dark:bg-gray-850 dark:text-white shadow-lg"
|
||||
sideOffset={8}
|
||||
side="bottom"
|
||||
align="end"
|
||||
transition={flyAndScale}
|
||||
>
|
||||
<DropdownMenu.Item
|
||||
class="flex gap-2 items-center px-3 py-2 text-sm cursor-pointer dark:hover:bg-gray-850 rounded-md"
|
||||
class="flex gap-2 items-center px-3 py-2 text-sm cursor-pointer dark:hover:bg-gray-800 rounded-md"
|
||||
on:click={async () => {
|
||||
await showSettings.set(!$showSettings);
|
||||
}}
|
||||
@@ -112,7 +112,7 @@
|
||||
|
||||
{#if shareEnabled}
|
||||
<DropdownMenu.Item
|
||||
class="flex gap-2 items-center px-3 py-2 text-sm cursor-pointer dark:hover:bg-gray-850 rounded-md"
|
||||
class="flex gap-2 items-center px-3 py-2 text-sm cursor-pointer dark:hover:bg-gray-800 rounded-md"
|
||||
id="chat-share-button"
|
||||
on:click={() => {
|
||||
shareHandler();
|
||||
@@ -141,7 +141,7 @@
|
||||
/> -->
|
||||
<DropdownMenu.Sub>
|
||||
<DropdownMenu.SubTrigger
|
||||
class="flex gap-2 items-center px-3 py-2 text-sm cursor-pointer dark:hover:bg-gray-850 rounded-md"
|
||||
class="flex gap-2 items-center px-3 py-2 text-sm cursor-pointer dark:hover:bg-gray-800 rounded-md"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -161,12 +161,12 @@
|
||||
<div class="flex items-center">{$i18n.t('Download')}</div>
|
||||
</DropdownMenu.SubTrigger>
|
||||
<DropdownMenu.SubContent
|
||||
class="w-full rounded-lg px-1 py-1.5 border border-gray-300/30 dark:border-gray-700/50 z-50 bg-white dark:bg-gray-900 dark:text-white shadow-lg"
|
||||
class="w-full rounded-lg px-1 py-1.5 border border-gray-300/30 dark:border-gray-700/50 z-50 bg-white dark:bg-gray-850 dark:text-white shadow-lg"
|
||||
transition={flyAndScale}
|
||||
sideOffset={8}
|
||||
>
|
||||
<DropdownMenu.Item
|
||||
class="flex gap-2 items-center px-3 py-2 text-sm cursor-pointer dark:hover:bg-gray-850 rounded-md"
|
||||
class="flex gap-2 items-center px-3 py-2 text-sm cursor-pointer dark:hover:bg-gray-800 rounded-md"
|
||||
on:click={() => {
|
||||
downloadTxt();
|
||||
}}
|
||||
@@ -175,7 +175,7 @@
|
||||
</DropdownMenu.Item>
|
||||
|
||||
<DropdownMenu.Item
|
||||
class="flex gap-2 items-center px-3 py-2 text-sm cursor-pointer dark:hover:bg-gray-850 rounded-md"
|
||||
class="flex gap-2 items-center px-3 py-2 text-sm cursor-pointer dark:hover:bg-gray-800 rounded-md"
|
||||
on:click={() => {
|
||||
downloadPdf();
|
||||
}}
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { user, chats, settings, showSettings, chatId, tags, showSidebar } from '$lib/stores';
|
||||
import {
|
||||
user,
|
||||
chats,
|
||||
settings,
|
||||
showSettings,
|
||||
chatId,
|
||||
tags,
|
||||
showSidebar,
|
||||
mobile
|
||||
} from '$lib/stores';
|
||||
import { onMount, getContext } from 'svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
@@ -183,6 +192,17 @@
|
||||
}}
|
||||
/>
|
||||
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
|
||||
{#if $showSidebar}
|
||||
<div
|
||||
class=" fixed md:hidden z-40 top-0 right-0 left-0 bottom-0 bg-black/60 w-full min-h-screen h-screen flex justify-center overflow-hidden overscroll-contain"
|
||||
on:mousedown={() => {
|
||||
showSidebar.set(!$showSidebar);
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<div
|
||||
bind:this={navElement}
|
||||
id="sidebar"
|
||||
@@ -193,14 +213,37 @@
|
||||
data-state={$showSidebar}
|
||||
>
|
||||
<div
|
||||
class="py-2.5 my-auto flex flex-col justify-between h-screen max-h-[100dvh] w-[260px] {$showSidebar
|
||||
class="py-2.5 my-auto flex flex-col justify-between h-screen max-h-[100dvh] w-[260px] z-50 {$showSidebar
|
||||
? ''
|
||||
: 'invisible'}"
|
||||
>
|
||||
<div class="px-2 flex justify-center space-x-2">
|
||||
<div class="px-2 flex justify-between space-x-2">
|
||||
<button
|
||||
class=" cursor-pointer px-2 py-2 flex rounded-xl hover:bg-gray-100 dark:hover:bg-gray-850 transition"
|
||||
on:click={() => {
|
||||
showSidebar.set(!$showSidebar);
|
||||
}}
|
||||
>
|
||||
<div class=" m-auto self-center">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="2"
|
||||
stroke="currentColor"
|
||||
class="size-5"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25H12"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
<a
|
||||
id="sidebar-new-chat-button"
|
||||
class="flex-grow flex justify-between rounded-xl px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-900 transition"
|
||||
class="flex justify-between rounded-xl px-2 py-2 hover:bg-gray-100 dark:hover:bg-gray-850 transition"
|
||||
href="/"
|
||||
on:click={async () => {
|
||||
selectedChatId = null;
|
||||
@@ -212,24 +255,12 @@
|
||||
}, 0);
|
||||
}}
|
||||
>
|
||||
<div class="flex self-center">
|
||||
<div class="self-center mr-1.5">
|
||||
<img
|
||||
src="{WEBUI_BASE_URL}/static/favicon.png"
|
||||
class=" size-6 -translate-x-1.5 rounded-full"
|
||||
alt="logo"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class=" self-center font-medium text-sm">{$i18n.t('New Chat')}</div>
|
||||
</div>
|
||||
|
||||
<div class="self-center">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
class="size-5"
|
||||
>
|
||||
<path
|
||||
d="M5.433 13.917l1.262-3.155A4 4 0 017.58 9.42l6.92-6.918a2.121 2.121 0 013 3l-6.92 6.918c-.383.383-.84.685-1.343.886l-3.154 1.262a.5.5 0 01-.65-.65z"
|
||||
@@ -681,43 +712,45 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-2.5">
|
||||
<!-- <hr class=" border-gray-900 mb-1 w-full" /> -->
|
||||
{#if $mobile}
|
||||
<div class="px-2.5">
|
||||
<!-- <hr class=" border-gray-900 mb-1 w-full" /> -->
|
||||
|
||||
<div class="flex flex-col">
|
||||
{#if $user !== undefined}
|
||||
<UserMenu
|
||||
role={$user.role}
|
||||
on:show={(e) => {
|
||||
if (e.detail === 'archived-chat') {
|
||||
showArchivedChatsModal = true;
|
||||
}
|
||||
}}
|
||||
>
|
||||
<button
|
||||
class=" flex rounded-xl py-3 px-3.5 w-full hover:bg-gray-100 dark:hover:bg-gray-900 transition"
|
||||
on:click={() => {
|
||||
showDropdown = !showDropdown;
|
||||
<div class="flex flex-col">
|
||||
{#if $user !== undefined}
|
||||
<UserMenu
|
||||
role={$user.role}
|
||||
on:show={(e) => {
|
||||
if (e.detail === 'archived-chat') {
|
||||
showArchivedChatsModal = true;
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div class=" self-center mr-3">
|
||||
<img
|
||||
src={$user.profile_image_url}
|
||||
class=" max-w-[30px] object-cover rounded-full"
|
||||
alt="User profile"
|
||||
/>
|
||||
</div>
|
||||
<div class=" self-center font-semibold">{$user.name}</div>
|
||||
</button>
|
||||
</UserMenu>
|
||||
{/if}
|
||||
<button
|
||||
class=" flex rounded-xl py-3 px-3.5 w-full hover:bg-gray-100 dark:hover:bg-gray-900 transition"
|
||||
on:click={() => {
|
||||
showDropdown = !showDropdown;
|
||||
}}
|
||||
>
|
||||
<div class=" self-center mr-3">
|
||||
<img
|
||||
src={$user.profile_image_url}
|
||||
class=" max-w-[30px] object-cover rounded-full"
|
||||
alt="User profile"
|
||||
/>
|
||||
</div>
|
||||
<div class=" self-center font-semibold">{$user.name}</div>
|
||||
</button>
|
||||
</UserMenu>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div
|
||||
id="sidebar-handle"
|
||||
class="fixed left-0 top-[50dvh] -translate-y-1/2 transition-transform translate-x-[255px] md:translate-x-[260px] rotate-0"
|
||||
class=" hidden md:fixed left-0 top-[50dvh] -translate-y-1/2 transition-transform translate-x-[255px] md:translate-x-[260px] rotate-0"
|
||||
>
|
||||
<Tooltip
|
||||
placement="right"
|
||||
|
||||
@@ -36,14 +36,14 @@
|
||||
|
||||
<div slot="content">
|
||||
<DropdownMenu.Content
|
||||
class="w-full max-w-[180px] rounded-lg px-1 py-1.5 border border-gray-300/30 dark:border-gray-700/50 z-50 bg-white dark:bg-gray-900 dark:text-white shadow"
|
||||
class="w-full max-w-[180px] rounded-xl px-1 py-1.5 border border-gray-300/30 dark:border-gray-700/50 z-50 bg-white dark:bg-gray-850 dark:text-white shadow"
|
||||
sideOffset={-2}
|
||||
side="bottom"
|
||||
align="start"
|
||||
transition={flyAndScale}
|
||||
>
|
||||
<DropdownMenu.Item
|
||||
class="flex gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer dark:hover:bg-gray-850 rounded-md"
|
||||
class="flex gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer dark:hover:bg-gray-800 rounded-md"
|
||||
on:click={() => {
|
||||
shareHandler();
|
||||
}}
|
||||
@@ -53,7 +53,7 @@
|
||||
</DropdownMenu.Item>
|
||||
|
||||
<DropdownMenu.Item
|
||||
class="flex gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer dark:hover:bg-gray-850 rounded-md"
|
||||
class="flex gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer dark:hover:bg-gray-800 rounded-md"
|
||||
on:click={() => {
|
||||
renameHandler();
|
||||
}}
|
||||
@@ -63,7 +63,7 @@
|
||||
</DropdownMenu.Item>
|
||||
|
||||
<DropdownMenu.Item
|
||||
class="flex gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer dark:hover:bg-gray-850 rounded-md"
|
||||
class="flex gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer dark:hover:bg-gray-800 rounded-md"
|
||||
on:click={() => {
|
||||
deleteHandler();
|
||||
}}
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
<slot name="content">
|
||||
<DropdownMenu.Content
|
||||
class="w-full max-w-[240px] rounded-lg p-1 py-1 border border-gray-850 z-50 bg-gray-900 text-white text-sm"
|
||||
class="w-full max-w-[240px] rounded-lg p-1 py-1 border border-gray-300/30 dark:border-gray-700/50 z-50 bg-gray-850 text-white text-sm"
|
||||
sideOffset={8}
|
||||
side="bottom"
|
||||
align="start"
|
||||
|
||||
Reference in New Issue
Block a user