Spaces:
Runtime error
Runtime error
| <script lang="ts"> | |
| import { toast } from "svelte-sonner"; | |
| import { createEventDispatcher, onMount, getContext } from "svelte"; | |
| import { getLanguages, changeLanguage } from "$lib/i18n"; | |
| const dispatch = createEventDispatcher(); | |
| import { config, 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, | |
| stream_delta_chunk_size: null, | |
| function_calling: null, | |
| reasoning_tags: 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, | |
| use_mmap: null, | |
| use_mlock: null, | |
| num_thread: null, | |
| num_gpu: null, | |
| think: null, | |
| format: null, | |
| keep_alive: null, | |
| }; | |
| const saveHandler = async () => { | |
| saveSettings({ | |
| system: system !== "" ? system : undefined, | |
| params: { | |
| stream_response: | |
| params.stream_response !== null | |
| ? params.stream_response | |
| : undefined, | |
| stream_delta_chunk_size: | |
| params.stream_delta_chunk_size !== null | |
| ? params.stream_delta_chunk_size | |
| : undefined, | |
| function_calling: | |
| params.function_calling !== null | |
| ? params.function_calling | |
| : undefined, | |
| reasoning_tags: | |
| params.reasoning_tags !== null | |
| ? params.reasoning_tags | |
| : 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.presence_penalty !== null | |
| ? params.presence_penalty | |
| : undefined, | |
| repeat_penalty: | |
| params.repeat_penalty !== null | |
| ? params.repeat_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, | |
| ...(params.custom_params && | |
| Object.keys(params.custom_params).length > 0 | |
| ? { custom_params: params.custom_params } | |
| : {}), | |
| }, | |
| }); | |
| dispatch("save"); | |
| }; | |
| onMount(async () => { | |
| selectedTheme = localStorage.theme ?? "system"; | |
| languages = await getLanguages(); | |
| if (!$config?.features?.enable_easter_eggs) { | |
| languages = languages.filter((l) => l.code !== "dg-DG"); | |
| } | |
| 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 === "her" | |
| ? "light" | |
| : _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); | |
| }; | |
| </script> | |
| <div class="flex flex-col h-full justify-between text-sm" id="tab-general"> | |
| <div class=" overflow-y-scroll max-h-[28rem] md:max-h-full"> | |
| <div class=""> | |
| <div class=" mb-1 text-sm font-medium"> | |
| {$i18n.t("rexpro 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="w-fit pr-8 rounded-sm py-2 px-2 text-xs bg-transparent text-right {$settings.highContrastMode | |
| ? '' | |
| : 'outline-hidden'}" | |
| bind:value={selectedTheme} | |
| placeholder={$i18n.t("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> | |
| {#if $config?.features?.enable_easter_eggs} | |
| <option value="her">🌷 Her</option> | |
| {/if} | |
| </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="w-fit pr-8 rounded-sm py-2 px-2 text-xs bg-transparent text-right {$settings.highContrastMode | |
| ? '' | |
| : 'outline-hidden'}" | |
| bind:value={lang} | |
| placeholder={$i18n.t("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" && !($config?.license_metadata ?? false)} | |
| <div | |
| class="mb-2 text-xs {($settings?.highContrastMode ?? false) | |
| ? 'text-gray-800 dark:text-gray-100' | |
| : 'text-gray-400 dark:text-gray-500'}" | |
| > | |
| Couldn't find your language? | |
| <a | |
| class="font-medium underline {($settings?.highContrastMode ?? | |
| false) | |
| ? 'text-gray-700 dark:text-gray-200' | |
| : 'text-gray-300'}" | |
| href="https://github.com/rexpro-ai/rexpro-ai/blob/main/docs/CONTRIBUTING.md#-translations-and-internationalization" | |
| target="_blank" | |
| > | |
| Help us translate rexpro-ai! | |
| </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" | |
| role="switch" | |
| aria-checked={notificationEnabled} | |
| > | |
| {#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?.controls ?? true) && ($user?.permissions.chat?.system_prompt ?? true))} | |
| <hr class="border-gray-100/30 dark:border-gray-850/30 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-transparent text-gray-900 dark:text-gray-100 focus:ring-1 focus:ring-blue-500 focus:border-blue-500 overflow-y-hidden" | |
| : " dark:text-gray-300 ")} | |
| rows="4" | |
| placeholder={$i18n.t("Enter system prompt here")} | |
| /> | |
| </div> | |
| {/if} | |
| {#if $user?.role === "admin" || (($user?.permissions.chat?.controls ?? true) && ($user?.permissions.chat?.params ?? 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 {($settings?.highContrastMode ?? | |
| false) | |
| ? 'text-gray-800 dark:text-gray-100' | |
| : 'text-gray-400 dark:text-gray-500'}" | |
| type="button" | |
| aria-expanded={showAdvanced} | |
| on:click={() => { | |
| showAdvanced = !showAdvanced; | |
| }} | |
| >{showAdvanced | |
| ? $i18n.t("Hide") | |
| : $i18n.t("Show")}</button | |
| > | |
| </div> | |
| {#if showAdvanced} | |
| <AdvancedParams | |
| admin={$user?.role === "admin"} | |
| custom={true} | |
| 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> | |
| </div> | |