This commit is contained in:
Timothy Jaeryang Baek
2026-07-15 01:22:51 -04:00
parent 43e792a8f4
commit c64fe45376
30 changed files with 100 additions and 63 deletions
-8
View File
@@ -307,8 +307,6 @@
}, 100);
};
$: selectedTabTitle = allSettings.find((tab) => tab.id === selectedTab)?.title ?? 'Settings';
const tabButtonClass = (active) =>
`flex items-center gap-1.5 h-7 px-2 lg:w-full shrink-0 rounded-lg text-xs text-left transition-colors duration-75 select-none ${
active
@@ -394,12 +392,6 @@
<div class="flex-1 min-h-0 p-4 lg:px-5 flex flex-col">
<div class="w-full h-full min-h-0 flex flex-col">
{#if selectedTab !== 'models'}
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">
{$i18n.t(selectedTabTitle)}
</h2>
{/if}
<div class="flex-1 min-h-0 overflow-hidden">
{#if selectedTab === 'general'}
<General
@@ -233,6 +233,8 @@
dispatch('save');
}}
>
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('Audio')}</h2>
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
<div class="flex flex-col gap-3">
<div>
@@ -118,6 +118,10 @@
</script>
<form class="flex h-full flex-col justify-between text-sm" on:submit|preventDefault={submitHandler}>
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">
{$i18n.t('Authentication')}
</h2>
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
{#if adminConfig !== null}
<AdminSettingSection title={$i18n.t('User Access')} first>
@@ -47,6 +47,10 @@
saveHandler();
}}
>
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">
{$i18n.t('Code Execution')}
</h2>
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
{#if config}
<AdminSettingSection first>
@@ -218,6 +218,8 @@
/>
<form class="flex h-full flex-col justify-between text-sm" on:submit|preventDefault={submitHandler}>
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('Connections')}</h2>
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
{#if ENABLE_OPENAI_API !== null && ENABLE_OLLAMA_API !== null && connectionsConfig !== null}
<AdminSettingSection first>
@@ -49,6 +49,8 @@
</script>
<div class="flex flex-col h-full justify-between text-sm">
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('Database')}</h2>
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
<input
id="config-json-input"
@@ -374,6 +374,8 @@
submitHandler();
}}
>
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('Documents')}</h2>
{#if RAGConfig}
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
<div class="">
@@ -104,6 +104,8 @@
dispatch('save');
}}
>
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('Evaluations')}</h2>
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
{#if evaluationConfig !== null}
<AdminSettingSection first>
@@ -84,6 +84,8 @@
updateHandler();
}}
>
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('General')}</h2>
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
{#if adminConfig !== null}
<AdminSettingSection first>
@@ -289,6 +289,8 @@
saveHandler();
}}
>
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('Images')}</h2>
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
{#if config}
<div>
@@ -161,6 +161,8 @@
updateHandler();
}}
>
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('Integrations')}</h2>
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
{#if servers !== null}
<AdminSettingSection title={$i18n.t('Tools')} first>
@@ -108,6 +108,10 @@
dispatch('save');
}}
>
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">
{$i18n.t('Interface')}
</h2>
<div class=" flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
<div class="mb-5">
<div class=" mb-2 text-xs text-gray-400 dark:text-gray-600">
@@ -238,6 +238,8 @@
updateHandler();
}}
>
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('Pipelines')}</h2>
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
{#if PIPELINES_LIST !== null}
{#if PIPELINES_LIST.length > 0}
@@ -57,6 +57,8 @@
</script>
<form class="flex h-full flex-col justify-between text-sm" on:submit|preventDefault={save}>
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('Sub-agents')}</h2>
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
{#if loading}
<div class="flex justify-center py-8"><Spinner className="size-6" /></div>
@@ -152,6 +152,8 @@
saveHandler();
}}
>
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('Web Search')}</h2>
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
{#if webConfig}
<div class="">
+8 -20
View File
@@ -1,16 +1,10 @@
<script>
import { toast } from 'svelte-sonner';
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
dayjs.extend(relativeTime);
import { onMount, getContext } from 'svelte';
import { goto } from '$app/navigation';
import { WEBUI_NAME, config, user, showSidebar, knowledge } from '$lib/stores';
import { WEBUI_BASE_URL } from '$lib/constants';
import { user } from '$lib/stores';
import Tooltip from '$lib/components/common/Tooltip.svelte';
import Plus from '$lib/components/icons/Plus.svelte';
import UsersSolid from '$lib/components/icons/UsersSolid.svelte';
import ChevronRight from '$lib/components/icons/ChevronRight.svelte';
@@ -22,11 +16,7 @@
import Check from '$lib/components/icons/Check.svelte';
import Select from '$lib/components/common/Select.svelte';
import { createNewGroup, getGroups } from '$lib/apis/groups';
import {
getUserDefaultPermissions,
getAllUsers,
updateUserDefaultPermissions
} from '$lib/apis/users';
import { getUserDefaultPermissions, updateUserDefaultPermissions } from '$lib/apis/users';
const i18n = getContext('i18n');
@@ -146,16 +136,14 @@
</div>
</div>
<div
class="py-2 bg-white dark:bg-gray-900 rounded-3xl border border-gray-100/30 dark:border-gray-850/30"
>
<div class="flex items-center w-full space-x-2 py-0.5 px-3.5">
<div class="flex flex-1">
<div class="space-y-1">
<div class="flex h-8 flex-1 items-center w-full gap-2">
<div class="flex min-w-0 flex-1">
<div class="self-center ml-1 mr-3">
<Search className="size-3.5" />
</div>
<input
class="w-full text-sm py-1 rounded-r-xl outline-hidden bg-transparent"
class="w-full text-sm pr-4 py-1 rounded-r-xl outline-hidden bg-transparent"
bind:value={query}
aria-label={$i18n.t('Search Groups')}
placeholder={$i18n.t('Search Groups')}
@@ -203,7 +191,7 @@
</div>
{#if filteredGroups.length !== 0}
<div class="my-2 px-3 grid grid-cols-1 gap-1">
<div class="my-1 gap-x-2 gap-y-0.5 grid lg:grid-cols-2">
{#each filteredGroups as group}
<GroupItem {group} {setGroups} {defaultPermissions} />
{/each}
@@ -232,7 +220,7 @@
{/if}
<button
class="flex items-center justify-between rounded-lg w-full transition mt-4"
class="flex items-center justify-between rounded-2xl w-full transition mt-2 px-2.5 py-2 hover:bg-gray-50/70 dark:hover:bg-gray-850/50"
aria-haspopup="dialog"
on:click={() => {
showDefaultPermissionsModal = true;
@@ -63,32 +63,32 @@
/>
<button
class="flex space-x-4 cursor-pointer text-left w-full px-3.5 py-2.5 dark:hover:bg-gray-850/50 hover:bg-gray-50 transition rounded-2xl"
class="flex space-x-4 cursor-pointer text-left w-full px-2.5 py-1.5 rounded-2xl hover:bg-gray-50/70 dark:hover:bg-gray-850/50 transition"
on:click={() => {
showEdit = true;
}}
>
<div class="w-full">
<div class="flex items-center justify-between">
<div class="flex-1">
<div class="flex items-center gap-2">
<div class="flex-1 min-w-0 pl-1">
<div class="flex items-center gap-1.5">
<div class="text-sm font-normal line-clamp-1">{group.name}</div>
</div>
<div class="flex items-center gap-2 mt-0.5 line-clamp-1">
<div class="flex items-center gap-1.5 px-1 line-clamp-1">
<div class="text-xs text-gray-500 shrink-0">
{$i18n.t('{{COUNT}} members', { COUNT: group?.member_count ?? 0 })}
</div>
{#if group?.description}
<div class="text-xs text-gray-500 line-clamp-1">
<div class="text-xs overflow-hidden text-ellipsis line-clamp-1">
{group.description}
</div>
{/if}
</div>
</div>
<div class="flex self-center ml-2">
<div class="flex self-center ml-2 p-1.5 dark:text-gray-300">
<Pencil className="size-3.5" />
</div>
</div>
@@ -45,6 +45,8 @@
</script>
<div id="tab-about" class="flex flex-col h-full justify-between space-y-3 text-sm">
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('About')}</h2>
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5 space-y-3">
<div>
<div class="text-xs text-gray-400 dark:text-gray-600 mb-2.5 flex space-x-2 items-center">
@@ -174,6 +174,10 @@
/>
<div id="tab-archived-chats" class="flex flex-col h-full text-sm">
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">
{$i18n.t('Archived Chats')}
</h2>
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
<div>
<div class="text-xs text-gray-400 dark:text-gray-600 mb-2">
@@ -175,6 +175,8 @@
dispatch('save');
}}
>
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('Audio')}</h2>
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5 space-y-3">
<div>
<div class="text-xs text-gray-400 dark:text-gray-600 mb-2">
@@ -76,6 +76,8 @@
updateHandler();
}}
>
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('Connections')}</h2>
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
{#if config !== null}
<div class="">
@@ -133,6 +133,10 @@
/>
<div id="tab-chats" class="flex flex-col h-full text-sm">
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">
{$i18n.t('Data Controls')}
</h2>
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5 space-y-3">
<input
id="chat-import-input"
@@ -206,6 +206,8 @@
</script>
<div class="flex flex-col h-full justify-between text-sm" id="tab-general">
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('General')}</h2>
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
<div class="">
<div class="text-xs text-gray-400 dark:text-gray-600 mb-2">
@@ -80,6 +80,8 @@
updateHandler();
}}
>
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('Integrations')}</h2>
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
{#if servers !== null}
<div>
@@ -307,6 +307,8 @@
dispatch('save');
}}
>
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('Interface')}</h2>
<input
bind:this={filesInputElement}
bind:files={inputFiles}
@@ -29,6 +29,10 @@
dispatch('save');
}}
>
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">
{$i18n.t('Personalization')}
</h2>
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5 py-1">
<div>
<div class="flex items-center justify-between mb-1">
@@ -39,6 +39,10 @@
</script>
<div id="tab-shortcuts" class="flex h-full flex-col text-sm">
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">
{$i18n.t('Keyboard Shortcuts')}
</h2>
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
<div class="flex items-center gap-2 px-1 pb-1">
<span class="flex-1 text-[0.625rem] font-medium text-gray-400 dark:text-gray-600">
@@ -750,7 +750,6 @@
$: if ($user?.role !== 'admin' && isAdminTab(selectedTab)) {
selectedTab = 'general';
}
$: selectedTabTitle = allKnownSettings.find((tab) => tab.id === selectedTab)?.title ?? 'Settings';
onMount(() => {
availableSettings = getAvailableSettings();
@@ -1001,12 +1000,6 @@
</nav>
<div class="flex-1 min-h-0 p-4 md:px-5 flex flex-col">
{#if selectedTab !== 'account' && selectedTab !== 'admin:models'}
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">
{$i18n.t(selectedTabTitle)}
</h2>
{/if}
<div class="flex-1 min-h-0 overflow-hidden">
{#if selectedTab === 'general'}
<General
@@ -527,20 +527,20 @@
{#if !edit || (edit && model)}
<form
class="flex flex-col md:flex-row w-full gap-3 md:gap-6"
class="flex flex-col md:flex-row w-full gap-3"
on:submit|preventDefault={() => {
submitHandler();
}}
>
<div class="w-full px-1">
<div class="flex flex-row gap-4 md:gap-6 w-full">
<div class="self-start flex justify-center my-2 shrink-0">
<div class="self-center">
<div class="flex flex-row gap-3 w-full">
<div class="self-start flex justify-center py-1 shrink-0">
<div class="self-center group">
<button
class="rounded-2xl flex shrink-0 items-center {info.meta.profile_image_url !==
`${WEBUI_BASE_URL}/static/favicon.png`
class="relative flex size-14 md:size-16 shrink-0 items-center overflow-hidden rounded-xl {info
.meta.profile_image_url !== `${WEBUI_BASE_URL}/static/favicon.png`
? 'bg-transparent'
: 'bg-white'} shadow-xl group relative"
: 'bg-gray-50 dark:bg-gray-850'} ring-1 ring-gray-200/70 transition hover:ring-gray-300 dark:ring-white/10 dark:hover:ring-white/20"
type="button"
aria-label={$i18n.t('Upload profile image')}
on:click={() => {
@@ -551,26 +551,28 @@
<img
src={info.meta.profile_image_url}
alt="model profile"
class="rounded-xl size-20 md:size-48 object-cover shrink-0"
class="size-full object-cover"
/>
{:else}
<img
src="{WEBUI_BASE_URL}/static/favicon.png"
alt="model profile"
class=" rounded-xl size-20 md:size-48 object-cover shrink-0"
class="size-full object-cover"
/>
{/if}
<div class="absolute bottom-0 right-0 z-10">
<div class="m-1.5">
<div
class="absolute bottom-0 right-0 z-10 opacity-0 transition group-hover:opacity-100"
>
<div class="m-1">
<div
class="shadow-xl p-1 rounded-full border-2 border-white bg-gray-800 text-white group-hover:bg-gray-600 transition dark:border-black dark:bg-white dark:group-hover:bg-gray-200 dark:text-black"
class="rounded-full bg-gray-900 p-1 text-white shadow-sm transition dark:bg-white dark:text-black"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="size-5"
class="size-3"
>
<path
fill-rule="evenodd"
@@ -583,13 +585,13 @@
</div>
<div
class="absolute top-0 bottom-0 left-0 right-0 bg-white dark:bg-black rounded-lg opacity-0 group-hover:opacity-20 transition"
class="absolute inset-0 bg-white opacity-0 transition group-hover:opacity-20 dark:bg-black"
></div>
</button>
<div class="flex w-full mt-1 justify-end">
<div class="mt-1 flex w-full justify-center">
<button
class="px-2 py-1 text-gray-500 rounded-lg text-xs"
class="rounded-lg px-1.5 py-0.5 text-[0.6875rem] text-gray-400 transition hover:text-gray-600 dark:hover:text-gray-300"
on:click={() => {
info.meta.profile_image_url = `${WEBUI_BASE_URL}/static/favicon.png`;
}}
@@ -602,11 +604,11 @@
</div>
<div class="flex flex-col w-full flex-1">
<div class="flex justify-between items-start my-2">
<div class="flex justify-between items-start gap-3 py-1">
<div class=" flex flex-col w-full">
<div class="flex-1 w-full">
<input
class="text-3xl w-full bg-transparent outline-hidden"
class="w-full bg-transparent text-lg font-medium text-gray-900 outline-hidden placeholder:text-gray-300 dark:text-white dark:placeholder:text-gray-700 md:text-xl"
placeholder={$i18n.t('Model Name')}
bind:value={name}
required
@@ -616,7 +618,7 @@
<div class="flex-1 w-full">
<div>
<input
class="text-xs w-full bg-transparent outline-hidden"
class="w-full bg-transparent py-0.5 text-xs text-gray-500 outline-hidden placeholder:text-gray-300 dark:text-gray-500 dark:placeholder:text-gray-700"
placeholder={$i18n.t('Model ID')}
bind:value={id}
disabled={edit}
@@ -628,7 +630,7 @@
<div class="shrink-0">
<button
class="bg-gray-50 shrink-0 hover:bg-gray-100 text-black dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-white transition px-2 py-1 rounded-full flex gap-1 items-center"
class="flex h-7 shrink-0 items-center gap-1 rounded-lg bg-gray-50 px-2 text-xs text-gray-700 transition hover:bg-gray-100 dark:bg-white/[0.03] dark:text-gray-300 dark:hover:bg-white/[0.06]"
type="button"
on:click={() => {
showAccessControlModal = true;
@@ -636,7 +638,7 @@
>
<LockClosed strokeWidth="2.5" className="size-3.5 shrink-0" />
<div class="text-sm font-normal shrink-0">
<div class="font-normal shrink-0">
{$i18n.t('Access')}
</div>
</button>
+1 -1
View File
@@ -21,7 +21,7 @@
? '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 select-none">
<nav class=" px-2.5 backdrop-blur-xl w-full drag-region select-none">
<div class=" flex items-center">
{#if $mobile}
<div class="{$showSidebar ? 'md:hidden' : ''} flex flex-none items-center self-end">