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