Update General.svelte

This commit is contained in:
Classic298 2025-06-18 13:10:02 +02:00 committed by GitHub
parent 3814bbf7b3
commit b9ea6130c7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1,318 +1,318 @@
<script lang="ts"> <script lang="ts">
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';
import { createEventDispatcher, onMount, getContext } from 'svelte'; import { createEventDispatcher, onMount, getContext } from 'svelte';
import { getLanguages, changeLanguage } from '$lib/i18n'; import { getLanguages, changeLanguage } from '$lib/i18n';
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
import { models, settings, theme, user } from '$lib/stores'; import { models, settings, theme, user } from '$lib/stores';
const i18n = getContext('i18n'); const i18n = getContext('i18n');
import AdvancedParams from './Advanced/AdvancedParams.svelte'; import AdvancedParams from './Advanced/AdvancedParams.svelte';
import Textarea from '$lib/components/common/Textarea.svelte'; import Textarea from '$lib/components/common/Textarea.svelte';
export let saveSettings: Function; export let saveSettings: Function;
export let getModels: Function; export let getModels: Function;
// General // General
let themes = ['dark', 'light', 'oled-dark']; let themes = ['dark', 'light', 'oled-dark'];
let selectedTheme = 'system'; let selectedTheme = 'system';
let languages: Awaited<ReturnType<typeof getLanguages>> = []; let languages: Awaited<ReturnType<typeof getLanguages>> = [];
let lang = $i18n.language; let lang = $i18n.language;
let notificationEnabled = false; let notificationEnabled = false;
let system = ''; let system = '';
let showAdvanced = false; let showAdvanced = false;
const toggleNotification = async () => { const toggleNotification = async () => {
const permission = await Notification.requestPermission(); const permission = await Notification.requestPermission();
if (permission === 'granted') { if (permission === 'granted') {
notificationEnabled = !notificationEnabled; notificationEnabled = !notificationEnabled;
saveSettings({ notificationEnabled: notificationEnabled }); saveSettings({ notificationEnabled: notificationEnabled });
} else { } else {
toast.error( toast.error(
$i18n.t( $i18n.t(
'Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.' 'Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.'
) )
); );
} }
}; };
let params = { let params = {
// Advanced // Advanced
stream_response: null, stream_response: null,
function_calling: null, function_calling: null,
seed: null, seed: null,
temperature: null, temperature: null,
reasoning_effort: null, reasoning_effort: null,
logit_bias: null, logit_bias: null,
frequency_penalty: null, frequency_penalty: null,
presence_penalty: null, presence_penalty: null,
repeat_penalty: null, repeat_penalty: null,
repeat_last_n: null, repeat_last_n: null,
mirostat: null, mirostat: null,
mirostat_eta: null, mirostat_eta: null,
mirostat_tau: null, mirostat_tau: null,
top_k: null, top_k: null,
top_p: null, top_p: null,
min_p: null, min_p: null,
stop: null, stop: null,
tfs_z: null, tfs_z: null,
num_ctx: null, num_ctx: null,
num_batch: null, num_batch: null,
num_keep: null, num_keep: null,
max_tokens: null, max_tokens: null,
num_gpu: null num_gpu: null
}; };
const saveHandler = async () => { const saveHandler = async () => {
saveSettings({ saveSettings({
system: system !== '' ? system : undefined, system: system !== '' ? system : undefined,
params: { params: {
stream_response: params.stream_response !== null ? params.stream_response : undefined, stream_response: params.stream_response !== null ? params.stream_response : undefined,
function_calling: params.function_calling !== null ? params.function_calling : undefined, function_calling: params.function_calling !== null ? params.function_calling : undefined,
seed: (params.seed !== null ? params.seed : undefined) ?? undefined, seed: (params.seed !== null ? params.seed : undefined) ?? undefined,
stop: params.stop ? params.stop.split(',').filter((e) => e) : undefined, stop: params.stop ? params.stop.split(',').filter((e) => e) : undefined,
temperature: params.temperature !== null ? params.temperature : undefined, temperature: params.temperature !== null ? params.temperature : undefined,
reasoning_effort: params.reasoning_effort !== null ? params.reasoning_effort : undefined, reasoning_effort: params.reasoning_effort !== null ? params.reasoning_effort : undefined,
logit_bias: params.logit_bias !== null ? params.logit_bias : undefined, logit_bias: params.logit_bias !== null ? params.logit_bias : undefined,
frequency_penalty: params.frequency_penalty !== null ? params.frequency_penalty : undefined, frequency_penalty: params.frequency_penalty !== null ? params.frequency_penalty : undefined,
presence_penalty: params.frequency_penalty !== null ? params.frequency_penalty : undefined, presence_penalty: params.frequency_penalty !== null ? params.frequency_penalty : undefined,
repeat_penalty: params.frequency_penalty !== null ? params.frequency_penalty : undefined, repeat_penalty: params.frequency_penalty !== null ? params.frequency_penalty : undefined,
repeat_last_n: params.repeat_last_n !== null ? params.repeat_last_n : undefined, repeat_last_n: params.repeat_last_n !== null ? params.repeat_last_n : undefined,
mirostat: params.mirostat !== null ? params.mirostat : undefined, mirostat: params.mirostat !== null ? params.mirostat : undefined,
mirostat_eta: params.mirostat_eta !== null ? params.mirostat_eta : undefined, mirostat_eta: params.mirostat_eta !== null ? params.mirostat_eta : undefined,
mirostat_tau: params.mirostat_tau !== null ? params.mirostat_tau : undefined, mirostat_tau: params.mirostat_tau !== null ? params.mirostat_tau : undefined,
top_k: params.top_k !== null ? params.top_k : undefined, top_k: params.top_k !== null ? params.top_k : undefined,
top_p: params.top_p !== null ? params.top_p : undefined, top_p: params.top_p !== null ? params.top_p : undefined,
min_p: params.min_p !== null ? params.min_p : undefined, min_p: params.min_p !== null ? params.min_p : undefined,
tfs_z: params.tfs_z !== null ? params.tfs_z : undefined, tfs_z: params.tfs_z !== null ? params.tfs_z : undefined,
num_ctx: params.num_ctx !== null ? params.num_ctx : undefined, num_ctx: params.num_ctx !== null ? params.num_ctx : undefined,
num_batch: params.num_batch !== null ? params.num_batch : undefined, num_batch: params.num_batch !== null ? params.num_batch : undefined,
num_keep: params.num_keep !== null ? params.num_keep : undefined, num_keep: params.num_keep !== null ? params.num_keep : undefined,
max_tokens: params.max_tokens !== null ? params.max_tokens : undefined, max_tokens: params.max_tokens !== null ? params.max_tokens : undefined,
use_mmap: params.use_mmap !== null ? params.use_mmap : undefined, use_mmap: params.use_mmap !== null ? params.use_mmap : undefined,
use_mlock: params.use_mlock !== null ? params.use_mlock : undefined, use_mlock: params.use_mlock !== null ? params.use_mlock : undefined,
num_thread: params.num_thread !== null ? params.num_thread : undefined, num_thread: params.num_thread !== null ? params.num_thread : undefined,
num_gpu: params.num_gpu !== null ? params.num_gpu : undefined, num_gpu: params.num_gpu !== null ? params.num_gpu : undefined,
think: params.think !== null ? params.think : undefined, think: params.think !== null ? params.think : undefined,
keep_alive: params.keep_alive !== null ? params.keep_alive : undefined, keep_alive: params.keep_alive !== null ? params.keep_alive : undefined,
format: params.format !== null ? params.format : undefined format: params.format !== null ? params.format : undefined
} }
}); });
dispatch('save'); dispatch('save');
}; };
onMount(async () => { onMount(async () => {
selectedTheme = localStorage.theme ?? 'system'; selectedTheme = localStorage.theme ?? 'system';
languages = await getLanguages(); languages = await getLanguages();
notificationEnabled = $settings.notificationEnabled ?? false; notificationEnabled = $settings.notificationEnabled ?? false;
system = $settings.system ?? ''; system = $settings.system ?? '';
params = { ...params, ...$settings.params }; params = { ...params, ...$settings.params };
params.stop = $settings?.params?.stop ? ($settings?.params?.stop ?? []).join(',') : null; params.stop = $settings?.params?.stop ? ($settings?.params?.stop ?? []).join(',') : null;
}); });
const applyTheme = (_theme: string) => { const applyTheme = (_theme: string) => {
let themeToApply = _theme === 'oled-dark' ? 'dark' : _theme; let themeToApply = _theme === 'oled-dark' ? 'dark' : _theme;
if (_theme === 'system') { if (_theme === 'system') {
themeToApply = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; themeToApply = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
} }
if (themeToApply === 'dark' && !_theme.includes('oled')) { if (themeToApply === 'dark' && !_theme.includes('oled')) {
document.documentElement.style.setProperty('--color-gray-800', '#333'); document.documentElement.style.setProperty('--color-gray-800', '#333');
document.documentElement.style.setProperty('--color-gray-850', '#262626'); document.documentElement.style.setProperty('--color-gray-850', '#262626');
document.documentElement.style.setProperty('--color-gray-900', '#171717'); document.documentElement.style.setProperty('--color-gray-900', '#171717');
document.documentElement.style.setProperty('--color-gray-950', '#0d0d0d'); document.documentElement.style.setProperty('--color-gray-950', '#0d0d0d');
} }
themes themes
.filter((e) => e !== themeToApply) .filter((e) => e !== themeToApply)
.forEach((e) => { .forEach((e) => {
e.split(' ').forEach((e) => { e.split(' ').forEach((e) => {
document.documentElement.classList.remove(e); document.documentElement.classList.remove(e);
}); });
}); });
themeToApply.split(' ').forEach((e) => { themeToApply.split(' ').forEach((e) => {
document.documentElement.classList.add(e); document.documentElement.classList.add(e);
}); });
const metaThemeColor = document.querySelector('meta[name="theme-color"]'); const metaThemeColor = document.querySelector('meta[name="theme-color"]');
if (metaThemeColor) { if (metaThemeColor) {
if (_theme.includes('system')) { if (_theme.includes('system')) {
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark' ? 'dark'
: 'light'; : 'light';
console.log('Setting system meta theme color: ' + systemTheme); console.log('Setting system meta theme color: ' + systemTheme);
metaThemeColor.setAttribute('content', systemTheme === 'light' ? '#ffffff' : '#171717'); metaThemeColor.setAttribute('content', systemTheme === 'light' ? '#ffffff' : '#171717');
} else { } else {
console.log('Setting meta theme color: ' + _theme); console.log('Setting meta theme color: ' + _theme);
metaThemeColor.setAttribute( metaThemeColor.setAttribute(
'content', 'content',
_theme === 'dark' _theme === 'dark'
? '#171717' ? '#171717'
: _theme === 'oled-dark' : _theme === 'oled-dark'
? '#000000' ? '#000000'
: _theme === 'her' : _theme === 'her'
? '#983724' ? '#983724'
: '#ffffff' : '#ffffff'
); );
} }
} }
if (typeof window !== 'undefined' && window.applyTheme) { if (typeof window !== 'undefined' && window.applyTheme) {
window.applyTheme(); window.applyTheme();
} }
if (_theme.includes('oled')) { if (_theme.includes('oled')) {
document.documentElement.style.setProperty('--color-gray-800', '#101010'); document.documentElement.style.setProperty('--color-gray-800', '#101010');
document.documentElement.style.setProperty('--color-gray-850', '#050505'); document.documentElement.style.setProperty('--color-gray-850', '#050505');
document.documentElement.style.setProperty('--color-gray-900', '#000000'); document.documentElement.style.setProperty('--color-gray-900', '#000000');
document.documentElement.style.setProperty('--color-gray-950', '#000000'); document.documentElement.style.setProperty('--color-gray-950', '#000000');
document.documentElement.classList.add('dark'); document.documentElement.classList.add('dark');
} }
console.log(_theme); console.log(_theme);
}; };
const themeChangeHandler = (_theme: string) => { const themeChangeHandler = (_theme: string) => {
theme.set(_theme); theme.set(_theme);
localStorage.setItem('theme', _theme); localStorage.setItem('theme', _theme);
applyTheme(_theme); applyTheme(_theme);
}; };
</script> </script>
<div class="flex flex-col h-full justify-between text-sm" id="tab-general"> <div class="flex flex-col h-full justify-between text-sm" id="tab-general">
<div class=" overflow-y-scroll max-h-[28rem] lg:max-h-full"> <div class=" overflow-y-scroll max-h-[28rem] lg:max-h-full">
<div class=""> <div class="">
<div class=" mb-1 text-sm font-medium">{$i18n.t('WebUI Settings')}</div> <div class=" mb-1 text-sm font-medium">{$i18n.t('WebUI Settings')}</div>
<div class="flex w-full justify-between"> <div class="flex w-full justify-between">
<div class=" self-center text-xs font-medium">{$i18n.t('Theme')}</div> <div class=" self-center text-xs font-medium">{$i18n.t('Theme')}</div>
<div class="flex items-center relative"> <div class="flex items-center relative">
<select <select
class="dark:bg-gray-900 w-fit pr-8 rounded-sm py-2 px-2 text-xs bg-transparent text-right {$settings.highContrastMode class="dark:bg-gray-900 w-fit pr-8 rounded-sm py-2 px-2 text-xs bg-transparent text-right {$settings.highContrastMode
? '' ? ''
: 'outline-hidden'}" : 'outline-hidden'}"
bind:value={selectedTheme} bind:value={selectedTheme}
placeholder="Select a theme" placeholder="Select a theme"
on:change={() => themeChangeHandler(selectedTheme)} on:change={() => themeChangeHandler(selectedTheme)}
> >
<option value="system">⚙️ {$i18n.t('System')}</option> <option value="system">⚙️ {$i18n.t('System')}</option>
<option value="dark">🌑 {$i18n.t('Dark')}</option> <option value="dark">🌑 {$i18n.t('Dark')}</option>
<option value="oled-dark">🌃 {$i18n.t('OLED Dark')}</option> <option value="oled-dark">🌃 {$i18n.t('OLED Dark')}</option>
<option value="light">☀️ {$i18n.t('Light')}</option> <option value="light">☀️ {$i18n.t('Light')}</option>
<option value="her">🌷 Her</option> <option value="her">🌷 Her</option>
<!-- <option value="rose-pine dark">🪻 {$i18n.t('Rosé Pine')}</option> <!-- <option value="rose-pine dark">🪻 {$i18n.t('Rosé Pine')}</option>
<option value="rose-pine-dawn light">🌷 {$i18n.t('Rosé Pine Dawn')}</option> --> <option value="rose-pine-dawn light">🌷 {$i18n.t('Rosé Pine Dawn')}</option> -->
</select> </select>
</div> </div>
</div> </div>
<div class=" flex w-full justify-between"> <div class=" flex w-full justify-between">
<div class=" self-center text-xs font-medium">{$i18n.t('Language')}</div> <div class=" self-center text-xs font-medium">{$i18n.t('Language')}</div>
<div class="flex items-center relative"> <div class="flex items-center relative">
<select <select
class="dark:bg-gray-900 w-fit pr-8 rounded-sm py-2 px-2 text-xs bg-transparent text-right {$settings.highContrastMode class="dark:bg-gray-900 w-fit pr-8 rounded-sm py-2 px-2 text-xs bg-transparent text-right {$settings.highContrastMode
? '' ? ''
: 'outline-hidden'}" : 'outline-hidden'}"
bind:value={lang} bind:value={lang}
placeholder="Select a language" placeholder="Select a language"
on:change={(e) => { on:change={(e) => {
changeLanguage(lang); changeLanguage(lang);
}} }}
> >
{#each languages as language} {#each languages as language}
<option value={language['code']}>{language['title']}</option> <option value={language['code']}>{language['title']}</option>
{/each} {/each}
</select> </select>
</div> </div>
</div> </div>
{#if $i18n.language === 'en-US'} {#if $i18n.language === 'en-US'}
<div class="mb-2 text-xs text-gray-400 dark:text-gray-500"> <div class="mb-2 text-xs text-gray-400 dark:text-gray-500">
Couldn't find your language? Couldn't find your language?
<a <a
class=" text-gray-300 font-medium underline" class=" text-gray-300 font-medium underline"
href="https://github.com/open-webui/open-webui/blob/main/docs/CONTRIBUTING.md#-translations-and-internationalization" href="https://github.com/open-webui/open-webui/blob/main/docs/CONTRIBUTING.md#-translations-and-internationalization"
target="_blank" target="_blank"
> >
Help us translate Open WebUI! Help us translate Open WebUI!
</a> </a>
</div> </div>
{/if} {/if}
<div> <div>
<div class=" py-0.5 flex w-full justify-between"> <div class=" py-0.5 flex w-full justify-between">
<div class=" self-center text-xs font-medium">{$i18n.t('Notifications')}</div> <div class=" self-center text-xs font-medium">{$i18n.t('Notifications')}</div>
<button <button
class="p-1 px-3 text-xs flex rounded-sm transition" class="p-1 px-3 text-xs flex rounded-sm transition"
on:click={() => { on:click={() => {
toggleNotification(); toggleNotification();
}} }}
type="button" type="button"
> >
{#if notificationEnabled === true} {#if notificationEnabled === true}
<span class="ml-2 self-center">{$i18n.t('On')}</span> <span class="ml-2 self-center">{$i18n.t('On')}</span>
{:else} {:else}
<span class="ml-2 self-center">{$i18n.t('Off')}</span> <span class="ml-2 self-center">{$i18n.t('Off')}</span>
{/if} {/if}
</button> </button>
</div> </div>
</div> </div>
</div> </div>
{#if $user?.role === 'admin' || ($user?.permissions.chat?.system_prompt ?? true)} {#if $user?.role === 'admin' || ($user?.permissions.chat?.system_prompt ?? true)}
<hr class="border-gray-50 dark:border-gray-850 my-3" /> <hr class="border-gray-50 dark:border-gray-850 my-3" />
<div> <div>
<div class=" my-2.5 text-sm font-medium">{$i18n.t('System Prompt')}</div> <div class=" my-2.5 text-sm font-medium">{$i18n.t('System Prompt')}</div>
<Textarea <Textarea
bind:value={system} bind:value={system}
className={ className={
"w-full text-sm outline-hidden resize-vertical" + ($settings.highContrastMode "w-full text-sm outline-hidden resize-vertical" + ($settings.highContrastMode
? " p-2.5 border-2 border-gray-300 dark:border-gray-700 rounded-lg bg-gray-50 dark:bg-gray-850 text-gray-900 dark:text-gray-100 focus:ring-1 focus:ring-blue-500 focus:border-blue-500 overflow-y-hidden" ? " p-2.5 border-2 border-gray-300 dark:border-gray-700 rounded-lg bg-gray-50 dark:bg-gray-850 text-gray-900 dark:text-gray-100 focus:ring-1 focus:ring-blue-500 focus:border-blue-500 overflow-y-hidden"
: " bg-white dark:text-gray-300 dark:bg-gray-900") : " bg-white dark:text-gray-300 dark:bg-gray-900")
} }
rows="4" rows="4"
placeholder={$i18n.t('Enter system prompt here')} placeholder={$i18n.t('Enter system prompt here')}
/> />
</div> </div>
{/if} {/if}
{#if $user?.role === 'admin' || ($user?.permissions.chat?.controls ?? true)} {#if $user?.role === 'admin' || ($user?.permissions.chat?.controls ?? true)}
<div class="mt-2 space-y-3 pr-1.5"> <div class="mt-2 space-y-3 pr-1.5">
<div class="flex justify-between items-center text-sm"> <div class="flex justify-between items-center text-sm">
<div class=" font-medium">{$i18n.t('Advanced Parameters')}</div> <div class=" font-medium">{$i18n.t('Advanced Parameters')}</div>
<button <button
class=" text-xs font-medium text-gray-500" class=" text-xs font-medium text-gray-500"
type="button" type="button"
on:click={() => { on:click={() => {
showAdvanced = !showAdvanced; showAdvanced = !showAdvanced;
}}>{showAdvanced ? $i18n.t('Hide') : $i18n.t('Show')}</button }}>{showAdvanced ? $i18n.t('Hide') : $i18n.t('Show')}</button
> >
</div> </div>
{#if showAdvanced} {#if showAdvanced}
<AdvancedParams admin={$user?.role === 'admin'} bind:params /> <AdvancedParams admin={$user?.role === 'admin'} bind:params />
{/if} {/if}
</div> </div>
{/if} {/if}
</div> </div>
<div class="flex justify-end pt-3 text-sm font-medium"> <div class="flex justify-end pt-3 text-sm font-medium">
<button <button
class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full" class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
on:click={() => { on:click={() => {
saveHandler(); saveHandler();
}} }}
> >
{$i18n.t('Save')} {$i18n.t('Save')}
</button> </button>
</div> </div>
</div> </div>