Unverified Commit bced9073 authored by Timothy Jaeryang Baek's avatar Timothy Jaeryang Baek Committed by GitHub
Browse files

Merge branch 'websearch' into feat/backend-web-search

parents 276b7b90 b6b71c08
...@@ -6,6 +6,9 @@ ...@@ -6,6 +6,9 @@
import General from './Settings/General.svelte'; import General from './Settings/General.svelte';
import Users from './Settings/Users.svelte'; import Users from './Settings/Users.svelte';
import Banners from '$lib/components/admin/Settings/Banners.svelte';
import { toast } from 'svelte-sonner';
const i18n = getContext('i18n'); const i18n = getContext('i18n');
export let show = false; export let show = false;
...@@ -117,24 +120,63 @@ ...@@ -117,24 +120,63 @@
</div> </div>
<div class=" self-center">{$i18n.t('Database')}</div> <div class=" self-center">{$i18n.t('Database')}</div>
</button> </button>
<button
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
'banners'
? 'bg-gray-200 dark:bg-gray-700'
: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
on:click={() => {
selectedTab = 'banners';
}}
>
<div class=" self-center mr-2">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="size-4"
>
<path
d="M5.85 3.5a.75.75 0 0 0-1.117-1 9.719 9.719 0 0 0-2.348 4.876.75.75 0 0 0 1.479.248A8.219 8.219 0 0 1 5.85 3.5ZM19.267 2.5a.75.75 0 1 0-1.118 1 8.22 8.22 0 0 1 1.987 4.124.75.75 0 0 0 1.48-.248A9.72 9.72 0 0 0 19.266 2.5Z"
/>
<path
fill-rule="evenodd"
d="M12 2.25A6.75 6.75 0 0 0 5.25 9v.75a8.217 8.217 0 0 1-2.119 5.52.75.75 0 0 0 .298 1.206c1.544.57 3.16.99 4.831 1.243a3.75 3.75 0 1 0 7.48 0 24.583 24.583 0 0 0 4.83-1.244.75.75 0 0 0 .298-1.205 8.217 8.217 0 0 1-2.118-5.52V9A6.75 6.75 0 0 0 12 2.25ZM9.75 18c0-.034 0-.067.002-.1a25.05 25.05 0 0 0 4.496 0l.002.1a2.25 2.25 0 1 1-4.5 0Z"
clip-rule="evenodd"
/>
</svg>
</div>
<div class=" self-center">{$i18n.t('Banners')}</div>
</button>
</div> </div>
<div class="flex-1 md:min-h-[380px]"> <div class="flex-1 md:min-h-[380px]">
{#if selectedTab === 'general'} {#if selectedTab === 'general'}
<General <General
saveHandler={() => { saveHandler={() => {
show = false; show = false;
toast.success($i18n.t('Settings saved successfully!'));
}} }}
/> />
{:else if selectedTab === 'users'} {:else if selectedTab === 'users'}
<Users <Users
saveHandler={() => { saveHandler={() => {
show = false; show = false;
toast.success($i18n.t('Settings saved successfully!'));
}} }}
/> />
{:else if selectedTab === 'db'} {:else if selectedTab === 'db'}
<Database <Database
saveHandler={() => { saveHandler={() => {
show = false; show = false;
toast.success($i18n.t('Settings saved successfully!'));
}}
/>
{:else if selectedTab === 'banners'}
<Banners
saveHandler={() => {
show = false;
toast.success($i18n.t('Settings saved successfully!'));
}} }}
/> />
{/if} {/if}
......
...@@ -15,7 +15,8 @@ ...@@ -15,7 +15,8 @@
settings, settings,
showSidebar, showSidebar,
tags as _tags, tags as _tags,
WEBUI_NAME WEBUI_NAME,
banners
} from '$lib/stores'; } from '$lib/stores';
import { convertMessagesToHistory, copyToClipboard, splitStream } from '$lib/utils'; import { convertMessagesToHistory, copyToClipboard, splitStream } from '$lib/utils';
...@@ -45,6 +46,9 @@ ...@@ -45,6 +46,9 @@
import type { Writable } from 'svelte/store'; import type { Writable } from 'svelte/store';
import type { i18n as i18nType } from 'i18next'; import type { i18n as i18nType } from 'i18next';
import { runWebSearch } from '$lib/apis/rag'; import { runWebSearch } from '$lib/apis/rag';
import Banner from '../common/Banner.svelte';
import { getUserSettings } from '$lib/apis/users';
const i18n: Writable<i18nType> = getContext('i18n'); const i18n: Writable<i18nType> = getContext('i18n');
...@@ -141,6 +145,7 @@ ...@@ -141,6 +145,7 @@
} else if ($settings?.models) { } else if ($settings?.models) {
selectedModels = $settings?.models; selectedModels = $settings?.models;
} else if ($config?.default_models) { } else if ($config?.default_models) {
console.log($config?.default_models.split(',') ?? '');
selectedModels = $config?.default_models.split(','); selectedModels = $config?.default_models.split(',');
} else { } else {
selectedModels = ['']; selectedModels = [''];
...@@ -159,10 +164,13 @@ ...@@ -159,10 +164,13 @@
$models.map((m) => m.id).includes(modelId) ? modelId : '' $models.map((m) => m.id).includes(modelId) ? modelId : ''
); );
let _settings = JSON.parse(localStorage.getItem('settings') ?? '{}'); const userSettings = await getUserSettings(localStorage.token);
settings.set({
..._settings if (userSettings) {
}); settings.set(userSettings.ui);
} else {
settings.set(JSON.parse(localStorage.getItem('settings') ?? '{}'));
}
const chatInput = document.getElementById('chat-textarea'); const chatInput = document.getElementById('chat-textarea');
setTimeout(() => chatInput?.focus(), 0); setTimeout(() => chatInput?.focus(), 0);
...@@ -192,12 +200,20 @@ ...@@ -192,12 +200,20 @@
: convertMessagesToHistory(chatContent.messages); : convertMessagesToHistory(chatContent.messages);
title = chatContent.title; title = chatContent.title;
let _settings = JSON.parse(localStorage.getItem('settings') ?? '{}'); const userSettings = await getUserSettings(localStorage.token);
if (userSettings) {
await settings.set(userSettings.ui);
} else {
await settings.set(JSON.parse(localStorage.getItem('settings') ?? '{}'));
}
await settings.set({ await settings.set({
..._settings, ...$settings,
system: chatContent.system ?? _settings.system, system: chatContent.system ?? $settings.system,
params: chatContent.options ?? _settings.params params: chatContent.options ?? $settings.params
}); });
autoScroll = true; autoScroll = true;
await tick(); await tick();
...@@ -1073,6 +1089,34 @@ ...@@ -1073,6 +1089,34 @@
{chat} {chat}
{initNewChat} {initNewChat}
/> />
{#if $banners.length > 0 && !$chatId && selectedModels.length <= 1}
<div
class="absolute top-[4.25rem] w-full {$showSidebar ? 'md:max-w-[calc(100%-260px)]' : ''}"
>
<div class=" flex flex-col gap-1 w-full">
{#each $banners.filter( (b) => (b.dismissible ? !JSON.parse(localStorage.getItem('dismissedBannerIds') ?? '[]').includes(b.id) : true) ) as banner}
<Banner
{banner}
on:dismiss={(e) => {
const bannerId = e.detail;
localStorage.setItem(
'dismissedBannerIds',
JSON.stringify(
[
bannerId,
...JSON.parse(localStorage.getItem('dismissedBannerIds') ?? '[]')
].filter((id) => $banners.find((b) => b.id === id))
)
);
}}
/>
{/each}
</div>
</div>
{/if}
<div class="flex flex-col flex-auto"> <div class="flex flex-col flex-auto">
<div <div
class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0 max-w-full" class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0 max-w-full"
......
...@@ -797,7 +797,7 @@ ...@@ -797,7 +797,7 @@
</button> </button>
</Tooltip> </Tooltip>
{#if $config.enable_image_generation && !readOnly} {#if $config?.features.enable_image_generation && !readOnly}
<Tooltip content="Generate Image" placement="bottom"> <Tooltip content="Generate Image" placement="bottom">
<button <button
class="{isLastMessage class="{isLastMessage
......
<script lang="ts"> <script lang="ts">
import { Collapsible } from 'bits-ui';
import { setDefaultModels } from '$lib/apis/configs';
import { models, showSettings, settings, user, mobile } from '$lib/stores'; import { models, showSettings, settings, user, mobile } from '$lib/stores';
import { onMount, tick, getContext } from 'svelte'; import { onMount, tick, getContext } from 'svelte';
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';
import Selector from './ModelSelector/Selector.svelte'; import Selector from './ModelSelector/Selector.svelte';
import Tooltip from '../common/Tooltip.svelte'; import Tooltip from '../common/Tooltip.svelte';
import { setDefaultModels } from '$lib/apis/configs';
import { updateUserSettings } from '$lib/apis/users';
const i18n = getContext('i18n'); const i18n = getContext('i18n');
export let selectedModels = ['']; export let selectedModels = [''];
...@@ -22,12 +22,8 @@ ...@@ -22,12 +22,8 @@
return; return;
} }
settings.set({ ...$settings, models: selectedModels }); settings.set({ ...$settings, models: selectedModels });
localStorage.setItem('settings', JSON.stringify($settings)); await updateUserSettings(localStorage.token, { ui: $settings });
if ($user.role === 'admin') {
console.log('setting default models globally');
await setDefaultModels(localStorage.token, selectedModels.join(','));
}
toast.success($i18n.t('Default model updated')); toast.success($i18n.t('Default model updated'));
}; };
......
<script lang="ts"> <script lang="ts">
import { getVersionUpdates } from '$lib/apis'; import { getVersionUpdates } from '$lib/apis';
import { getOllamaVersion } from '$lib/apis/ollama'; import { getOllamaVersion } from '$lib/apis/ollama';
import { WEBUI_VERSION } from '$lib/constants'; import { WEBUI_BUILD_HASH, WEBUI_VERSION } from '$lib/constants';
import { WEBUI_NAME, config, showChangelog } from '$lib/stores'; import { WEBUI_NAME, config, showChangelog } from '$lib/stores';
import { compareVersion } from '$lib/utils'; import { compareVersion } from '$lib/utils';
import { onMount, getContext } from 'svelte'; import { onMount, getContext } from 'svelte';
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
<div class="flex w-full justify-between items-center"> <div class="flex w-full justify-between items-center">
<div class="flex flex-col text-xs text-gray-700 dark:text-gray-200"> <div class="flex flex-col text-xs text-gray-700 dark:text-gray-200">
<div class="flex gap-1"> <div class="flex gap-1">
<Tooltip content={WEBUI_VERSION === '0.1.117' ? "🪖 We're just getting started." : ''}> <Tooltip content={WEBUI_BUILD_HASH}>
v{WEBUI_VERSION} v{WEBUI_VERSION}
</Tooltip> </Tooltip>
......
<script lang="ts"> <script lang="ts">
import { getAudioConfig, updateAudioConfig } from '$lib/apis/audio'; import { getAudioConfig, updateAudioConfig } from '$lib/apis/audio';
import { user } from '$lib/stores'; import { user, settings } from '$lib/stores';
import { createEventDispatcher, onMount, getContext } from 'svelte'; import { createEventDispatcher, onMount, getContext } from 'svelte';
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
...@@ -99,16 +99,14 @@ ...@@ -99,16 +99,14 @@
}; };
onMount(async () => { onMount(async () => {
let settings = JSON.parse(localStorage.getItem('settings') ?? '{}'); conversationMode = $settings.conversationMode ?? false;
speechAutoSend = $settings.speechAutoSend ?? false;
conversationMode = settings.conversationMode ?? false; responseAutoPlayback = $settings.responseAutoPlayback ?? false;
speechAutoSend = settings.speechAutoSend ?? false;
responseAutoPlayback = settings.responseAutoPlayback ?? false; STTEngine = $settings?.audio?.STTEngine ?? '';
TTSEngine = $settings?.audio?.TTSEngine ?? '';
STTEngine = settings?.audio?.STTEngine ?? ''; speaker = $settings?.audio?.speaker ?? '';
TTSEngine = settings?.audio?.TTSEngine ?? ''; model = $settings?.audio?.model ?? '';
speaker = settings?.audio?.speaker ?? '';
model = settings?.audio?.model ?? '';
if (TTSEngine === 'openai') { if (TTSEngine === 'openai') {
getOpenAIVoices(); getOpenAIVoices();
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
import fileSaver from 'file-saver'; import fileSaver from 'file-saver';
const { saveAs } = fileSaver; const { saveAs } = fileSaver;
import { chats, user, config } from '$lib/stores'; import { chats, user, settings } from '$lib/stores';
import { import {
archiveAllChats, archiveAllChats,
...@@ -99,9 +99,7 @@ ...@@ -99,9 +99,7 @@
}; };
onMount(async () => { onMount(async () => {
let settings = JSON.parse(localStorage.getItem('settings') ?? '{}'); saveChatHistory = $settings.saveChatHistory ?? true;
saveChatHistory = settings.saveChatHistory ?? true;
}); });
</script> </script>
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
} from '$lib/apis/openai'; } from '$lib/apis/openai';
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';
import Switch from '$lib/components/common/Switch.svelte'; import Switch from '$lib/components/common/Switch.svelte';
import Spinner from '$lib/components/common/Spinner.svelte';
const i18n = getContext('i18n'); const i18n = getContext('i18n');
...@@ -31,8 +32,8 @@ ...@@ -31,8 +32,8 @@
let OPENAI_API_KEYS = ['']; let OPENAI_API_KEYS = [''];
let OPENAI_API_BASE_URLS = ['']; let OPENAI_API_BASE_URLS = [''];
let ENABLE_OPENAI_API = false; let ENABLE_OPENAI_API = null;
let ENABLE_OLLAMA_API = false; let ENABLE_OLLAMA_API = null;
const updateOpenAIHandler = async () => { const updateOpenAIHandler = async () => {
OPENAI_API_BASE_URLS = await updateOpenAIUrls(localStorage.token, OPENAI_API_BASE_URLS); OPENAI_API_BASE_URLS = await updateOpenAIUrls(localStorage.token, OPENAI_API_BASE_URLS);
...@@ -57,16 +58,23 @@ ...@@ -57,16 +58,23 @@
onMount(async () => { onMount(async () => {
if ($user.role === 'admin') { if ($user.role === 'admin') {
await Promise.all([
(async () => {
OLLAMA_BASE_URLS = await getOllamaUrls(localStorage.token);
})(),
(async () => {
OPENAI_API_BASE_URLS = await getOpenAIUrls(localStorage.token);
})(),
(async () => {
OPENAI_API_KEYS = await getOpenAIKeys(localStorage.token);
})()
]);
const ollamaConfig = await getOllamaConfig(localStorage.token); const ollamaConfig = await getOllamaConfig(localStorage.token);
const openaiConfig = await getOpenAIConfig(localStorage.token); const openaiConfig = await getOpenAIConfig(localStorage.token);
ENABLE_OPENAI_API = openaiConfig.ENABLE_OPENAI_API; ENABLE_OPENAI_API = openaiConfig.ENABLE_OPENAI_API;
ENABLE_OLLAMA_API = ollamaConfig.ENABLE_OLLAMA_API; ENABLE_OLLAMA_API = ollamaConfig.ENABLE_OLLAMA_API;
OLLAMA_BASE_URLS = await getOllamaUrls(localStorage.token);
OPENAI_API_BASE_URLS = await getOpenAIUrls(localStorage.token);
OPENAI_API_KEYS = await getOpenAIKeys(localStorage.token);
} }
}); });
</script> </script>
...@@ -78,7 +86,8 @@ ...@@ -78,7 +86,8 @@
dispatch('save'); dispatch('save');
}} }}
> >
<div class=" pr-1.5 overflow-y-scroll max-h-[25rem] space-y-3"> <div class="space-y-3 pr-1.5 overflow-y-scroll h-[24rem] max-h-[25rem]">
{#if ENABLE_OPENAI_API !== null && ENABLE_OLLAMA_API !== null}
<div class=" space-y-3"> <div class=" space-y-3">
<div class="mt-2 space-y-2 pr-1.5"> <div class="mt-2 space-y-2 pr-1.5">
<div class="flex justify-between items-center text-sm"> <div class="flex justify-between items-center text-sm">
...@@ -219,7 +228,9 @@ ...@@ -219,7 +228,9 @@
<button <button
class="px-1" class="px-1"
on:click={() => { on:click={() => {
OLLAMA_BASE_URLS = OLLAMA_BASE_URLS.filter((url, urlIdx) => idx !== urlIdx); OLLAMA_BASE_URLS = OLLAMA_BASE_URLS.filter(
(url, urlIdx) => idx !== urlIdx
);
}} }}
type="button" type="button"
> >
...@@ -274,6 +285,13 @@ ...@@ -274,6 +285,13 @@
</div> </div>
{/if} {/if}
</div> </div>
{:else}
<div class="flex h-full justify-center">
<div class="my-auto">
<Spinner className="size-6" />
</div>
</div>
{/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">
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
import { getLanguages } from '$lib/i18n'; import { getLanguages } from '$lib/i18n';
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
import { models, user, theme } from '$lib/stores'; import { models, settings, theme } from '$lib/stores';
const i18n = getContext('i18n'); const i18n = getContext('i18n');
...@@ -71,23 +71,22 @@ ...@@ -71,23 +71,22 @@
onMount(async () => { onMount(async () => {
selectedTheme = localStorage.theme ?? 'system'; selectedTheme = localStorage.theme ?? 'system';
let settings = JSON.parse(localStorage.getItem('settings') ?? '{}');
languages = await getLanguages(); languages = await getLanguages();
notificationEnabled = settings.notificationEnabled ?? false; notificationEnabled = $settings.notificationEnabled ?? false;
system = settings.system ?? ''; system = $settings.system ?? '';
requestFormat = settings.requestFormat ?? ''; requestFormat = $settings.requestFormat ?? '';
keepAlive = settings.keepAlive ?? null; keepAlive = $settings.keepAlive ?? null;
params.seed = settings.seed ?? 0; params.seed = $settings.seed ?? 0;
params.temperature = settings.temperature ?? ''; params.temperature = $settings.temperature ?? '';
params.frequency_penalty = settings.frequency_penalty ?? ''; params.frequency_penalty = $settings.frequency_penalty ?? '';
params.top_k = settings.top_k ?? ''; params.top_k = $settings.top_k ?? '';
params.top_p = settings.top_p ?? ''; params.top_p = $settings.top_p ?? '';
params.num_ctx = settings.num_ctx ?? ''; params.num_ctx = $settings.num_ctx ?? '';
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) => {
......
...@@ -105,23 +105,18 @@ ...@@ -105,23 +105,18 @@
promptSuggestions = $config?.default_prompt_suggestions; promptSuggestions = $config?.default_prompt_suggestions;
} }
let settings = JSON.parse(localStorage.getItem('settings') ?? '{}'); titleAutoGenerate = $settings?.title?.auto ?? true;
titleAutoGenerateModel = $settings?.title?.model ?? '';
titleAutoGenerate = settings?.title?.auto ?? true; titleAutoGenerateModelExternal = $settings?.title?.modelExternal ?? '';
titleAutoGenerateModel = settings?.title?.model ?? '';
titleAutoGenerateModelExternal = settings?.title?.modelExternal ?? '';
titleGenerationPrompt = titleGenerationPrompt =
settings?.title?.prompt ?? $settings?.title?.prompt ??
$i18n.t( `Create a concise, 3-5 word phrase as a header for the following query, strictly adhering to the 3-5 word limit and avoiding the use of the word 'title': {{prompt}}`;
"Create a concise, 3-5 word phrase as a header for the following query, strictly adhering to the 3-5 word limit and avoiding the use of the word 'title':" responseAutoCopy = $settings.responseAutoCopy ?? false;
) + ' {{prompt}}'; showUsername = $settings.showUsername ?? false;
chatBubble = $settings.chatBubble ?? true;
responseAutoCopy = settings.responseAutoCopy ?? false; fullScreenMode = $settings.fullScreenMode ?? false;
showUsername = settings.showUsername ?? false; splitLargeChunks = $settings.splitLargeChunks ?? false;
chatBubble = settings.chatBubble ?? true; chatDirection = $settings.chatDirection ?? 'LTR';
fullScreenMode = settings.fullScreenMode ?? false;
splitLargeChunks = settings.splitLargeChunks ?? false;
chatDirection = settings.chatDirection ?? 'LTR';
}); });
</script> </script>
......
...@@ -19,8 +19,7 @@ ...@@ -19,8 +19,7 @@
let enableMemory = false; let enableMemory = false;
onMount(async () => { onMount(async () => {
let settings = JSON.parse(localStorage.getItem('settings') ?? '{}'); enableMemory = $settings?.memory ?? false;
enableMemory = settings?.memory ?? false;
}); });
</script> </script>
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
import Images from './Settings/Images.svelte'; import Images from './Settings/Images.svelte';
import User from '../icons/User.svelte'; import User from '../icons/User.svelte';
import Personalization from './Settings/Personalization.svelte'; import Personalization from './Settings/Personalization.svelte';
import { updateUserSettings } from '$lib/apis/users';
const i18n = getContext('i18n'); const i18n = getContext('i18n');
...@@ -26,7 +27,7 @@ ...@@ -26,7 +27,7 @@
console.log(updated); console.log(updated);
await settings.set({ ...$settings, ...updated }); await settings.set({ ...$settings, ...updated });
await models.set(await getModels()); await models.set(await getModels());
localStorage.setItem('settings', JSON.stringify($settings)); await updateUserSettings(localStorage.token, { ui: $settings });
}; };
const getModels = async () => { const getModels = async () => {
......
<script lang="ts"> <script lang="ts">
import { getContext, onMount } from 'svelte'; import { getContext, onMount } from 'svelte';
import { models } from '$lib/stores'; import { models, config } from '$lib/stores';
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';
import { deleteSharedChatById, getChatById, shareChatById } from '$lib/apis/chats'; import { deleteSharedChatById, getChatById, shareChatById } from '$lib/apis/chats';
...@@ -134,6 +134,7 @@ ...@@ -134,6 +134,7 @@
<div class="flex justify-end"> <div class="flex justify-end">
<div class="flex flex-col items-end space-x-1 mt-1.5"> <div class="flex flex-col items-end space-x-1 mt-1.5">
<div class="flex gap-1"> <div class="flex gap-1">
{#if $config?.features.enable_community_sharing}
<button <button
class=" self-center px-3.5 py-2 rounded-xl text-sm font-medium bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-white" class=" self-center px-3.5 py-2 rounded-xl text-sm font-medium bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-white"
type="button" type="button"
...@@ -144,6 +145,7 @@ ...@@ -144,6 +145,7 @@
> >
{$i18n.t('Share to OpenWebUI Community')} {$i18n.t('Share to OpenWebUI Community')}
</button> </button>
{/if}
<button <button
class=" self-center flex items-center gap-1 px-3.5 py-2 rounded-xl text-sm font-medium bg-emerald-600 hover:bg-emerald-500 text-white" class=" self-center flex items-center gap-1 px-3.5 py-2 rounded-xl text-sm font-medium bg-emerald-600 hover:bg-emerald-500 text-white"
......
<script lang="ts">
import type { Banner } from '$lib/types';
import { onMount, createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition';
const dispatch = createEventDispatcher();
export let banner: Banner = {
id: '',
type: 'info',
title: '',
content: '',
url: '',
dismissable: true,
timestamp: Math.floor(Date.now() / 1000)
};
export let dismissed = false;
let mounted = false;
const classNames: Record<string, string> = {
info: 'bg-blue-500/20 text-blue-700 dark:text-blue-200 ',
success: 'bg-green-500/20 text-green-700 dark:text-green-200',
warning: 'bg-yellow-500/20 text-yellow-700 dark:text-yellow-200',
error: 'bg-red-500/20 text-red-700 dark:text-red-200'
};
const dismiss = (id) => {
dismissed = true;
dispatch('dismiss', id);
};
onMount(() => {
mounted = true;
});
</script>
{#if !dismissed}
{#if mounted}
<div
class=" top-0 left-0 right-0 p-2 mx-4 px-3 flex justify-center items-center relative rounded-xl border border-gray-100 dark:border-gray-850 text-gray-800 dark:text-gary-100 bg-white dark:bg-gray-900 backdrop-blur-xl z-40"
transition:fade={{ delay: 100, duration: 300 }}
>
<div class=" flex flex-col md:flex-row md:items-center flex-1 text-sm w-fit gap-1.5">
<div class="flex justify-between self-start">
<div
class=" text-xs font-black {classNames[banner.type] ??
classNames['info']} w-fit px-2 rounded uppercase line-clamp-1 mr-0.5"
>
{banner.type}
</div>
{#if banner.url}
<div class="flex md:hidden group w-fit md:items-center">
<a
class="text-gray-700 dark:text-white text-xs font-bold underline"
href="/assets/files/whitepaper.pdf"
target="_blank">Learn More</a
>
<div
class=" ml-1 text-gray-400 group-hover:text-gray-600 dark:group-hover:text-white"
>
<!-- -->
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
fill-rule="evenodd"
d="M4.22 11.78a.75.75 0 0 1 0-1.06L9.44 5.5H5.75a.75.75 0 0 1 0-1.5h5.5a.75.75 0 0 1 .75.75v5.5a.75.75 0 0 1-1.5 0V6.56l-5.22 5.22a.75.75 0 0 1-1.06 0Z"
clip-rule="evenodd"
/>
</svg>
</div>
</div>
{/if}
</div>
<div class="flex-1 text-xs text-gray-700 dark:text-white">
{banner.content}
</div>
</div>
{#if banner.url}
<div class="hidden md:flex group w-fit md:items-center">
<a
class="text-gray-700 dark:text-white text-xs font-bold underline"
href="/"
target="_blank">Learn More</a
>
<div class=" ml-1 text-gray-400 group-hover:text-gray-600 dark:group-hover:text-white">
<!-- -->
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="size-4"
>
<path
fill-rule="evenodd"
d="M4.22 11.78a.75.75 0 0 1 0-1.06L9.44 5.5H5.75a.75.75 0 0 1 0-1.5h5.5a.75.75 0 0 1 .75.75v5.5a.75.75 0 0 1-1.5 0V6.56l-5.22 5.22a.75.75 0 0 1-1.06 0Z"
clip-rule="evenodd"
/>
</svg>
</div>
</div>
{/if}
<div class="flex self-start">
{#if banner.dismissible}
<button
on:click={() => {
dismiss(banner.id);
}}
class=" -mt-[3px] ml-1.5 mr-1 text-gray-400 dark:hover:text-white h-1">&times;</button
>
{/if}
</div>
</div>
{/if}
{/if}
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
import ArchiveBox from '../icons/ArchiveBox.svelte'; import ArchiveBox from '../icons/ArchiveBox.svelte';
import ArchivedChatsModal from './Sidebar/ArchivedChatsModal.svelte'; import ArchivedChatsModal from './Sidebar/ArchivedChatsModal.svelte';
import UserMenu from './Sidebar/UserMenu.svelte'; import UserMenu from './Sidebar/UserMenu.svelte';
import { updateUserSettings } from '$lib/apis/users';
const BREAKPOINT = 768; const BREAKPOINT = 768;
...@@ -183,7 +184,7 @@ ...@@ -183,7 +184,7 @@
const saveSettings = async (updated) => { const saveSettings = async (updated) => {
await settings.set({ ...$settings, ...updated }); await settings.set({ ...$settings, ...updated });
localStorage.setItem('settings', JSON.stringify($settings)); await updateUserSettings(localStorage.token, { ui: $settings });
location.href = '/'; location.href = '/';
}; };
......
...@@ -101,7 +101,7 @@ ...@@ -101,7 +101,7 @@
<hr class=" dark:border-gray-850 my-2" /> <hr class=" dark:border-gray-850 my-2" />
<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6"> <div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
{#if chats.length > 0} {#if chats.length > 0}
<div> <div class="w-full">
<div class="text-left text-sm w-full mb-3 max-h-[22rem] overflow-y-scroll"> <div class="text-left text-sm w-full mb-3 max-h-[22rem] overflow-y-scroll">
<div class="relative overflow-x-auto"> <div class="relative overflow-x-auto">
<table class="w-full text-sm text-left text-gray-600 dark:text-gray-400 table-auto"> <table class="w-full text-sm text-left text-gray-600 dark:text-gray-400 table-auto">
......
...@@ -13,6 +13,7 @@ export const IMAGES_API_BASE_URL = `${WEBUI_BASE_URL}/images/api/v1`; ...@@ -13,6 +13,7 @@ export const IMAGES_API_BASE_URL = `${WEBUI_BASE_URL}/images/api/v1`;
export const RAG_API_BASE_URL = `${WEBUI_BASE_URL}/rag/api/v1`; export const RAG_API_BASE_URL = `${WEBUI_BASE_URL}/rag/api/v1`;
export const WEBUI_VERSION = APP_VERSION; export const WEBUI_VERSION = APP_VERSION;
export const WEBUI_BUILD_HASH = APP_BUILD_HASH;
export const REQUIRED_OLLAMA_VERSION = '0.1.16'; export const REQUIRED_OLLAMA_VERSION = '0.1.16';
export const SUPPORTED_FILE_TYPE = [ export const SUPPORTED_FILE_TYPE = [
......
...@@ -63,6 +63,7 @@ ...@@ -63,6 +63,7 @@
"available!": "متاح", "available!": "متاح",
"Back": "خلف", "Back": "خلف",
"Bad Response": "استجابة خطاء", "Bad Response": "استجابة خطاء",
"Banners": "",
"Base Model (From)": "", "Base Model (From)": "",
"before": "قبل", "before": "قبل",
"Being lazy": "كون كسول", "Being lazy": "كون كسول",
...@@ -188,6 +189,7 @@ ...@@ -188,6 +189,7 @@
"Enter Your Full Name": "أدخل الاسم كامل", "Enter Your Full Name": "أدخل الاسم كامل",
"Enter Your Password": "ادخل كلمة المرور", "Enter Your Password": "ادخل كلمة المرور",
"Enter Your Role": "أدخل الصلاحيات", "Enter Your Role": "أدخل الصلاحيات",
"Error": "",
"Experimental": "تجريبي", "Experimental": "تجريبي",
"Export All Chats (All Users)": "تصدير جميع الدردشات (جميع المستخدمين)", "Export All Chats (All Users)": "تصدير جميع الدردشات (جميع المستخدمين)",
"Export Chats": "تصدير جميع الدردشات", "Export Chats": "تصدير جميع الدردشات",
...@@ -228,6 +230,7 @@ ...@@ -228,6 +230,7 @@
"Import Models": "", "Import Models": "",
"Import Prompts": "مطالبات الاستيراد", "Import Prompts": "مطالبات الاستيراد",
"Include `--api` flag when running stable-diffusion-webui": "قم بتضمين علامة `-api` عند تشغيل Stable-diffusion-webui", "Include `--api` flag when running stable-diffusion-webui": "قم بتضمين علامة `-api` عند تشغيل Stable-diffusion-webui",
"Info": "",
"Input commands": "إدخال الأوامر", "Input commands": "إدخال الأوامر",
"Interface": "واجهه المستخدم", "Interface": "واجهه المستخدم",
"Invalid Tag": "تاق غير صالحة", "Invalid Tag": "تاق غير صالحة",
...@@ -450,6 +453,7 @@ ...@@ -450,6 +453,7 @@
"Top P": "Top P", "Top P": "Top P",
"Trouble accessing Ollama?": "هل تواجه مشكلة في الوصول", "Trouble accessing Ollama?": "هل تواجه مشكلة في الوصول",
"TTS Settings": "TTS اعدادات", "TTS Settings": "TTS اعدادات",
"Type": "",
"Type Hugging Face Resolve (Download) URL": "اكتب عنوان URL لحل مشكلة الوجه (تنزيل).", "Type Hugging Face Resolve (Download) URL": "اكتب عنوان URL لحل مشكلة الوجه (تنزيل).",
"Uh-oh! There was an issue connecting to {{provider}}.": "{{provider}}خطاء أوه! حدثت مشكلة في الاتصال بـ ", "Uh-oh! There was an issue connecting to {{provider}}.": "{{provider}}خطاء أوه! حدثت مشكلة في الاتصال بـ ",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "نوع ملف غير معروف '{{file_type}}', ولكن القبول والتعامل كنص عادي ", "Unknown File Type '{{file_type}}', but accepting and treating as plain text": "نوع ملف غير معروف '{{file_type}}', ولكن القبول والتعامل كنص عادي ",
...@@ -470,6 +474,7 @@ ...@@ -470,6 +474,7 @@
"variable": "المتغير", "variable": "المتغير",
"variable to have them replaced with clipboard content.": "متغير لاستبدالها بمحتوى الحافظة.", "variable to have them replaced with clipboard content.": "متغير لاستبدالها بمحتوى الحافظة.",
"Version": "إصدار", "Version": "إصدار",
"Warning": "",
"Warning: If you update or change your embedding model, you will need to re-import all documents.": "تحذير: إذا قمت بتحديث أو تغيير نموذج التضمين الخاص بك، فستحتاج إلى إعادة استيراد كافة المستندات.", "Warning: If you update or change your embedding model, you will need to re-import all documents.": "تحذير: إذا قمت بتحديث أو تغيير نموذج التضمين الخاص بك، فستحتاج إلى إعادة استيراد كافة المستندات.",
"Web": "Web", "Web": "Web",
"Web Loader Settings": "Web تحميل اعدادات", "Web Loader Settings": "Web تحميل اعدادات",
......
...@@ -63,6 +63,7 @@ ...@@ -63,6 +63,7 @@
"available!": "наличен!", "available!": "наличен!",
"Back": "Назад", "Back": "Назад",
"Bad Response": "Невалиден отговор от API", "Bad Response": "Невалиден отговор от API",
"Banners": "",
"Base Model (From)": "", "Base Model (From)": "",
"before": "преди", "before": "преди",
"Being lazy": "Да бъдеш мързелив", "Being lazy": "Да бъдеш мързелив",
...@@ -188,6 +189,7 @@ ...@@ -188,6 +189,7 @@
"Enter Your Full Name": "Въведете вашето пълно име", "Enter Your Full Name": "Въведете вашето пълно име",
"Enter Your Password": "Въведете вашата парола", "Enter Your Password": "Въведете вашата парола",
"Enter Your Role": "Въведете вашата роля", "Enter Your Role": "Въведете вашата роля",
"Error": "",
"Experimental": "Експериментално", "Experimental": "Експериментално",
"Export All Chats (All Users)": "Експортване на всички чатове (За всички потребители)", "Export All Chats (All Users)": "Експортване на всички чатове (За всички потребители)",
"Export Chats": "Експортване на чатове", "Export Chats": "Експортване на чатове",
...@@ -228,6 +230,7 @@ ...@@ -228,6 +230,7 @@
"Import Models": "", "Import Models": "",
"Import Prompts": "Импортване на промптове", "Import Prompts": "Импортване на промптове",
"Include `--api` flag when running stable-diffusion-webui": "Включете флага `--api`, когато стартирате stable-diffusion-webui", "Include `--api` flag when running stable-diffusion-webui": "Включете флага `--api`, когато стартирате stable-diffusion-webui",
"Info": "",
"Input commands": "Въведете команди", "Input commands": "Въведете команди",
"Interface": "Интерфейс", "Interface": "Интерфейс",
"Invalid Tag": "Невалиден тег", "Invalid Tag": "Невалиден тег",
...@@ -450,6 +453,7 @@ ...@@ -450,6 +453,7 @@
"Top P": "Top P", "Top P": "Top P",
"Trouble accessing Ollama?": "Проблеми с достъпът до Ollama?", "Trouble accessing Ollama?": "Проблеми с достъпът до Ollama?",
"TTS Settings": "TTS Настройки", "TTS Settings": "TTS Настройки",
"Type": "",
"Type Hugging Face Resolve (Download) URL": "Въведете Hugging Face Resolve (Download) URL", "Type Hugging Face Resolve (Download) URL": "Въведете Hugging Face Resolve (Download) URL",
"Uh-oh! There was an issue connecting to {{provider}}.": "О, не! Възникна проблем при свързването с {{provider}}.", "Uh-oh! There was an issue connecting to {{provider}}.": "О, не! Възникна проблем при свързването с {{provider}}.",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Непознат файлов тип '{{file_type}}', но се приема и обработва като текст", "Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Непознат файлов тип '{{file_type}}', но се приема и обработва като текст",
...@@ -470,6 +474,7 @@ ...@@ -470,6 +474,7 @@
"variable": "променлива", "variable": "променлива",
"variable to have them replaced with clipboard content.": "променливи да се заменят съдържанието от клипборд.", "variable to have them replaced with clipboard content.": "променливи да се заменят съдържанието от клипборд.",
"Version": "Версия", "Version": "Версия",
"Warning": "",
"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Предупреждение: Ако актуализирате или промените вашия модел за вграждане, трябва да повторите импортирането на всички документи.", "Warning: If you update or change your embedding model, you will need to re-import all documents.": "Предупреждение: Ако актуализирате или промените вашия модел за вграждане, трябва да повторите импортирането на всички документи.",
"Web": "Уеб", "Web": "Уеб",
"Web Loader Settings": "Настройки за зареждане на уеб", "Web Loader Settings": "Настройки за зареждане на уеб",
......
...@@ -63,6 +63,7 @@ ...@@ -63,6 +63,7 @@
"available!": "উপলব্ধ!", "available!": "উপলব্ধ!",
"Back": "পেছনে", "Back": "পেছনে",
"Bad Response": "খারাপ প্রতিক্রিয়া", "Bad Response": "খারাপ প্রতিক্রিয়া",
"Banners": "",
"Base Model (From)": "", "Base Model (From)": "",
"before": "পূর্ববর্তী", "before": "পূর্ববর্তী",
"Being lazy": "অলস হওয়া", "Being lazy": "অলস হওয়া",
...@@ -188,6 +189,7 @@ ...@@ -188,6 +189,7 @@
"Enter Your Full Name": "আপনার পূর্ণ নাম লিখুন", "Enter Your Full Name": "আপনার পূর্ণ নাম লিখুন",
"Enter Your Password": "আপনার পাসওয়ার্ড লিখুন", "Enter Your Password": "আপনার পাসওয়ার্ড লিখুন",
"Enter Your Role": "আপনার রোল লিখুন", "Enter Your Role": "আপনার রোল লিখুন",
"Error": "",
"Experimental": "পরিক্ষামূলক", "Experimental": "পরিক্ষামূলক",
"Export All Chats (All Users)": "সব চ্যাট এক্সপোর্ট করুন (সব ইউজারের)", "Export All Chats (All Users)": "সব চ্যাট এক্সপোর্ট করুন (সব ইউজারের)",
"Export Chats": "চ্যাটগুলো এক্সপোর্ট করুন", "Export Chats": "চ্যাটগুলো এক্সপোর্ট করুন",
...@@ -228,6 +230,7 @@ ...@@ -228,6 +230,7 @@
"Import Models": "", "Import Models": "",
"Import Prompts": "প্রম্পটগুলো ইমপোর্ট করুন", "Import Prompts": "প্রম্পটগুলো ইমপোর্ট করুন",
"Include `--api` flag when running stable-diffusion-webui": "stable-diffusion-webui চালু করার সময় `--api` ফ্ল্যাগ সংযুক্ত করুন", "Include `--api` flag when running stable-diffusion-webui": "stable-diffusion-webui চালু করার সময় `--api` ফ্ল্যাগ সংযুক্ত করুন",
"Info": "",
"Input commands": "ইনপুট কমান্ডস", "Input commands": "ইনপুট কমান্ডস",
"Interface": "ইন্টারফেস", "Interface": "ইন্টারফেস",
"Invalid Tag": "অবৈধ ট্যাগ", "Invalid Tag": "অবৈধ ট্যাগ",
...@@ -450,6 +453,7 @@ ...@@ -450,6 +453,7 @@
"Top P": "Top P", "Top P": "Top P",
"Trouble accessing Ollama?": "Ollama এক্সেস করতে সমস্যা হচ্ছে?", "Trouble accessing Ollama?": "Ollama এক্সেস করতে সমস্যা হচ্ছে?",
"TTS Settings": "TTS সেটিংসমূহ", "TTS Settings": "TTS সেটিংসমূহ",
"Type": "",
"Type Hugging Face Resolve (Download) URL": "Hugging Face থেকে ডাউনলোড করার ইউআরএল টাইপ করুন", "Type Hugging Face Resolve (Download) URL": "Hugging Face থেকে ডাউনলোড করার ইউআরএল টাইপ করুন",
"Uh-oh! There was an issue connecting to {{provider}}.": "ওহ-হো! {{provider}} এর সাথে কানেকশনে সমস্যা হয়েছে।", "Uh-oh! There was an issue connecting to {{provider}}.": "ওহ-হো! {{provider}} এর সাথে কানেকশনে সমস্যা হয়েছে।",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "অপরিচিত ফাইল ফরম্যাট '{{file_type}}', তবে প্লেইন টেক্সট হিসেবে গ্রহণ করা হলো", "Unknown File Type '{{file_type}}', but accepting and treating as plain text": "অপরিচিত ফাইল ফরম্যাট '{{file_type}}', তবে প্লেইন টেক্সট হিসেবে গ্রহণ করা হলো",
...@@ -470,6 +474,7 @@ ...@@ -470,6 +474,7 @@
"variable": "ভেরিয়েবল", "variable": "ভেরিয়েবল",
"variable to have them replaced with clipboard content.": "ক্লিপবোর্ডের কন্টেন্ট দিয়ে যেই ভেরিয়েবল রিপ্লেস করা যাবে।", "variable to have them replaced with clipboard content.": "ক্লিপবোর্ডের কন্টেন্ট দিয়ে যেই ভেরিয়েবল রিপ্লেস করা যাবে।",
"Version": "ভার্সন", "Version": "ভার্সন",
"Warning": "",
"Warning: If you update or change your embedding model, you will need to re-import all documents.": "সতর্কীকরণ: আপনি যদি আপনার এম্বেডিং মডেল আপডেট বা পরিবর্তন করেন, তাহলে আপনাকে সমস্ত নথি পুনরায় আমদানি করতে হবে।.", "Warning: If you update or change your embedding model, you will need to re-import all documents.": "সতর্কীকরণ: আপনি যদি আপনার এম্বেডিং মডেল আপডেট বা পরিবর্তন করেন, তাহলে আপনাকে সমস্ত নথি পুনরায় আমদানি করতে হবে।.",
"Web": "ওয়েব", "Web": "ওয়েব",
"Web Loader Settings": "ওয়েব লোডার সেটিংস", "Web Loader Settings": "ওয়েব লোডার সেটিংস",
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment