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

Merge pull request #1187 from open-webui/dev

0.1.113
parents 5ce421e7 ec9895a1
...@@ -2,10 +2,12 @@ ...@@ -2,10 +2,12 @@
import { getBackendConfig } from '$lib/apis'; import { getBackendConfig } from '$lib/apis';
import { setDefaultPromptSuggestions } from '$lib/apis/configs'; import { setDefaultPromptSuggestions } from '$lib/apis/configs';
import { config, models, user } from '$lib/stores'; import { config, models, user } from '$lib/stores';
import { createEventDispatcher, onMount } from 'svelte'; import { createEventDispatcher, onMount, getContext } from 'svelte';
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
const i18n = getContext('i18n');
export let saveSettings: Function; export let saveSettings: Function;
// Addons // Addons
...@@ -82,7 +84,9 @@ ...@@ -82,7 +84,9 @@
titleAutoGenerateModel = settings.titleAutoGenerateModel ?? ''; titleAutoGenerateModel = settings.titleAutoGenerateModel ?? '';
titleGenerationPrompt = titleGenerationPrompt =
settings.titleGenerationPrompt ?? settings.titleGenerationPrompt ??
`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}}`; $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}}';
}); });
</script> </script>
...@@ -93,13 +97,13 @@ ...@@ -93,13 +97,13 @@
dispatch('save'); dispatch('save');
}} }}
> >
<div class=" space-y-3 pr-1.5 overflow-y-scroll h-80"> <div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-[22rem]">
<div> <div>
<div class=" mb-1 text-sm font-medium">WebUI Add-ons</div> <div class=" mb-1 text-sm font-medium">{$i18n.t('WebUI Add-ons')}</div>
<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">Title Auto-Generation</div> <div class=" self-center text-xs font-medium">{$i18n.t('Title Auto-Generation')}</div>
<button <button
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition"
...@@ -109,9 +113,9 @@ ...@@ -109,9 +113,9 @@
type="button" type="button"
> >
{#if titleAutoGenerate === true} {#if titleAutoGenerate === true}
<span class="ml-2 self-center">On</span> <span class="ml-2 self-center">{$i18n.t('On')}</span>
{:else} {:else}
<span class="ml-2 self-center">Off</span> <span class="ml-2 self-center">{$i18n.t('Off')}</span>
{/if} {/if}
</button> </button>
</div> </div>
...@@ -119,7 +123,9 @@ ...@@ -119,7 +123,9 @@
<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">Response AutoCopy to Clipboard</div> <div class=" self-center text-xs font-medium">
{$i18n.t('Response AutoCopy to Clipboard')}
</div>
<button <button
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition"
...@@ -129,9 +135,9 @@ ...@@ -129,9 +135,9 @@
type="button" type="button"
> >
{#if responseAutoCopy === true} {#if responseAutoCopy === true}
<span class="ml-2 self-center">On</span> <span class="ml-2 self-center">{$i18n.t('On')}</span>
{:else} {:else}
<span class="ml-2 self-center">Off</span> <span class="ml-2 self-center">{$i18n.t('Off')}</span>
{/if} {/if}
</button> </button>
</div> </div>
...@@ -139,7 +145,7 @@ ...@@ -139,7 +145,7 @@
<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">Full Screen Mode</div> <div class=" self-center text-xs font-medium">{$i18n.t('Full Screen Mode')}</div>
<button <button
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition"
...@@ -149,9 +155,9 @@ ...@@ -149,9 +155,9 @@
type="button" type="button"
> >
{#if fullScreenMode === true} {#if fullScreenMode === true}
<span class="ml-2 self-center">On</span> <span class="ml-2 self-center">{$i18n.t('On')}</span>
{:else} {:else}
<span class="ml-2 self-center">Off</span> <span class="ml-2 self-center">{$i18n.t('Off')}</span>
{/if} {/if}
</button> </button>
</div> </div>
...@@ -160,7 +166,7 @@ ...@@ -160,7 +166,7 @@
<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"> <div class=" self-center text-xs font-medium">
Display the username instead of "You" in the Chat {$i18n.t('Display the username instead of You in the Chat')}
</div> </div>
<button <button
...@@ -171,9 +177,9 @@ ...@@ -171,9 +177,9 @@
type="button" type="button"
> >
{#if showUsername === true} {#if showUsername === true}
<span class="ml-2 self-center">On</span> <span class="ml-2 self-center">{$i18n.t('On')}</span>
{:else} {:else}
<span class="ml-2 self-center">Off</span> <span class="ml-2 self-center">{$i18n.t('Off')}</span>
{/if} {/if}
</button> </button>
</div> </div>
...@@ -183,15 +189,15 @@ ...@@ -183,15 +189,15 @@
<hr class=" dark:border-gray-700" /> <hr class=" dark:border-gray-700" />
<div> <div>
<div class=" mb-2.5 text-sm font-medium">Set Title Auto-Generation Model</div> <div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Title Auto-Generation Model')}</div>
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1 mr-2"> <div class="flex-1 mr-2">
<select <select
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={titleAutoGenerateModel} bind:value={titleAutoGenerateModel}
placeholder="Select a model" placeholder={$i18n.t('Select a model')}
> >
<option value="" selected>Current Model</option> <option value="" selected>{$i18n.t('Current Model')}</option>
{#each $models as model} {#each $models as model}
{#if model.size != null} {#if model.size != null}
<option value={model.name} class="bg-gray-100 dark:bg-gray-700"> <option value={model.name} class="bg-gray-100 dark:bg-gray-700">
...@@ -202,8 +208,9 @@ ...@@ -202,8 +208,9 @@
</select> </select>
</div> </div>
</div> </div>
<div class="mt-3 mr-2"> <div class="mt-3 mr-2">
<div class=" mb-2.5 text-sm font-medium">Title Generation Prompt</div> <div class=" mb-2.5 text-sm font-medium">{$i18n.t('Title Generation Prompt')}</div>
<textarea <textarea
bind:value={titleGenerationPrompt} bind:value={titleGenerationPrompt}
class="w-full rounded-lg p-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none resize-none" class="w-full rounded-lg p-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none resize-none"
...@@ -217,7 +224,9 @@ ...@@ -217,7 +224,9 @@
<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80"> <div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80">
<div class="flex w-full justify-between mb-2"> <div class="flex w-full justify-between mb-2">
<div class=" self-center text-sm font-semibold">Default Prompt Suggestions</div> <div class=" self-center text-sm font-semibold">
{$i18n.t('Default Prompt Suggestions')}
</div>
<button <button
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition"
...@@ -290,19 +299,19 @@ ...@@ -290,19 +299,19 @@
{#if promptSuggestions.length > 0} {#if promptSuggestions.length > 0}
<div class="text-xs text-left w-full mt-2"> <div class="text-xs text-left w-full mt-2">
Adjusting these settings will apply changes universally to all users. {$i18n.t('Adjusting these settings will apply changes universally to all users.')}
</div> </div>
{/if} {/if}
</div> </div>
{/if} {/if}
</div> </div>
<div class="flex justify-end pt-3 text-sm font-medium"> <div class="flex justify-end text-sm font-medium">
<button <button
class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg" class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg"
type="submit" type="submit"
> >
Save {$i18n.t('Save')}
</button> </button>
</div> </div>
</form> </form>
...@@ -12,10 +12,12 @@ ...@@ -12,10 +12,12 @@
import { WEBUI_API_BASE_URL, WEBUI_BASE_URL } from '$lib/constants'; import { WEBUI_API_BASE_URL, WEBUI_BASE_URL } from '$lib/constants';
import { WEBUI_NAME, models, user } from '$lib/stores'; import { WEBUI_NAME, models, user } from '$lib/stores';
import { splitStream } from '$lib/utils'; import { splitStream } from '$lib/utils';
import { onMount } from 'svelte'; import { onMount, getContext } from 'svelte';
import { addLiteLLMModel, deleteLiteLLMModel, getLiteLLMModelInfo } from '$lib/apis/litellm'; import { addLiteLLMModel, deleteLiteLLMModel, getLiteLLMModelInfo } from '$lib/apis/litellm';
import Tooltip from '$lib/components/common/Tooltip.svelte'; import Tooltip from '$lib/components/common/Tooltip.svelte';
const i18n = getContext('i18n');
export let getModels: Function; export let getModels: Function;
let showLiteLLM = false; let showLiteLLM = false;
...@@ -134,11 +136,17 @@ ...@@ -134,11 +136,17 @@
const pullModelHandler = async () => { const pullModelHandler = async () => {
const sanitizedModelTag = modelTag.trim(); const sanitizedModelTag = modelTag.trim();
if (modelDownloadStatus[sanitizedModelTag]) { if (modelDownloadStatus[sanitizedModelTag]) {
toast.error(`Model '${sanitizedModelTag}' is already in queue for downloading.`); toast.error(
$i18n.t(`Model '{{modelTag}}' is already in queue for downloading.`, {
modelTag: sanitizedModelTag
})
);
return; return;
} }
if (Object.keys(modelDownloadStatus).length === 3) { if (Object.keys(modelDownloadStatus).length === 3) {
toast.error('Maximum of 3 models can be downloaded simultaneously. Please try again later.'); toast.error(
$i18n.t('Maximum of 3 models can be downloaded simultaneously. Please try again later.')
);
return; return;
} }
...@@ -156,10 +164,12 @@ ...@@ -156,10 +164,12 @@
if (!data.success) { if (!data.success) {
toast.error(data.error); toast.error(data.error);
} else { } else {
toast.success(`Model '${modelName}' has been successfully downloaded.`); toast.success(
$i18n.t(`Model '{{modelName}}' has been successfully downloaded.`, { modelName })
);
const notification = new Notification($WEBUI_NAME, { const notification = new Notification($WEBUI_NAME, {
body: `Model '${modelName}' has been successfully downloaded.`, body: $i18n.t(`Model '{{modelName}}' has been successfully downloaded.`, { modelName }),
icon: `${WEBUI_BASE_URL}/static/favicon.png` icon: `${WEBUI_BASE_URL}/static/favicon.png`
}); });
...@@ -323,7 +333,7 @@ ...@@ -323,7 +333,7 @@
); );
if (res) { if (res) {
toast.success(`Deleted ${deleteModelTag}`); toast.success($i18n.t(`Deleted {{deleteModelTag}}`, { deleteModelTag }));
} }
deleteModelTag = ''; deleteModelTag = '';
...@@ -410,7 +420,7 @@ ...@@ -410,7 +420,7 @@
} }
} }
} else { } else {
toast.error(`Model ${liteLLMModelName} already exists.`); toast.error($i18n.t(`Model {{modelName}} already exists.`, { modelName: liteLLMModelName }));
} }
liteLLMModelName = ''; liteLLMModelName = '';
...@@ -459,10 +469,10 @@ ...@@ -459,10 +469,10 @@
</script> </script>
<div class="flex flex-col h-full justify-between text-sm"> <div class="flex flex-col h-full justify-between text-sm">
<div class=" space-y-3 pr-1.5 overflow-y-scroll h-[23rem]"> <div class=" space-y-3 pr-1.5 overflow-y-scroll h-[24rem]">
{#if ollamaVersion} {#if ollamaVersion}
<div class="space-y-2 pr-1.5"> <div class="space-y-2 pr-1.5">
<div class="text-sm font-medium">Manage Ollama Models</div> <div class="text-sm font-medium">{$i18n.t('Manage Ollama Models')}</div>
{#if OLLAMA_URLS.length > 0} {#if OLLAMA_URLS.length > 0}
<div class="flex gap-2"> <div class="flex gap-2">
...@@ -470,7 +480,7 @@ ...@@ -470,7 +480,7 @@
<select <select
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={selectedOllamaUrlIdx} bind:value={selectedOllamaUrlIdx}
placeholder="Select an Ollama instance" placeholder={$i18n.t('Select an Ollama instance')}
> >
{#each OLLAMA_URLS as url, idx} {#each OLLAMA_URLS as url, idx}
<option value={idx} class="bg-gray-100 dark:bg-gray-700">{url}</option> <option value={idx} class="bg-gray-100 dark:bg-gray-700">{url}</option>
...@@ -513,12 +523,14 @@ ...@@ -513,12 +523,14 @@
<div class="space-y-2"> <div class="space-y-2">
<div> <div>
<div class=" mb-2 text-sm font-medium">Pull a model from Ollama.com</div> <div class=" mb-2 text-sm font-medium">{$i18n.t('Pull a model from Ollama.com')}</div>
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1 mr-2"> <div class="flex-1 mr-2">
<input <input
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder="Enter model tag (e.g. mistral:7b)" placeholder={$i18n.t('Enter model tag (e.g. {{modelTag}})', {
modelTag: 'mistral:7b'
})}
bind:value={modelTag} bind:value={modelTag}
/> />
</div> </div>
...@@ -573,14 +585,13 @@ ...@@ -573,14 +585,13 @@
</button> </button>
</div> </div>
<div> <div class="mt-2 mb-1 text-xs text-gray-400 dark:text-gray-500">
<div class="mt-2 mb-1 text-xs text-gray-400 dark:text-gray-500"> {$i18n.t('To access the available model names for downloading,')}
To access the available model names for downloading, <a <a
class=" text-gray-500 dark:text-gray-300 font-medium underline" class=" text-gray-500 dark:text-gray-300 font-medium underline"
href="https://ollama.com/library" href="https://ollama.com/library"
target="_blank">click here.</a target="_blank">{$i18n.t('click here.')}</a
> >
</div>
</div> </div>
{#if Object.keys(modelDownloadStatus).length > 0} {#if Object.keys(modelDownloadStatus).length > 0}
...@@ -604,16 +615,16 @@ ...@@ -604,16 +615,16 @@
</div> </div>
<div> <div>
<div class=" mb-2 text-sm font-medium">Delete a model</div> <div class=" mb-2 text-sm font-medium">{$i18n.t('Delete a model')}</div>
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1 mr-2"> <div class="flex-1 mr-2">
<select <select
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={deleteModelTag} bind:value={deleteModelTag}
placeholder="Select a model" placeholder={$i18n.t('Select a model')}
> >
{#if !deleteModelTag} {#if !deleteModelTag}
<option value="" disabled selected>Select a model</option> <option value="" disabled selected>{$i18n.t('Select a model')}</option>
{/if} {/if}
{#each $models.filter((m) => m.size != null && (selectedOllamaUrlIdx === null ? true : (m?.urls ?? []).includes(selectedOllamaUrlIdx))) as model} {#each $models.filter((m) => m.size != null && (selectedOllamaUrlIdx === null ? true : (m?.urls ?? []).includes(selectedOllamaUrlIdx))) as model}
<option value={model.name} class="bg-gray-100 dark:bg-gray-700" <option value={model.name} class="bg-gray-100 dark:bg-gray-700"
...@@ -646,13 +657,13 @@ ...@@ -646,13 +657,13 @@
<div class="pt-1"> <div class="pt-1">
<div class="flex justify-between items-center text-xs"> <div class="flex justify-between items-center text-xs">
<div class=" text-sm font-medium">Experimental</div> <div class=" text-sm font-medium">{$i18n.t('Experimental')}</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={() => {
showExperimentalOllama = !showExperimentalOllama; showExperimentalOllama = !showExperimentalOllama;
}}>{showExperimentalOllama ? 'Hide' : 'Show'}</button }}>{showExperimentalOllama ? $i18n.t('Hide') : $i18n.t('Show')}</button
> >
</div> </div>
</div> </div>
...@@ -664,7 +675,7 @@ ...@@ -664,7 +675,7 @@
}} }}
> >
<div class=" mb-2 flex w-full justify-between"> <div class=" mb-2 flex w-full justify-between">
<div class=" text-sm font-medium">Upload a GGUF model</div> <div class=" text-sm font-medium">{$i18n.t('Upload a GGUF model')}</div>
<button <button
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition"
...@@ -678,9 +689,9 @@ ...@@ -678,9 +689,9 @@
type="button" type="button"
> >
{#if modelUploadMode === 'file'} {#if modelUploadMode === 'file'}
<span class="ml-2 self-center">File Mode</span> <span class="ml-2 self-center">{$i18n.t('File Mode')}</span>
{:else} {:else}
<span class="ml-2 self-center">URL Mode</span> <span class="ml-2 self-center">{$i18n.t('URL Mode')}</span>
{/if} {/if}
</button> </button>
</div> </div>
...@@ -712,7 +723,7 @@ ...@@ -712,7 +723,7 @@
{#if modelInputFile && modelInputFile.length > 0} {#if modelInputFile && modelInputFile.length > 0}
{modelInputFile[0].name} {modelInputFile[0].name}
{:else} {:else}
Click here to select {$i18n.t('Click here to select')}
{/if} {/if}
</button> </button>
</div> </div>
...@@ -726,7 +737,7 @@ ...@@ -726,7 +737,7 @@
type="url" type="url"
required required
bind:value={modelFileUrl} bind:value={modelFileUrl}
placeholder="Type Hugging Face Resolve (Download) URL" placeholder={$i18n.t('Type Hugging Face Resolve (Download) URL')}
/> />
</div> </div>
{/if} {/if}
...@@ -786,7 +797,7 @@ ...@@ -786,7 +797,7 @@
{#if (modelUploadMode === 'file' && modelInputFile && modelInputFile.length > 0) || (modelUploadMode === 'url' && modelFileUrl !== '')} {#if (modelUploadMode === 'file' && modelInputFile && modelInputFile.length > 0) || (modelUploadMode === 'url' && modelFileUrl !== '')}
<div> <div>
<div> <div>
<div class=" my-2.5 text-sm font-medium">Modelfile Content</div> <div class=" my-2.5 text-sm font-medium">{$i18n.t('Modelfile Content')}</div>
<textarea <textarea
bind:value={modelFileContent} bind:value={modelFileContent}
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none resize-none" class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none resize-none"
...@@ -796,16 +807,17 @@ ...@@ -796,16 +807,17 @@
</div> </div>
{/if} {/if}
<div class=" mt-1 text-xs text-gray-400 dark:text-gray-500"> <div class=" mt-1 text-xs text-gray-400 dark:text-gray-500">
To access the GGUF models available for downloading, <a {$i18n.t('To access the GGUF models available for downloading,')}
<a
class=" text-gray-500 dark:text-gray-300 font-medium underline" class=" text-gray-500 dark:text-gray-300 font-medium underline"
href="https://huggingface.co/models?search=gguf" href="https://huggingface.co/models?search=gguf"
target="_blank">click here.</a target="_blank">{$i18n.t('click here.')}</a
> >
</div> </div>
{#if uploadProgress !== null} {#if uploadProgress !== null}
<div class="mt-2"> <div class="mt-2">
<div class=" mb-2 text-xs">Upload Progress</div> <div class=" mb-2 text-xs">{$i18n.t('Upload Progress')}</div>
<div class="w-full rounded-full dark:bg-gray-800"> <div class="w-full rounded-full dark:bg-gray-800">
<div <div
...@@ -832,13 +844,13 @@ ...@@ -832,13 +844,13 @@
<div> <div>
<div class="mb-2"> <div class="mb-2">
<div class="flex justify-between items-center text-xs"> <div class="flex justify-between items-center text-xs">
<div class=" text-sm font-medium">Manage LiteLLM Models</div> <div class=" text-sm font-medium">{$i18n.t('Manage LiteLLM Models')}</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={() => {
showLiteLLM = !showLiteLLM; showLiteLLM = !showLiteLLM;
}}>{showLiteLLM ? 'Hide' : 'Show'}</button }}>{showLiteLLM ? $i18n.t('Hide') : $i18n.t('Show')}</button
> >
</div> </div>
</div> </div>
...@@ -846,14 +858,16 @@ ...@@ -846,14 +858,16 @@
{#if showLiteLLM} {#if showLiteLLM}
<div> <div>
<div class="flex justify-between items-center text-xs"> <div class="flex justify-between items-center text-xs">
<div class=" text-sm font-medium">Add a model</div> <div class=" text-sm font-medium">{$i18n.t('Add a model')}</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={() => {
showLiteLLMParams = !showLiteLLMParams; showLiteLLMParams = !showLiteLLMParams;
}} }}
>{showLiteLLMParams ? 'Hide Additional Params' : 'Show Additional Params'}</button >{showLiteLLMParams
? $i18n.t('Hide Additional Params')
: $i18n.t('Show Additional Params')}</button
> >
</div> </div>
</div> </div>
...@@ -863,7 +877,7 @@ ...@@ -863,7 +877,7 @@
<div class="flex-1 mr-2"> <div class="flex-1 mr-2">
<input <input
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder="Enter LiteLLM Model (litellm_params.model)" placeholder={$i18n.t('Enter LiteLLM Model (litellm_params.model)')}
bind:value={liteLLMModel} bind:value={liteLLMModel}
autocomplete="off" autocomplete="off"
/> />
...@@ -890,7 +904,7 @@ ...@@ -890,7 +904,7 @@
{#if showLiteLLMParams} {#if showLiteLLMParams}
<div> <div>
<div class=" mb-1.5 text-sm font-medium">Model Name</div> <div class=" mb-1.5 text-sm font-medium">{$i18n.t('Model Name')}</div>
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1"> <div class="flex-1">
<input <input
...@@ -904,12 +918,14 @@ ...@@ -904,12 +918,14 @@
</div> </div>
<div> <div>
<div class=" mb-1.5 text-sm font-medium">API Base URL</div> <div class=" mb-1.5 text-sm font-medium">{$i18n.t('API Base URL')}</div>
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1"> <div class="flex-1">
<input <input
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder="Enter LiteLLM API Base URL (litellm_params.api_base)" placeholder={$i18n.t(
'Enter LiteLLM API Base URL (litellm_params.api_base)'
)}
bind:value={liteLLMAPIBase} bind:value={liteLLMAPIBase}
autocomplete="off" autocomplete="off"
/> />
...@@ -918,12 +934,12 @@ ...@@ -918,12 +934,12 @@
</div> </div>
<div> <div>
<div class=" mb-1.5 text-sm font-medium">API Key</div> <div class=" mb-1.5 text-sm font-medium">{$i18n.t('API Key')}</div>
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1"> <div class="flex-1">
<input <input
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder="Enter LiteLLM API Key (litellm_params.api_key)" placeholder={$i18n.t('Enter LiteLLM API Key (litellm_params.api_key)')}
bind:value={liteLLMAPIKey} bind:value={liteLLMAPIKey}
autocomplete="off" autocomplete="off"
/> />
...@@ -932,12 +948,12 @@ ...@@ -932,12 +948,12 @@
</div> </div>
<div> <div>
<div class="mb-1.5 text-sm font-medium">API RPM</div> <div class="mb-1.5 text-sm font-medium">{$i18n.t('API RPM')}</div>
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1"> <div class="flex-1">
<input <input
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder="Enter LiteLLM API RPM (litellm_params.rpm)" placeholder={$i18n.t('Enter LiteLLM API RPM (litellm_params.rpm)')}
bind:value={liteLLMRPM} bind:value={liteLLMRPM}
autocomplete="off" autocomplete="off"
/> />
...@@ -946,12 +962,12 @@ ...@@ -946,12 +962,12 @@
</div> </div>
<div> <div>
<div class="mb-1.5 text-sm font-medium">Max Tokens</div> <div class="mb-1.5 text-sm font-medium">{$i18n.t('Max Tokens')}</div>
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1"> <div class="flex-1">
<input <input
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder="Enter Max Tokens (litellm_params.max_tokens)" placeholder={$i18n.t('Enter Max Tokens (litellm_params.max_tokens)')}
bind:value={liteLLMMaxTokens} bind:value={liteLLMMaxTokens}
type="number" type="number"
min="1" min="1"
...@@ -964,27 +980,27 @@ ...@@ -964,27 +980,27 @@
</div> </div>
<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">
Not sure what to add? {$i18n.t('Not sure what to add?')}
<a <a
class=" text-gray-300 font-medium underline" class=" text-gray-300 font-medium underline"
href="https://litellm.vercel.app/docs/proxy/configs#quick-start" href="https://litellm.vercel.app/docs/proxy/configs#quick-start"
target="_blank" target="_blank"
> >
Click here for help. {$i18n.t('Click here for help.')}
</a> </a>
</div> </div>
<div> <div>
<div class=" mb-2.5 text-sm font-medium">Delete a model</div> <div class=" mb-2.5 text-sm font-medium">{$i18n.t('Delete a model')}</div>
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1 mr-2"> <div class="flex-1 mr-2">
<select <select
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={deleteLiteLLMModelId} bind:value={deleteLiteLLMModelId}
placeholder="Select a model" placeholder={$i18n.t('Select a model')}
> >
{#if !deleteLiteLLMModelId} {#if !deleteLiteLLMModelId}
<option value="" disabled selected>Select a model</option> <option value="" disabled selected>{$i18n.t('Select a model')}</option>
{/if} {/if}
{#each liteLLMModelInfo as model} {#each liteLLMModelInfo as model}
<option value={model.model_info.id} class="bg-gray-100 dark:bg-gray-700" <option value={model.model_info.id} class="bg-gray-100 dark:bg-gray-700"
......
<script lang="ts"> <script lang="ts">
import { getContext } from 'svelte';
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';
import { models, settings, user } from '$lib/stores'; import { models, settings, user } from '$lib/stores';
...@@ -17,6 +18,8 @@ ...@@ -17,6 +18,8 @@
import Connections from './Settings/Connections.svelte'; import Connections from './Settings/Connections.svelte';
import Images from './Settings/Images.svelte'; import Images from './Settings/Images.svelte';
const i18n = getContext('i18n');
export let show = false; export let show = false;
const saveSettings = async (updated) => { const saveSettings = async (updated) => {
...@@ -58,7 +61,7 @@ ...@@ -58,7 +61,7 @@
<Modal bind:show> <Modal bind:show>
<div> <div>
<div class=" flex justify-between dark:text-gray-300 px-5 py-4"> <div class=" flex justify-between dark:text-gray-300 px-5 py-4">
<div class=" text-lg font-medium self-center">Settings</div> <div class=" text-lg font-medium self-center">{$i18n.t('Settings')}</div>
<button <button
class="self-center" class="self-center"
on:click={() => { on:click={() => {
...@@ -106,7 +109,7 @@ ...@@ -106,7 +109,7 @@
/> />
</svg> </svg>
</div> </div>
<div class=" self-center">General</div> <div class=" self-center">{$i18n.t('General')}</div>
</button> </button>
{#if $user?.role === 'admin'} {#if $user?.role === 'admin'}
...@@ -131,7 +134,7 @@ ...@@ -131,7 +134,7 @@
/> />
</svg> </svg>
</div> </div>
<div class=" self-center">Connections</div> <div class=" self-center">{$i18n.t('Connections')}</div>
</button> </button>
<button <button
...@@ -157,7 +160,7 @@ ...@@ -157,7 +160,7 @@
/> />
</svg> </svg>
</div> </div>
<div class=" self-center">Models</div> <div class=" self-center">{$i18n.t('Models')}</div>
</button> </button>
{/if} {/if}
...@@ -184,7 +187,7 @@ ...@@ -184,7 +187,7 @@
/> />
</svg> </svg>
</div> </div>
<div class=" self-center">Interface</div> <div class=" self-center">{$i18n.t('Interface')}</div>
</button> </button>
<button <button
...@@ -211,7 +214,7 @@ ...@@ -211,7 +214,7 @@
/> />
</svg> </svg>
</div> </div>
<div class=" self-center">Audio</div> <div class=" self-center">{$i18n.t('Audio')}</div>
</button> </button>
{#if $user.role === 'admin'} {#if $user.role === 'admin'}
...@@ -238,7 +241,7 @@ ...@@ -238,7 +241,7 @@
/> />
</svg> </svg>
</div> </div>
<div class=" self-center">Images</div> <div class=" self-center">{$i18n.t('Images')}</div>
</button> </button>
{/if} {/if}
...@@ -265,7 +268,7 @@ ...@@ -265,7 +268,7 @@
/> />
</svg> </svg>
</div> </div>
<div class=" self-center">Chats</div> <div class=" self-center">{$i18n.t('Chats')}</div>
</button> </button>
<button <button
...@@ -291,7 +294,7 @@ ...@@ -291,7 +294,7 @@
/> />
</svg> </svg>
</div> </div>
<div class=" self-center">Account</div> <div class=" self-center">{$i18n.t('Account')}</div>
</button> </button>
<button <button
...@@ -317,16 +320,16 @@ ...@@ -317,16 +320,16 @@
/> />
</svg> </svg>
</div> </div>
<div class=" self-center">About</div> <div class=" self-center">{$i18n.t('About')}</div>
</button> </button>
</div> </div>
<div class="flex-1 md:min-h-[380px]"> <div class="flex-1 md:min-h-[25rem]">
{#if selectedTab === 'general'} {#if selectedTab === 'general'}
<General <General
{getModels} {getModels}
{saveSettings} {saveSettings}
on:save={() => { on:save={() => {
toast.success('Settings saved successfully!'); toast.success($i18n.t('Settings saved successfully!'));
}} }}
/> />
{:else if selectedTab === 'models'} {:else if selectedTab === 'models'}
...@@ -335,28 +338,28 @@ ...@@ -335,28 +338,28 @@
<Connections <Connections
{getModels} {getModels}
on:save={() => { on:save={() => {
toast.success('Settings saved successfully!'); toast.success($i18n.t('Settings saved successfully!'));
}} }}
/> />
{:else if selectedTab === 'interface'} {:else if selectedTab === 'interface'}
<Interface <Interface
{saveSettings} {saveSettings}
on:save={() => { on:save={() => {
toast.success('Settings saved successfully!'); toast.success($i18n.t('Settings saved successfully!'));
}} }}
/> />
{:else if selectedTab === 'audio'} {:else if selectedTab === 'audio'}
<Audio <Audio
{saveSettings} {saveSettings}
on:save={() => { on:save={() => {
toast.success('Settings saved successfully!'); toast.success($i18n.t('Settings saved successfully!'));
}} }}
/> />
{:else if selectedTab === 'images'} {:else if selectedTab === 'images'}
<Images <Images
{saveSettings} {saveSettings}
on:save={() => { on:save={() => {
toast.success('Settings saved successfully!'); toast.success($i18n.t('Settings saved successfully!'));
}} }}
/> />
{:else if selectedTab === 'chats'} {:else if selectedTab === 'chats'}
...@@ -364,7 +367,7 @@ ...@@ -364,7 +367,7 @@
{:else if selectedTab === 'account'} {:else if selectedTab === 'account'}
<Account <Account
saveHandler={() => { saveHandler={() => {
toast.success('Settings saved successfully!'); toast.success($i18n.t('Settings saved successfully!'));
}} }}
/> />
{:else if selectedTab === 'about'} {:else if selectedTab === 'about'}
......
<script lang="ts"> <script lang="ts">
import { getContext } from 'svelte';
import Modal from '../common/Modal.svelte'; import Modal from '../common/Modal.svelte';
const i18n = getContext('i18n');
export let downloadChat: Function; export let downloadChat: Function;
export let shareChat: Function; export let shareChat: Function;
...@@ -17,11 +20,11 @@ ...@@ -17,11 +20,11 @@
show = false; show = false;
}} }}
> >
Share to OpenWebUI Community {$i18n.t('Share to OpenWebUI Community')}
</button> </button>
<div class="flex justify-center space-x-1 mt-1.5"> <div class="flex justify-center space-x-1 mt-1.5">
<div class=" self-center text-gray-400 text-xs font-medium">or</div> <div class=" self-center text-gray-400 text-xs font-medium">{$i18n.t('or')}</div>
<button <button
class=" self-center rounded-full text-xs font-medium text-gray-700 dark:text-gray-500 underline" class=" self-center rounded-full text-xs font-medium text-gray-700 dark:text-gray-500 underline"
...@@ -31,7 +34,7 @@ ...@@ -31,7 +34,7 @@
show = false; show = false;
}} }}
> >
Download as a File {$i18n.t('Download as a File')}
</button> </button>
</div> </div>
</div> </div>
......
<script lang="ts"> <script lang="ts">
import { getContext } from 'svelte';
import Modal from '../common/Modal.svelte'; import Modal from '../common/Modal.svelte';
const i18n = getContext('i18n');
export let show = false; export let show = false;
</script> </script>
<Modal bind:show> <Modal bind:show>
<div> <div>
<div class=" flex justify-between dark:text-gray-300 px-5 py-4"> <div class=" flex justify-between dark:text-gray-300 px-5 py-4">
<div class=" text-lg font-medium self-center">Keyboard shortcuts</div> <div class=" text-lg font-medium self-center">{$i18n.t('Keyboard shortcuts')}</div>
<button <button
class="self-center" class="self-center"
on:click={() => { on:click={() => {
...@@ -32,7 +35,7 @@ ...@@ -32,7 +35,7 @@
<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">
<div class="flex flex-col space-y-3 w-full self-start"> <div class="flex flex-col space-y-3 w-full self-start">
<div class="w-full flex justify-between items-center"> <div class="w-full flex justify-between items-center">
<div class=" text-sm">Open new chat</div> <div class=" text-sm">{$i18n.t('Open new chat')}</div>
<div class="flex space-x-1 text-xs"> <div class="flex space-x-1 text-xs">
<div <div
...@@ -56,7 +59,7 @@ ...@@ -56,7 +59,7 @@
</div> </div>
<div class="w-full flex justify-between items-center"> <div class="w-full flex justify-between items-center">
<div class=" text-sm">Focus chat input</div> <div class=" text-sm">{$i18n.t('Focus chat input')}</div>
<div class="flex space-x-1 text-xs"> <div class="flex space-x-1 text-xs">
<div <div
...@@ -74,7 +77,7 @@ ...@@ -74,7 +77,7 @@
</div> </div>
<div class="w-full flex justify-between items-center"> <div class="w-full flex justify-between items-center">
<div class=" text-sm">Copy last code block</div> <div class=" text-sm">{$i18n.t('Copy last code block')}</div>
<div class="flex space-x-1 text-xs"> <div class="flex space-x-1 text-xs">
<div <div
...@@ -98,7 +101,7 @@ ...@@ -98,7 +101,7 @@
</div> </div>
<div class="w-full flex justify-between items-center"> <div class="w-full flex justify-between items-center">
<div class=" text-sm">Copy last response</div> <div class=" text-sm">{$i18n.t('Copy last response')}</div>
<div class="flex space-x-1 text-xs"> <div class="flex space-x-1 text-xs">
<div <div
...@@ -124,7 +127,7 @@ ...@@ -124,7 +127,7 @@
<div class="flex flex-col space-y-3 w-full self-start"> <div class="flex flex-col space-y-3 w-full self-start">
<div class="w-full flex justify-between items-center"> <div class="w-full flex justify-between items-center">
<div class=" text-sm">Toggle settings</div> <div class=" text-sm">{$i18n.t('Toggle settings')}</div>
<div class="flex space-x-1 text-xs"> <div class="flex space-x-1 text-xs">
<div <div
...@@ -141,7 +144,7 @@ ...@@ -141,7 +144,7 @@
</div> </div>
<div class="w-full flex justify-between items-center"> <div class="w-full flex justify-between items-center">
<div class=" text-sm">Toggle sidebar</div> <div class=" text-sm">{$i18n.t('Toggle sidebar')}</div>
<div class="flex space-x-1 text-xs"> <div class="flex space-x-1 text-xs">
<div <div
...@@ -165,7 +168,7 @@ ...@@ -165,7 +168,7 @@
</div> </div>
<div class="w-full flex justify-between items-center"> <div class="w-full flex justify-between items-center">
<div class=" text-sm">Delete chat</div> <div class=" text-sm">{$i18n.t('Delete chat')}</div>
<div class="flex space-x-1 text-xs"> <div class="flex space-x-1 text-xs">
<div <div
...@@ -188,7 +191,7 @@ ...@@ -188,7 +191,7 @@
</div> </div>
<div class="w-full flex justify-between items-center"> <div class="w-full flex justify-between items-center">
<div class=" text-sm">Show shortcuts</div> <div class=" text-sm">{$i18n.t('Show shortcuts')}</div>
<div class="flex space-x-1 text-xs"> <div class="flex space-x-1 text-xs">
<div <div
......
<script lang="ts">
import { DropdownMenu } from 'bits-ui';
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
</script>
<DropdownMenu.Root
onOpenChange={(state) => {
dispatch('change', state);
}}
>
<DropdownMenu.Trigger>
<slot />
</DropdownMenu.Trigger>
<slot name="content">
<DropdownMenu.Content
class="w-full max-w-[130px] rounded-lg px-1 py-1.5 border border-gray-700 z-50 bg-gray-850 text-white"
sideOffset={8}
side="bottom"
align="start"
>
<DropdownMenu.Item class="flex items-center px-3 py-2 text-sm font-medium">
<div class="flex items-center">Profile</div>
</DropdownMenu.Item>
<DropdownMenu.Item class="flex items-center px-3 py-2 text-sm font-medium">
<div class="flex items-center">Profile</div>
</DropdownMenu.Item>
<DropdownMenu.Item class="flex items-center px-3 py-2 text-sm font-medium">
<div class="flex items-center">Profile</div>
</DropdownMenu.Item>
</DropdownMenu.Content>
</slot>
</DropdownMenu.Root>
<script lang="ts"> <script lang="ts">
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher, getContext } from 'svelte';
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
const i18n = getContext('i18n');
let showTagInput = false; let showTagInput = false;
let tagName = ''; let tagName = '';
</script> </script>
...@@ -9,12 +11,6 @@ ...@@ -9,12 +11,6 @@
<div class="flex space-x-1 pl-1.5"> <div class="flex space-x-1 pl-1.5">
{#if showTagInput} {#if showTagInput}
<div class="flex items-center"> <div class="flex items-center">
<input
bind:value={tagName}
class=" cursor-pointer self-center text-xs h-fit bg-transparent outline-none line-clamp-1 w-[4rem]"
placeholder="Add a tag"
/>
<button <button
type="button" type="button"
on:click={() => { on:click={() => {
...@@ -36,6 +32,11 @@ ...@@ -36,6 +32,11 @@
/> />
</svg> </svg>
</button> </button>
<input
bind:value={tagName}
class=" pl-2 cursor-pointer self-center text-xs h-fit bg-transparent outline-none line-clamp-1 w-[8rem]"
placeholder={$i18n.t('Add a tag')}
/>
</div> </div>
<!-- TODO: Tag Suggestions --> <!-- TODO: Tag Suggestions -->
......
<script lang="ts"> <script lang="ts">
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { onMount } from 'svelte'; import { onMount, getContext } from 'svelte';
import { createNewDoc, getDocs, tagDocByName, updateDocByName } from '$lib/apis/documents'; import { createNewDoc, getDocs, tagDocByName, updateDocByName } from '$lib/apis/documents';
import Modal from '../common/Modal.svelte'; import Modal from '../common/Modal.svelte';
...@@ -13,6 +13,8 @@ ...@@ -13,6 +13,8 @@
import { transformFileName } from '$lib/utils'; import { transformFileName } from '$lib/utils';
import { SUPPORTED_FILE_EXTENSIONS, SUPPORTED_FILE_TYPE } from '$lib/constants'; import { SUPPORTED_FILE_EXTENSIONS, SUPPORTED_FILE_TYPE } from '$lib/constants';
const i18n = getContext('i18n');
export let show = false; export let show = false;
export let selectedDoc; export let selectedDoc;
let uploadDocInputElement: HTMLInputElement; let uploadDocInputElement: HTMLInputElement;
...@@ -71,7 +73,7 @@ ...@@ -71,7 +73,7 @@
inputFiles = null; inputFiles = null;
uploadDocInputElement.value = ''; uploadDocInputElement.value = '';
} else { } else {
toast.error(`File not found.`); toast.error($i18n.t(`File not found.`));
} }
show = false; show = false;
...@@ -96,7 +98,7 @@ ...@@ -96,7 +98,7 @@
<Modal size="sm" bind:show> <Modal size="sm" bind:show>
<div> <div>
<div class=" flex justify-between dark:text-gray-300 px-5 py-4"> <div class=" flex justify-between dark:text-gray-300 px-5 py-4">
<div class=" text-lg font-medium self-center">Add Docs</div> <div class=" text-lg font-medium self-center">{$i18n.t('Add Docs')}</div>
<button <button
class="self-center" class="self-center"
on:click={() => { on:click={() => {
...@@ -145,14 +147,14 @@ ...@@ -145,14 +147,14 @@
{#if inputFiles} {#if inputFiles}
{inputFiles.length > 0 ? `${inputFiles.length}` : ''} document(s) selected. {inputFiles.length > 0 ? `${inputFiles.length}` : ''} document(s) selected.
{:else} {:else}
Click here to select documents. {$i18n.t('Click here to select documents.')}
{/if} {/if}
</button> </button>
</div> </div>
<div class=" flex flex-col space-y-1.5"> <div class=" flex flex-col space-y-1.5">
<div class="flex flex-col w-full"> <div class="flex flex-col w-full">
<div class=" mb-1.5 text-xs text-gray-500">Tags</div> <div class=" mb-1.5 text-xs text-gray-500">{$i18n.t('Tags')}</div>
<Tags {tags} addTag={addTagHandler} deleteTag={deleteTagHandler} /> <Tags {tags} addTag={addTagHandler} deleteTag={deleteTagHandler} />
</div> </div>
...@@ -163,7 +165,7 @@ ...@@ -163,7 +165,7 @@
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded" class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
type="submit" type="submit"
> >
Save {$i18n.t('Save')}
</button> </button>
</div> </div>
</form> </form>
......
<script lang="ts"> <script lang="ts">
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { onMount } from 'svelte'; import { onMount, getContext } from 'svelte';
import { getDocs, tagDocByName, updateDocByName } from '$lib/apis/documents'; import { getDocs, tagDocByName, updateDocByName } from '$lib/apis/documents';
import Modal from '../common/Modal.svelte'; import Modal from '../common/Modal.svelte';
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
import Tags from '../common/Tags.svelte'; import Tags from '../common/Tags.svelte';
import { addTagById } from '$lib/apis/chats'; import { addTagById } from '$lib/apis/chats';
const i18n = getContext('i18n');
export let show = false; export let show = false;
export let selectedDoc; export let selectedDoc;
...@@ -74,7 +76,7 @@ ...@@ -74,7 +76,7 @@
<Modal size="sm" bind:show> <Modal size="sm" bind:show>
<div> <div>
<div class=" flex justify-between dark:text-gray-300 px-5 py-4"> <div class=" flex justify-between dark:text-gray-300 px-5 py-4">
<div class=" text-lg font-medium self-center">Edit Doc</div> <div class=" text-lg font-medium self-center">{$i18n.t('Edit Doc')}</div>
<button <button
class="self-center" class="self-center"
on:click={() => { on:click={() => {
...@@ -105,7 +107,7 @@ ...@@ -105,7 +107,7 @@
> >
<div class=" flex flex-col space-y-1.5"> <div class=" flex flex-col space-y-1.5">
<div class="flex flex-col w-full"> <div class="flex flex-col w-full">
<div class=" mb-1 text-xs text-gray-500">Name Tag</div> <div class=" mb-1 text-xs text-gray-500">{$i18n.t('Name Tag')}</div>
<div class="flex flex-1"> <div class="flex flex-1">
<div <div
...@@ -134,7 +136,7 @@ ...@@ -134,7 +136,7 @@
</div> </div>
<div class="flex flex-col w-full"> <div class="flex flex-col w-full">
<div class=" mb-1 text-xs text-gray-500">Title</div> <div class=" mb-1 text-xs text-gray-500">{$i18n.t('Title')}</div>
<div class="flex-1"> <div class="flex-1">
<input <input
...@@ -148,7 +150,7 @@ ...@@ -148,7 +150,7 @@
</div> </div>
<div class="flex flex-col w-full"> <div class="flex flex-col w-full">
<div class=" mb-1.5 text-xs text-gray-500">Tags</div> <div class=" mb-1.5 text-xs text-gray-500">{$i18n.t('Tags')}</div>
<Tags {tags} addTag={addTagHandler} deleteTag={deleteTagHandler} /> <Tags {tags} addTag={addTagHandler} deleteTag={deleteTagHandler} />
</div> </div>
...@@ -159,7 +161,7 @@ ...@@ -159,7 +161,7 @@
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded" class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
type="submit" type="submit"
> >
Save {$i18n.t('Save')}
</button> </button>
</div> </div>
</form> </form>
......
...@@ -8,9 +8,11 @@ ...@@ -8,9 +8,11 @@
updateQuerySettings updateQuerySettings
} from '$lib/apis/rag'; } from '$lib/apis/rag';
import { documents } from '$lib/stores'; import { documents } from '$lib/stores';
import { onMount } from 'svelte'; import { onMount, getContext } from 'svelte';
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';
const i18n = getContext('i18n');
export let saveHandler: Function; export let saveHandler: Function;
let loading = false; let loading = false;
...@@ -31,7 +33,7 @@ ...@@ -31,7 +33,7 @@
if (res) { if (res) {
await documents.set(await getDocs(localStorage.token)); await documents.set(await getDocs(localStorage.token));
toast.success('Scan complete!'); toast.success($i18n.t('Scan complete!'));
} }
}; };
...@@ -69,10 +71,12 @@ ...@@ -69,10 +71,12 @@
> >
<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80"> <div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80">
<div> <div>
<div class=" mb-2 text-sm font-medium">General Settings</div> <div class=" mb-2 text-sm font-medium">{$i18n.t('General Settings')}</div>
<div class=" flex w-full justify-between"> <div class=" flex w-full justify-between">
<div class=" self-center text-xs font-medium">Scan for documents from '/data/docs'</div> <div class=" self-center text-xs font-medium">
{$i18n.t('Scan for documents from {{path}}', { path: '/data/docs' })}
</div>
<button <button
class=" self-center text-xs p-1 px-3 bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 rounded flex flex-row space-x-1 items-center {loading class=" self-center text-xs p-1 px-3 bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 rounded flex flex-row space-x-1 items-center {loading
...@@ -85,7 +89,7 @@ ...@@ -85,7 +89,7 @@
type="button" type="button"
disabled={loading} disabled={loading}
> >
<div class="self-center font-medium">Scan</div> <div class="self-center font-medium">{$i18n.t('Scan')}</div>
<!-- <svg <!-- <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
...@@ -133,77 +137,76 @@ ...@@ -133,77 +137,76 @@
<hr class=" dark:border-gray-700" /> <hr class=" dark:border-gray-700" />
<div class=" space-y-3"> <div class=" ">
<div class=" space-y-3"> <div class=" text-sm font-medium">{$i18n.t('Chunk Params')}</div>
<div class=" text-sm font-medium">Chunk Params</div>
<div class=" flex">
<div class=" flex gap-2"> <div class=" flex w-full justify-between">
<div class=" flex w-full justify-between gap-2"> <div class="self-center text-xs font-medium min-w-fit">{$i18n.t('Chunk Size')}</div>
<div class="self-center text-xs font-medium min-w-fit">Chunk Size</div>
<div class="self-center p-3">
<div class="self-center"> <input
<input class=" w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
class=" w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600" type="number"
type="number" placeholder={$i18n.t('Enter Chunk Size')}
placeholder="Enter Chunk Size" bind:value={chunkSize}
bind:value={chunkSize} autocomplete="off"
autocomplete="off" min="0"
min="0" />
/>
</div>
</div>
<div class="flex w-full gap-2">
<div class=" self-center text-xs font-medium min-w-fit">Chunk Overlap</div>
<div class="self-center">
<input
class="w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
type="number"
placeholder="Enter Chunk Overlap"
bind:value={chunkOverlap}
autocomplete="off"
min="0"
/>
</div>
</div> </div>
</div> </div>
<div> <div class="flex w-full">
<div class="flex justify-between items-center text-xs"> <div class=" self-center text-xs font-medium min-w-fit">{$i18n.t('Chunk Overlap')}</div>
<div class=" text-xs font-medium">PDF Extract Images (OCR)</div>
<div class="self-center p-3">
<button <input
class=" text-xs font-medium text-gray-500" class="w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
type="button" type="number"
on:click={() => { placeholder={$i18n.t('Enter Chunk Overlap')}
pdfExtractImages = !pdfExtractImages; bind:value={chunkOverlap}
}}>{pdfExtractImages ? 'On' : 'Off'}</button autocomplete="off"
> min="0"
/>
</div> </div>
</div> </div>
</div> </div>
<div> <div>
<div class=" text-sm font-medium">Query Params</div> <div class="flex justify-between items-center text-xs">
<div class=" text-xs font-medium">{$i18n.t('PDF Extract Images (OCR)')}</div>
<div class=" flex py-2">
<div class=" flex w-full justify-between gap-2"> <button
<div class="self-center text-xs font-medium flex-1">Top K</div> class=" text-xs font-medium text-gray-500"
type="button"
on:click={() => {
pdfExtractImages = !pdfExtractImages;
}}>{pdfExtractImages ? $i18n.t('On') : $i18n.t('Off')}</button
>
</div>
</div>
</div>
<div class="self-center"> <div>
<input <div class=" text-sm font-medium">{$i18n.t('Query Params')}</div>
class=" w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
type="number" <div class=" flex">
placeholder="Enter Top K" <div class=" flex w-full justify-between">
bind:value={querySettings.k} <div class="self-center text-xs font-medium flex-1">{$i18n.t('Top K')}</div>
autocomplete="off"
min="0" <div class="self-center p-3">
/> <input
</div> class=" w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
type="number"
placeholder={$i18n.t('Enter Top K')}
bind:value={querySettings.k}
autocomplete="off"
min="0"
/>
</div> </div>
</div>
<!-- <div class="flex w-full"> <!-- <div class="flex w-full">
<div class=" self-center text-xs font-medium min-w-fit">Chunk Overlap</div> <div class=" self-center text-xs font-medium min-w-fit">Chunk Overlap</div>
<div class="self-center p-3"> <div class="self-center p-3">
...@@ -217,16 +220,15 @@ ...@@ -217,16 +220,15 @@
/> />
</div> </div>
</div> --> </div> -->
</div> </div>
<div> <div>
<div class=" mb-2.5 text-sm font-medium">RAG Template</div> <div class=" mb-2.5 text-sm font-medium">{$i18n.t('RAG Template')}</div>
<textarea <textarea
bind:value={querySettings.template} bind:value={querySettings.template}
class="w-full rounded p-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none resize-none" class="w-full rounded p-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none resize-none"
rows="4" rows="4"
/> />
</div>
</div> </div>
</div> </div>
</div> </div>
...@@ -236,7 +238,7 @@ ...@@ -236,7 +238,7 @@
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded" class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
type="submit" type="submit"
> >
Save {$i18n.t('Save')}
</button> </button>
</div> </div>
</form> </form>
<script> <script>
import { getContext } from 'svelte';
import Modal from '../common/Modal.svelte'; import Modal from '../common/Modal.svelte';
import General from './Settings/General.svelte'; import General from './Settings/General.svelte';
const i18n = getContext('i18n');
export let show = false; export let show = false;
let selectedTab = 'general'; let selectedTab = 'general';
...@@ -10,7 +13,7 @@ ...@@ -10,7 +13,7 @@
<Modal bind:show> <Modal bind:show>
<div> <div>
<div class=" flex justify-between dark:text-gray-300 px-5 py-4"> <div class=" flex justify-between dark:text-gray-300 px-5 py-4">
<div class=" text-lg font-medium self-center">Document Settings</div> <div class=" text-lg font-medium self-center">{$i18n.t('Document Settings')}</div>
<button <button
class="self-center" class="self-center"
on:click={() => { on:click={() => {
...@@ -58,7 +61,7 @@ ...@@ -58,7 +61,7 @@
/> />
</svg> </svg>
</div> </div>
<div class=" self-center">General</div> <div class=" self-center">{$i18n.t('General')}</div>
</button> </button>
</div> </div>
<div class="flex-1 md:min-h-[380px]"> <div class="flex-1 md:min-h-[380px]">
......
<script lang="ts">
export let className = 'w-4 h-4';
export let strokeWidth = '1.5';
</script>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width={strokeWidth}
stroke="currentColor"
class={className}
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0"
/>
</svg>
<script lang="ts">
export let className = 'w-4 h-4';
export let strokeWidth = '1.5';
</script>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width={strokeWidth}
stroke="currentColor"
class={className}
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
/>
</svg>
<script lang="ts"> <script lang="ts">
import { getContext } from 'svelte';
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';
import fileSaver from 'file-saver'; import fileSaver from 'file-saver';
const { saveAs } = fileSaver; const { saveAs } = fileSaver;
...@@ -9,6 +10,8 @@ ...@@ -9,6 +10,8 @@
import TagInput from '../common/Tags/TagInput.svelte'; import TagInput from '../common/Tags/TagInput.svelte';
import Tags from '../common/Tags.svelte'; import Tags from '../common/Tags.svelte';
const i18n = getContext('i18n');
export let initNewChat: Function; export let initNewChat: Function;
export let title: string = $WEBUI_NAME; export let title: string = $WEBUI_NAME;
export let shareEnabled: boolean = false; export let shareEnabled: boolean = false;
...@@ -26,7 +29,7 @@ ...@@ -26,7 +29,7 @@
const chat = (await getChatById(localStorage.token, $chatId)).chat; const chat = (await getChatById(localStorage.token, $chatId)).chat;
console.log('share', chat); console.log('share', chat);
toast.success('Redirecting you to OpenWebUI Community'); toast.success($i18n.t('Redirecting you to OpenWebUI Community'));
const url = 'https://openwebui.com'; const url = 'https://openwebui.com';
// const url = 'http://localhost:5173'; // const url = 'http://localhost:5173';
......
...@@ -7,7 +7,10 @@ ...@@ -7,7 +7,10 @@
import { goto, invalidateAll } from '$app/navigation'; import { goto, invalidateAll } from '$app/navigation';
import { page } from '$app/stores'; import { page } from '$app/stores';
import { user, chats, settings, showSettings, chatId, tags } from '$lib/stores'; import { user, chats, settings, showSettings, chatId, tags } from '$lib/stores';
import { onMount } from 'svelte'; import { onMount, getContext } from 'svelte';
const i18n = getContext('i18n');
import { import {
deleteChatById, deleteChatById,
getChatList, getChatList,
...@@ -20,6 +23,8 @@ ...@@ -20,6 +23,8 @@
import { slide } from 'svelte/transition'; import { slide } from 'svelte/transition';
import { WEBUI_BASE_URL } from '$lib/constants'; import { WEBUI_BASE_URL } from '$lib/constants';
import Tooltip from '../common/Tooltip.svelte'; import Tooltip from '../common/Tooltip.svelte';
import Dropdown from '../common/Dropdown.svelte';
import ChatMenu from './Sidebar/ChatMenu.svelte';
let show = false; let show = false;
let navElement; let navElement;
...@@ -27,6 +32,8 @@ ...@@ -27,6 +32,8 @@
let title: string = 'UI'; let title: string = 'UI';
let search = ''; let search = '';
let selectedChatId = null;
let chatDeleteId = null; let chatDeleteId = null;
let chatTitleEditId = null; let chatTitleEditId = null;
let chatTitle = ''; let chatTitle = '';
...@@ -82,7 +89,10 @@ ...@@ -82,7 +89,10 @@
}); });
if (res) { if (res) {
goto('/'); if ($chatId === id) {
goto('/');
}
await chats.set(await getChatList(localStorage.token)); await chats.set(await getChatList(localStorage.token));
} }
}; };
...@@ -112,6 +122,8 @@ ...@@ -112,6 +122,8 @@
class="flex-grow flex justify-between rounded-xl px-3.5 py-2 hover:bg-gray-900 transition" class="flex-grow flex justify-between rounded-xl px-3.5 py-2 hover:bg-gray-900 transition"
href="/" href="/"
on:click={async () => { on:click={async () => {
selectedChatId = null;
await goto('/'); await goto('/');
const newChatButton = document.getElementById('new-chat-button'); const newChatButton = document.getElementById('new-chat-button');
setTimeout(() => { setTimeout(() => {
...@@ -128,7 +140,7 @@ ...@@ -128,7 +140,7 @@
/> />
</div> </div>
<div class=" self-center font-medium text-sm">New Chat</div> <div class=" self-center font-medium text-sm">{$i18n.t('New Chat')}</div>
</div> </div>
<div class="self-center"> <div class="self-center">
...@@ -154,6 +166,10 @@ ...@@ -154,6 +166,10 @@
<a <a
class="flex-grow flex space-x-3 rounded-xl px-3.5 py-2 hover:bg-gray-900 transition" class="flex-grow flex space-x-3 rounded-xl px-3.5 py-2 hover:bg-gray-900 transition"
href="/modelfiles" href="/modelfiles"
on:click={() => {
selectedChatId = null;
chatId.set('');
}}
> >
<div class="self-center"> <div class="self-center">
<svg <svg
...@@ -173,7 +189,7 @@ ...@@ -173,7 +189,7 @@
</div> </div>
<div class="flex self-center"> <div class="flex self-center">
<div class=" self-center font-medium text-sm">Modelfiles</div> <div class=" self-center font-medium text-sm">{$i18n.t('Modelfiles')}</div>
</div> </div>
</a> </a>
</div> </div>
...@@ -182,6 +198,10 @@ ...@@ -182,6 +198,10 @@
<a <a
class="flex-grow flex space-x-3 rounded-xl px-3.5 py-2 hover:bg-gray-900 transition" class="flex-grow flex space-x-3 rounded-xl px-3.5 py-2 hover:bg-gray-900 transition"
href="/prompts" href="/prompts"
on:click={() => {
selectedChatId = null;
chatId.set('');
}}
> >
<div class="self-center"> <div class="self-center">
<svg <svg
...@@ -201,7 +221,7 @@ ...@@ -201,7 +221,7 @@
</div> </div>
<div class="flex self-center"> <div class="flex self-center">
<div class=" self-center font-medium text-sm">Prompts</div> <div class=" self-center font-medium text-sm">{$i18n.t('Prompts')}</div>
</div> </div>
</a> </a>
</div> </div>
...@@ -210,6 +230,10 @@ ...@@ -210,6 +230,10 @@
<a <a
class="flex-grow flex space-x-3 rounded-xl px-3.5 py-2 hover:bg-gray-900 transition" class="flex-grow flex space-x-3 rounded-xl px-3.5 py-2 hover:bg-gray-900 transition"
href="/documents" href="/documents"
on:click={() => {
selectedChatId = null;
chatId.set('');
}}
> >
<div class="self-center"> <div class="self-center">
<svg <svg
...@@ -229,7 +253,7 @@ ...@@ -229,7 +253,7 @@
</div> </div>
<div class="flex self-center"> <div class="flex self-center">
<div class=" self-center font-medium text-sm">Documents</div> <div class=" self-center font-medium text-sm">{$i18n.t('Documents')}</div>
</div> </div>
</a> </a>
</div> </div>
...@@ -239,11 +263,13 @@ ...@@ -239,11 +263,13 @@
{#if !($settings.saveChatHistory ?? true)} {#if !($settings.saveChatHistory ?? true)}
<div class="absolute z-40 w-full h-full bg-black/90 flex justify-center"> <div class="absolute z-40 w-full h-full bg-black/90 flex justify-center">
<div class=" text-left px-5 py-2"> <div class=" text-left px-5 py-2">
<div class=" font-medium">Chat History is off for this browser.</div> <div class=" font-medium">{$i18n.t('Chat History is off for this browser.')}</div>
<div class="text-xs mt-2"> <div class="text-xs mt-2">
When history is turned off, new chats on this browser won't appear in your history on {$i18n.t(
any of your devices. <span class=" font-semibold" "When history is turned off, new chats on this browser won't appear in your history on any of your devices."
>This setting does not sync across browsers or devices.</span )}
<span class=" font-semibold"
>{$i18n.t('This setting does not sync across browsers or devices.')}</span
> >
</div> </div>
...@@ -270,7 +296,7 @@ ...@@ -270,7 +296,7 @@
/> />
</svg> </svg>
<div>Enable Chat History</div> <div>{$i18n.t('Enable Chat History')}</div>
</button> </button>
</div> </div>
</div> </div>
...@@ -296,29 +322,12 @@ ...@@ -296,29 +322,12 @@
<input <input
class="w-full rounded-r-xl py-1.5 pl-2.5 pr-4 text-sm text-gray-300 bg-gray-950 outline-none" class="w-full rounded-r-xl py-1.5 pl-2.5 pr-4 text-sm text-gray-300 bg-gray-950 outline-none"
placeholder="Search" placeholder={$i18n.t('Search')}
bind:value={search} bind:value={search}
on:focus={() => { on:focus={() => {
enrichChatsWithContent($chats); enrichChatsWithContent($chats);
}} }}
/> />
<!-- <div class="self-center pr-3 py-2 bg-gray-900">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-4 h-4"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M12 3c2.755 0 5.455.232 8.083.678.533.09.917.556.917 1.096v1.044a2.25 2.25 0 01-.659 1.591l-5.432 5.432a2.25 2.25 0 00-.659 1.591v2.927a2.25 2.25 0 01-1.244 2.013L9.75 21v-6.568a2.25 2.25 0 00-.659-1.591L3.659 7.409A2.25 2.25 0 013 5.818V4.774c0-.54.384-1.006.917-1.096A48.32 48.32 0 0112 3z"
/>
</svg>
</div> -->
</div> </div>
</div> </div>
...@@ -370,24 +379,31 @@ ...@@ -370,24 +379,31 @@
return title.includes(query) || contentMatches; return title.includes(query) || contentMatches;
} }
}) as chat, i} }) as chat, i}
<div class=" w-full pr-2 relative"> <div class=" w-full pr-2 relative group">
{#if chatTitleEditId === chat.id} {#if chatTitleEditId === chat.id}
<div <div
class=" w-full flex justify-between rounded-xl px-3 py-2 hover:bg-gray-900 {chat.id === class=" w-full flex justify-between rounded-xl px-3 py-2 {chat.id === $chatId ||
$chatId chat.id === chatTitleEditId ||
chat.id === chatDeleteId
? 'bg-gray-900' ? 'bg-gray-900'
: ''} transition whitespace-nowrap text-ellipsis" : chat.id === selectedChatId
? 'bg-gray-950'
: 'group-hover:bg-gray-950'} whitespace-nowrap text-ellipsis"
> >
<input bind:value={chatTitle} class=" bg-transparent w-full outline-none mr-10" /> <input bind:value={chatTitle} class=" bg-transparent w-full outline-none mr-10" />
</div> </div>
{:else} {:else}
<a <a
class=" w-full flex justify-between rounded-xl px-3 py-2 hover:bg-gray-900 {chat.id === class=" w-full flex justify-between rounded-xl px-3 py-2 {chat.id === $chatId ||
$chatId chat.id === chatTitleEditId ||
chat.id === chatDeleteId
? 'bg-gray-900' ? 'bg-gray-900'
: ''} transition whitespace-nowrap text-ellipsis" : chat.id === selectedChatId
? 'bg-gray-950'
: 'group-hover:bg-gray-950'} whitespace-nowrap text-ellipsis"
href="/c/{chat.id}" href="/c/{chat.id}"
on:click={() => { on:click={() => {
selectedChatId = chat.id;
if (window.innerWidth < 1024) { if (window.innerWidth < 1024) {
show = false; show = false;
} }
...@@ -395,156 +411,142 @@ ...@@ -395,156 +411,142 @@
draggable="false" draggable="false"
> >
<div class=" flex self-center flex-1 w-full"> <div class=" flex self-center flex-1 w-full">
<div <div class=" text-left self-center overflow-hidden w-full h-[20px]">
class=" text-left self-center overflow-hidden {chat.id === $chatId
? 'w-[160px]'
: 'w-full'} h-[20px]"
>
{chat.title} {chat.title}
</div> </div>
</div> </div>
</a> </a>
{/if} {/if}
{#if chat.id === $chatId} <div
<div class=" absolute right-[22px] top-[10px]"> class="
{#if chatTitleEditId === chat.id}
<div class="flex self-center space-x-1.5"> {chat.id === $chatId || chat.id === chatTitleEditId || chat.id === chatDeleteId
<button ? ' from-gray-900'
class=" self-center hover:text-white transition" : chat.id === selectedChatId
on:click={() => { ? 'from-gray-950'
editChatTitle(chat.id, chatTitle); : 'invisible group-hover:visible from-gray-950'}
chatTitleEditId = null; absolute right-[10px] top-[10px] pr-2 pl-5 bg-gradient-to-l from-80%
chatTitle = '';
}} to-transparent"
> >
<svg {#if chatTitleEditId === chat.id}
xmlns="http://www.w3.org/2000/svg" <div class="flex self-center space-x-1.5 z-10">
viewBox="0 0 20 20" <button
fill="currentColor" class=" self-center hover:text-white transition"
class="w-4 h-4" on:click={() => {
> editChatTitle(chat.id, chatTitle);
<path chatTitleEditId = null;
fill-rule="evenodd" chatTitle = '';
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z" }}
clip-rule="evenodd" >
/> <svg
</svg> xmlns="http://www.w3.org/2000/svg"
</button> viewBox="0 0 20 20"
<button fill="currentColor"
class=" self-center hover:text-white transition" class="w-4 h-4"
on:click={() => {
chatTitleEditId = null;
chatTitle = '';
}}
> >
<svg <path
xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd"
viewBox="0 0 20 20" d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
fill="currentColor" clip-rule="evenodd"
class="w-4 h-4" />
> </svg>
<path </button>
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" <button
/> class=" self-center hover:text-white transition"
</svg> on:click={() => {
</button> chatTitleEditId = null;
</div> chatTitle = '';
{:else if chatDeleteId === chat.id} }}
<div class="flex self-center space-x-1.5"> >
<button <svg
class=" self-center hover:text-white transition" xmlns="http://www.w3.org/2000/svg"
on:click={() => { viewBox="0 0 20 20"
deleteChat(chat.id); fill="currentColor"
}} class="w-4 h-4"
> >
<svg <path
xmlns="http://www.w3.org/2000/svg" d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
viewBox="0 0 20 20" />
fill="currentColor" </svg>
class="w-4 h-4" </button>
> </div>
<path {:else if chatDeleteId === chat.id}
fill-rule="evenodd" <div class="flex self-center space-x-1.5 z-10">
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z" <button
clip-rule="evenodd" class=" self-center hover:text-white transition"
/> on:click={() => {
</svg> deleteChat(chat.id);
</button> }}
<button >
class=" self-center hover:text-white transition" <svg
on:click={() => { xmlns="http://www.w3.org/2000/svg"
chatDeleteId = null; viewBox="0 0 20 20"
}} fill="currentColor"
class="w-4 h-4"
> >
<svg <path
xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd"
viewBox="0 0 20 20" d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
fill="currentColor" clip-rule="evenodd"
class="w-4 h-4" />
> </svg>
<path </button>
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" <button
/> class=" self-center hover:text-white transition"
</svg> on:click={() => {
</button> chatDeleteId = null;
</div> }}
{:else} >
<div class="flex self-center space-x-1.5"> <svg
<button xmlns="http://www.w3.org/2000/svg"
id="delete-chat-button" viewBox="0 0 20 20"
class=" hidden" fill="currentColor"
on:click={() => { class="w-4 h-4"
deleteChat(chat.id);
}}
/>
<button
class=" self-center hover:text-white transition"
on:click={() => {
chatTitle = chat.title;
chatTitleEditId = chat.id;
}}
> >
<svg <path
xmlns="http://www.w3.org/2000/svg" d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
fill="none" />
viewBox="0 0 24 24" </svg>
stroke-width="1.5" </button>
stroke="currentColor" </div>
class="w-4 h-4" {:else}
> <div class="flex self-center space-x-1.5 z-10">
<path <ChatMenu
stroke-linecap="round" renameHandler={() => {
stroke-linejoin="round" chatTitle = chat.title;
d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125" chatTitleEditId = chat.id;
/> }}
</svg> deleteHandler={() => {
</button> chatDeleteId = chat.id;
}}
onClose={() => {
selectedChatId = null;
}}
>
<button <button
aria-label="Chat Menu"
class=" self-center hover:text-white transition" class=" self-center hover:text-white transition"
on:click={() => { on:click={() => {
chatDeleteId = chat.id; selectedChatId = chat.id;
}} }}
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
fill="none" viewBox="0 0 16 16"
viewBox="0 0 24 24" fill="currentColor"
stroke-width="1.5"
stroke="currentColor"
class="w-4 h-4" class="w-4 h-4"
> >
<path <path
stroke-linecap="round" d="M2 8a1.5 1.5 0 1 1 3 0 1.5 1.5 0 0 1-3 0ZM6.5 8a1.5 1.5 0 1 1 3 0 1.5 1.5 0 0 1-3 0ZM12.5 6.5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Z"
stroke-linejoin="round"
d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0"
/> />
</svg> </svg>
</button> </button>
</div> </ChatMenu>
{/if} </div>
</div> {/if}
{/if} </div>
</div> </div>
{/each} {/each}
</div> </div>
...@@ -602,7 +604,7 @@ ...@@ -602,7 +604,7 @@
/> />
</svg> </svg>
</div> </div>
<div class=" self-center font-medium">Admin Panel</div> <div class=" self-center font-medium">{$i18n.t('Admin Panel')}</div>
</button> </button>
<button <button
...@@ -628,7 +630,7 @@ ...@@ -628,7 +630,7 @@
/> />
</svg> </svg>
</div> </div>
<div class=" self-center font-medium">Playground</div> <div class=" self-center font-medium">{$i18n.t('Playground')}</div>
</button> </button>
{/if} {/if}
...@@ -660,7 +662,7 @@ ...@@ -660,7 +662,7 @@
/> />
</svg> </svg>
</div> </div>
<div class=" self-center font-medium">Settings</div> <div class=" self-center font-medium">{$i18n.t('Settings')}</div>
</button> </button>
</div> </div>
...@@ -694,7 +696,7 @@ ...@@ -694,7 +696,7 @@
/> />
</svg> </svg>
</div> </div>
<div class=" self-center font-medium">Sign Out</div> <div class=" self-center font-medium">{$i18n.t('Sign Out')}</div>
</button> </button>
</div> </div>
</div> </div>
...@@ -707,7 +709,11 @@ ...@@ -707,7 +709,11 @@
<div <div
class="fixed left-0 top-[50dvh] z-40 -translate-y-1/2 transition-transform translate-x-[255px] md:translate-x-[260px] rotate-0" class="fixed left-0 top-[50dvh] z-40 -translate-y-1/2 transition-transform translate-x-[255px] md:translate-x-[260px] rotate-0"
> >
<Tooltip placement="right" content={`${show ? 'Close' : 'Open'} sidebar`} touch={false}> <Tooltip
placement="right"
content={`${show ? $i18n.t('Close') : $i18n.t('Open')} ${$i18n.t('sidebar')}`}
touch={false}
>
<button <button
id="sidebar-toggle-button" id="sidebar-toggle-button"
class=" group" class=" group"
......
<script lang="ts">
import { DropdownMenu } from 'bits-ui';
import Dropdown from '$lib/components/common/Dropdown.svelte';
import GarbageBin from '$lib/components/icons/GarbageBin.svelte';
import Pencil from '$lib/components/icons/Pencil.svelte';
import Tooltip from '$lib/components/common/Tooltip.svelte';
export let renameHandler: Function;
export let deleteHandler: Function;
export let onClose: Function;
</script>
<Dropdown
on:change={(e) => {
if (e.detail === false) {
onClose();
}
}}
>
<Tooltip content="More">
<slot />
</Tooltip>
<div slot="content">
<DropdownMenu.Content
class="w-full max-w-[130px] rounded-lg px-1 py-1.5 border border-gray-700/50 z-50 bg-gray-850 text-white"
sideOffset={-2}
side="bottom"
align="start"
>
<DropdownMenu.Item
class="flex gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer"
on:click={() => {
renameHandler();
}}
>
<Pencil strokeWidth="2" />
<div class="flex items-center">Rename</div>
</DropdownMenu.Item>
<DropdownMenu.Item
class="flex gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer"
on:click={() => {
deleteHandler();
}}
>
<GarbageBin strokeWidth="2" />
<div class="flex items-center">Delete</div>
</DropdownMenu.Item>
</DropdownMenu.Content>
</div>
</Dropdown>
<script lang="ts"> <script lang="ts">
import { onMount } from 'svelte'; import { onMount, getContext } from 'svelte';
const i18n = getContext('i18n');
export let messages = []; export let messages = [];
let textAreaElement: HTMLTextAreaElement; let textAreaElement: HTMLTextAreaElement;
...@@ -19,16 +21,20 @@ ...@@ -19,16 +21,20 @@
class="px-2 py-1 text-sm font-semibold uppercase min-w-[6rem] text-left dark:group-hover:bg-gray-800 rounded-lg transition" class="px-2 py-1 text-sm font-semibold uppercase min-w-[6rem] text-left dark:group-hover:bg-gray-800 rounded-lg transition"
on:click={() => { on:click={() => {
message.role = message.role === 'user' ? 'assistant' : 'user'; message.role = message.role === 'user' ? 'assistant' : 'user';
}}>{message.role}</button }}>{$i18n.t(message.role)}</button
> >
</div> </div>
<div class="flex-1"> <div class="flex-1">
<!-- $i18n.t('a user') -->
<!-- $i18n.t('an assistant') -->
<textarea <textarea
id="{message.role}-{idx}-textarea" id="{message.role}-{idx}-textarea"
bind:this={textAreaElement} bind:this={textAreaElement}
class="w-full bg-transparent outline-none rounded-lg p-2 text-sm resize-none overflow-hidden" class="w-full bg-transparent outline-none rounded-lg p-2 text-sm resize-none overflow-hidden"
placeholder="Enter {message.role === 'user' ? 'a user' : 'an assistant'} message here" placeholder={$i18n.t(`Enter {{role}} message here`, {
role: message.role === 'user' ? $i18n.t('a user') : $i18n.t('an assistant')
})}
rows="1" rows="1"
on:input={(e) => { on:input={(e) => {
textAreaElement.style.height = ''; textAreaElement.style.height = '';
...@@ -100,6 +106,6 @@ ...@@ -100,6 +106,6 @@
</svg> </svg>
</div> </div>
<div class=" text-sm font-medium">Add message</div> <div class=" text-sm font-medium">{$i18n.t('Add message')}</div>
</button> </button>
</div> </div>
import i18next from 'i18next';
import resourcesToBackend from 'i18next-resources-to-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
import type { i18n as i18nType } from 'i18next';
import { writable } from 'svelte/store';
const createI18nStore = (i18n: i18nType) => {
const i18nWritable = writable(i18n);
i18n.on('initialized', () => {
i18nWritable.set(i18n);
});
i18n.on('loaded', () => {
i18nWritable.set(i18n);
});
i18n.on('added', () => i18nWritable.set(i18n));
i18n.on('languageChanged', () => {
i18nWritable.set(i18n);
});
return i18nWritable;
};
const createIsLoadingStore = (i18n: i18nType) => {
const isLoading = writable(false);
// if loaded resources are empty || {}, set loading to true
i18n.on('loaded', (resources) => {
// console.log('loaded:', resources);
Object.keys(resources).length !== 0 && isLoading.set(false);
});
// if resources failed loading, set loading to true
i18n.on('failedLoading', () => {
isLoading.set(true);
});
return isLoading;
};
i18next
.use(
resourcesToBackend(
(language: string, namespace: string) => import(`./locales/${language}/${namespace}.json`)
)
)
.use(LanguageDetector)
.init({
debug: false,
detection: {
order: ['querystring', 'localStorage', 'navigator'],
caches: ['localStorage'],
lookupQuerystring: 'lang',
lookupLocalStorage: 'locale'
},
fallbackLng: {
default: ['en-US']
},
ns: 'translation',
returnEmptyString: false,
interpolation: {
escapeValue: false // not needed for svelte as it escapes by default
}
});
const i18n = createI18nStore(i18next);
const isLoadingStore = createIsLoadingStore(i18next);
export const getLanguages = async () => {
const languages = (await import(`./locales/languages.json`)).default;
return languages;
};
export default i18n;
export const isLoading = isLoadingStore;
{
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' oder '-1' für kein Ablaufdatum.",
"(Beta)": "(Beta)",
"(e.g. `sh webui.sh --api`)": "(z.B. `sh webui.sh --api`)",
"(latest)": "(neueste)",
"{{modelName}} is thinking...": "{{modelName}} denkt nach...",
"{{webUIName}} Backend Required": "{{webUIName}}-Backend erforderlich",
"a user": "",
"About": "Über",
"Account": "Account",
"Action": "Aktion",
"Add a model": "Füge ein Modell hinzu",
"Add a model tag name": "Benenne dein Modell-Tag",
"Add a short description about what this modelfile does": "Füge eine kurze Beschreibung hinzu, was dieses Modelfile kann",
"Add a short title for this prompt": "Füge einen kurzen Titel für diesen Prompt hinzu",
"Add a tag": "Tag hinzugügen",
"Add Docs": "Dokumente hinzufügen",
"Add Files": "Dateien hinzufügen",
"Add message": "Nachricht eingeben",
"add tags": "Tags hinzufügen",
"Adjusting these settings will apply changes universally to all users.": "Das Anpassen dieser Einstellungen wirkt sich universell auf alle Benutzer aus.",
"admin": "Administrator",
"Admin Panel": "Admin Panel",
"Admin Settings": "Admin Einstellungen",
"Advanced Parameters": "Erweiterte Parameter",
"all": "Alle",
"All Users": "Alle Benutzer",
"Allow": "Erlauben",
"Allow Chat Deletion": "Chat Löschung erlauben",
"alphanumeric characters and hyphens": "alphanumerische Zeichen und Bindestriche",
"Already have an account?": "Hast du vielleicht schon ein Account?",
"an assistant": "",
"and": "und",
"API Base URL": "API Basis URL",
"API Key": "API Key",
"API RPM": "API RPM",
"are allowed - Activate this command by typing": "sind erlaubt - Aktiviere diesen Befehl, indem du",
"Are you sure?": "",
"Audio": "Audio",
"Auto-playback response": "Automatische Wiedergabe der Antwort",
"Auto-send input after 3 sec.": "Automatisches Senden der Eingabe nach 3 Sek",
"AUTOMATIC1111 Base URL": "AUTOMATIC1111 Basis URL",
"AUTOMATIC1111 Base URL is required.": "",
"available!": "verfügbar!",
"Back": "Zurück",
"Builder Mode": "Builder Modus",
"Cancel": "Abbrechen",
"Categories": "Kategorien",
"Change Password": "Passwort ändern",
"Chat": "Chat",
"Chat History": "Chat Verlauf",
"Chat History is off for this browser.": "Chat Verlauf ist für diesen Browser ausgeschaltet.",
"Chats": "Chats",
"Check Again": "Erneut überprüfen",
"Check for updates": "Nach Updates suchen",
"Checking for updates...": "Nach Updates suchen...",
"Choose a model before saving...": "Wähle bitte zuerst ein Modell, bevor du speicherst...",
"Chunk Overlap": "Chunk Overlap",
"Chunk Params": "Chunk Parameter",
"Chunk Size": "Chunk Size",
"Click here for help.": "Klicke hier für Hilfe.",
"Click here to check other modelfiles.": "Klicke hier, um andere Modelfiles zu überprüfen.",
"Click here to select": "",
"Click here to select documents.": "",
"click here.": "hier klicken.",
"Click on the user role button to change a user's role.": "Klicke auf die Benutzerrollenschaltfläche, um die Rolle eines Benutzers zu ändern.",
"Close": "Schließe",
"Collection": "Kollektion",
"Command": "Befehl",
"Confirm Password": "Passwort bestätigen",
"Connections": "Verbindungen",
"Content": "Inhalt",
"Context Length": "Context Length",
"Conversation Mode": "Konversationsmodus",
"Copy last code block": "Letzten Codeblock kopieren",
"Copy last response": "Letzte Antwort kopieren",
"Copying to clipboard was successful!": "Das Kopieren in die Zwischenablage war erfolgreich!",
"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':": "Erstelle einen prägnanten Satz mit 3-5 Wörtern als Überschrift für die folgende Abfrage. Halte dich dabei strikt an die 3-5-Wort-Grenze und vermeide die Verwendung des Wortes Titel:",
"Create a modelfile": "Modelfiles erstellen",
"Create Account": "Konto erstellen",
"Created at": "Erstellt am",
"Created by": "Erstellt von",
"Current Model": "Aktuelles Modell",
"Current Password": "Aktuelles Passwort",
"Custom": "Benutzerdefiniert",
"Customize Ollama models for a specific purpose": "Ollama-Modelle für einen bestimmten Zweck anpassen",
"Dark": "Dunkel",
"Database": "Datenbank",
"DD/MM/YYYY HH:mm": "DD.MM.YYYY HH:mm",
"Default": "Standard",
"Default (Automatic1111)": "",
"Default (Web API)": "Standard (Web-API)",
"Default model updated": "Standardmodell aktualisiert",
"Default Prompt Suggestions": "Standard-Prompt-Vorschläge",
"Default User Role": "Standardbenutzerrolle",
"delete": "löschen",
"Delete a model": "Ein Modell löschen",
"Delete chat": "Chat löschen",
"Delete Chats": "Chats löschen",
"Deleted {{deleteModelTag}}": "{{deleteModelTag}} gelöscht",
"Deleted {tagName}": "{tagName} gelöscht",
"Description": "Beschreibung",
"Desktop Notifications": "Desktop-Benachrichtigungen",
"Disabled": "Deaktiviert",
"Discover a modelfile": "Eine Modelfiles entdecken",
"Discover a prompt": "Einen Prompt entdecken",
"Discover, download, and explore custom prompts": "Benutzerdefinierte Prompts entdecken, herunterladen und erkunden",
"Discover, download, and explore model presets": "Modellvorgaben entdecken, herunterladen und erkunden",
"Display the username instead of You in the Chat": "Den Benutzernamen anstelle von 'Du' im Chat anzeigen",
"Document": "Dokument",
"Document Settings": "Dokumenteinstellungen",
"Documents": "Dokumente",
"does not make any external connections, and your data stays securely on your locally hosted server.": "stellt keine externen Verbindungen her, und Deine Daten bleiben sicher auf Deinen lokal gehosteten Server.",
"Don't Allow": "Nicht erlauben",
"Don't have an account?": "Hast du vielleicht noch kein Account?",
"Download as a File": "Als Datei herunterladen",
"Download Database": "Datenbank herunterladen",
"Drop any files here to add to the conversation": "Lasse Dateien hier fallen, um sie dem Chat anzuhängen",
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "z.B. '30s','10m'. Gültige Zeiteinheiten sind 's', 'm', 'h'.",
"Edit Doc": "Dokument bearbeiten",
"Edit User": "Benutzer bearbeiten",
"Email": "E-Mail",
"Enable Chat History": "Chat-Verlauf aktivieren",
"Enable New Sign Ups": "Neue Anmeldungen aktivieren",
"Enabled": "Aktiviert",
"Enter {{role}} message here": "",
"Enter API Key": "",
"Enter Chunk Overlap": "",
"Enter Chunk Size": "",
"Enter Image Size (e.g. 512x512)": "",
"Enter LiteLLM API Base URL (litellm_params.api_base)": "",
"Enter LiteLLM API Key (litellm_params.api_key)": "",
"Enter LiteLLM API RPM (litellm_params.rpm)": "",
"Enter LiteLLM Model (litellm_params.model)": "",
"Enter Max Tokens (litellm_params.max_tokens)": "",
"Enter model tag (e.g. {{modelTag}})": "",
"Enter Number of Steps (e.g. 50)": "",
"Enter stop sequence": "Stop-Sequenz eingeben",
"Enter Top K": "",
"Enter URL (e.g. http://127.0.0.1:7860/)": "",
"Enter Your Email": "Geben Deine E-Mail-Adresse ein",
"Enter Your Full Name": "Gebe Deinen vollständigen Namen ein",
"Enter Your Password": "Gebe Dein Passwort ein",
"Experimental": "Experimentell",
"Export All Chats (All Users)": "Alle Chats exportieren (alle Benutzer)",
"Export Chats": "Chats exportieren",
"Export Documents Mapping": "Dokumentenmapping exportieren",
"Export Modelfiles": "Modelfiles exportieren",
"Export Prompts": "Prompts exportieren",
"Failed to read clipboard contents": "Fehler beim Lesen des Zwischenablageninhalts",
"File Mode": "File Mode",
"File not found.": "Datei nicht gefunden.",
"Focus chat input": "Chat-Eingabe fokussieren",
"Format your variables using square brackets like this:": "Formatiere Deine Variablen mit eckigen Klammern wie folgt:",
"From (Base Model)": "Von (Basismodell)",
"Full Screen Mode": "Vollbildmodus",
"General": "Allgemein",
"General Settings": "Allgemeine Einstellungen",
"Hello, {{name}}": "Hallo, {{name}}",
"Hide": "Verbergen",
"Hide Additional Params": "Hide Additional Params",
"How can I help you today?": "Wie kann ich Dir heute helfen?",
"Image Generation (Experimental)": "Bildgenerierung (experimentell)",
"Image Generation Engine": "",
"Image Settings": "Bildeinstellungen",
"Images": "Bilder",
"Import Chats": "Chats importieren",
"Import Documents Mapping": "Dokumentenmapping importieren",
"Import Modelfiles": "Modelfiles importieren",
"Import Prompts": "Prompts importieren",
"Include `--api` flag when running stable-diffusion-webui": "Füge das `--api`-Flag hinzu, wenn Du stable-diffusion-webui nutzt",
"Interface": "Benutzeroberfläche",
"join our Discord for help.": "Trete unserem Discord bei, um Hilfe zu erhalten.",
"JSON": "JSON",
"JWT Expiration": "JWT-Ablauf",
"JWT Token": "JWT-Token",
"Keep Alive": "Keep Alive",
"Keyboard shortcuts": "Tastenkürzel",
"Language": "Sprache",
"Light": "Hell",
"Listening...": "Hören...",
"LLMs can make mistakes. Verify important information.": "LLMs können Fehler machen. Überprüfe wichtige Informationen.",
"Made by OpenWebUI Community": "Von der OpenWebUI-Community",
"Make sure to enclose them with": "Formatiere Deine Variablen mit:",
"Manage LiteLLM Models": "LiteLLM-Modelle verwalten",
"Manage Models": "Modelle verwalten",
"Manage Ollama Models": "Ollama-Modelle verwalten",
"Max Tokens": "Maximale Tokens",
"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Es können maximal 3 Modelle gleichzeitig heruntergeladen werden. Bitte versuche es später erneut.",
"Mirostat": "Mirostat",
"Mirostat Eta": "Mirostat Eta",
"Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "DD.MM.YYYY",
"Model '{{modelName}}' has been successfully downloaded.": "Modell '{{modelName}}' wurde erfolgreich heruntergeladen.",
"Model '{{modelTag}}' is already in queue for downloading.": "Modell '{{modelTag}}' befindet sich bereits in der Warteschlange zum Herunterladen.",
"Model {{modelId}} not found": "Modell {{modelId}} nicht gefunden",
"Model {{modelName}} already exists.": "Modell {{modelName}} existiert bereits.",
"Model Name": "Modellname",
"Model not selected": "Modell nicht ausgewählt",
"Model Tag Name": "Modell-Tag-Name",
"Model Whitelisting": "Modell-Whitelisting",
"Model(s) Whitelisted": "Modell(e) auf der Whitelist",
"Modelfile": "Modelfiles",
"Modelfile Advanced Settings": "Erweiterte Modelfileseinstellungen",
"Modelfile Content": "Modelfile Content",
"Modelfiles": "Modelfiles",
"Models": "Modelle",
"My Documents": "Meine Dokumente",
"My Modelfiles": "Meine Modelfiles",
"My Prompts": "Meine Prompts",
"Name": "Name",
"Name Tag": "Namens-Tag",
"Name your modelfile": "Name your modelfile",
"New Chat": "Neuer Chat",
"New Password": "Neues Passwort",
"Not sure what to add?": "Nicht sicher, was hinzugefügt werden soll?",
"Not sure what to write? Switch to": "Nicht sicher, was Du schreiben sollst? Wechsel zu",
"Off": "Aus",
"Okay, Let's Go!": "Okay, los geht's!",
"Ollama Base URL": "",
"Ollama Version": "Ollama-Version",
"On": "Ein",
"Only": "Nur",
"Only alphanumeric characters and hyphens are allowed in the command string.": "Nur alphanumerische Zeichen und Bindestriche sind im Befehlsstring erlaubt.",
"Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.": "Hoppla! Warte noch einen Moment! Die Dateien sind noch im der Verarbeitung. Bitte habe etwas Geduld und wir informieren Dich, sobald sie bereit sind.",
"Oops! Looks like the URL is invalid. Please double-check and try again.": "Oops! Looks like the URL is invalid. Please double-check and try again.",
"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.",
"Open": "Öffne",
"Open AI": "Open AI",
"Open AI (Dall-E)": "",
"Open new chat": "Neuen Chat öffnen",
"OpenAI API": "OpenAI-API",
"OpenAI API Key": "",
"OpenAI API Key is required.": "",
"or": "oder",
"Parameters": "Parameter",
"Password": "Passwort",
"PDF Extract Images (OCR)": "Text von Bilder aus PDFs extrahieren (OCR)",
"pending": "ausstehend",
"Permission denied when accessing microphone: {{error}}": "Zugriff auf das Mikrofon verweigert: {{error}}",
"Playground": "Playground",
"Profile": "Profil",
"Prompt Content": "Prompt-Inhalt",
"Prompt suggestions": "Prompt-Vorschläge",
"Prompts": "Prompts",
"Pull a model from Ollama.com": "Ein Modell von Ollama.com abrufen",
"Pull Progress": "Fortschritt abrufen",
"Query Params": "Query Parameter",
"RAG Template": "RAG-Vorlage",
"Raw Format": "Rohformat",
"Record voice": "Stimme aufnehmen",
"Redirecting you to OpenWebUI Community": "Du wirst zur OpenWebUI-Community weitergeleitet",
"Release Notes": "Versionshinweise",
"Repeat Last N": "Repeat Last N",
"Repeat Penalty": "Repeat Penalty",
"Request Mode": "Request-Modus",
"Reset Vector Storage": "Vektorspeicher zurücksetzen",
"Response AutoCopy to Clipboard": "Antwort automatisch in die Zwischenablage kopieren",
"Role": "Rolle",
"Rosé Pine": "Rosé Pine",
"Rosé Pine Dawn": "Rosé Pine Dawn",
"Save": "Speichern",
"Save & Create": "Speichern und erstellen",
"Save & Submit": "Speichern und senden",
"Save & Update": "Speichern und aktualisieren",
"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Das direkte Speichern von Chat-Protokollen im Browser-Speicher wird nicht mehr unterstützt. Bitte nehme Dir einen Moment Zeit, um Deine Chat-Protokolle herunterzuladen und zu löschen, indem Du auf die Schaltfläche unten klickst. Keine Sorge, Du kannst Deine Chat-Protokolle problemlos über das Backend wieder importieren.",
"Scan": "Scannen",
"Scan complete!": "Scan abgeschlossen!",
"Scan for documents from {{path}}": "Dokumente von {{path}} scannen",
"Search": "Suchen",
"Search Documents": "Dokumente suchen",
"Search Prompts": "Prompts suchen",
"See readme.md for instructions": "Anleitung in readme.md anzeigen",
"See what's new": "Was gibt's Neues",
"Seed": "Seed",
"Select a mode": "",
"Select a model": "Ein Modell auswählen",
"Select an Ollama instance": "",
"Send a Messsage": "Eine Nachricht senden",
"Send message": "Nachricht senden",
"Server connection verified": "Serververbindung überprüft",
"Set as default": "Als Standard festlegen",
"Set Default Model": "Standardmodell festlegen",
"Set Image Size": "Bildgröße festlegen",
"Set Steps": "Schritte festlegen",
"Set Title Auto-Generation Model": "Modell für automatische Titelgenerierung festlegen",
"Set Voice": "Stimme festlegen",
"Settings": "Einstellungen",
"Settings saved successfully!": "Einstellungen erfolgreich gespeichert!",
"Share to OpenWebUI Community": "Mit OpenWebUI Community teilen",
"short-summary": "kurze-zusammenfassung",
"Show": "Anzeigen",
"Show Additional Params": "Show Additional Params",
"Show shortcuts": "Verknüpfungen anzeigen",
"sidebar": "Seitenleiste",
"Sign in": "Anmelden",
"Sign Out": "Abmelden",
"Sign up": "Registrieren",
"Speech recognition error: {{error}}": "Spracherkennungsfehler: {{error}}",
"Speech-to-Text Engine": "Sprache-zu-Text-Engine",
"SpeechRecognition API is not supported in this browser.": "Die SpeechRecognition-API wird in diesem Browser nicht unterstützt.",
"Stop Sequence": "Stop Sequence",
"STT Settings": "STT-Einstellungen",
"Submit": "Senden",
"Success": "Erfolg",
"Successfully updated.": "Erfolgreich aktualisiert.",
"Sync All": "Alles synchronisieren",
"System": "System",
"System Prompt": "System-Prompt",
"Tags": "Tags",
"Temperature": "Temperatur",
"Template": "Vorlage",
"Text Completion": "Textvervollständigung",
"Text-to-Speech Engine": "Text-zu-Sprache-Engine",
"Tfs Z": "Tfs Z",
"Theme": "Design",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Dadurch werden Deine wertvollen Unterhaltungen sicher in der Backend-Datenbank gespeichert. Vielen Dank!",
"This setting does not sync across browsers or devices.": "Diese Einstellung wird nicht zwischen Browsern oder Geräten synchronisiert.",
"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.",
"Title": "Titel",
"Title Auto-Generation": "Automatische Titelgenerierung",
"Title Generation Prompt": "Prompt für Titelgenerierung",
"to": "für",
"To access the available model names for downloading,": "Um auf die verfügbaren Modellnamen zum Herunterladen zuzugreifen,",
"To access the GGUF models available for downloading,": "To access the GGUF models available for downloading,",
"to chat input.": "to chat input.",
"Toggle settings": "Einstellungen umschalten",
"Toggle sidebar": "Seitenleiste umschalten",
"Top K": "Top K",
"Top P": "Top P",
"Trouble accessing Ollama?": "Probleme beim Zugriff auf Ollama?",
"TTS Settings": "TTS-Einstellungen",
"Type Hugging Face Resolve (Download) URL": "",
"Uh-oh! There was an issue connecting to {{provider}}.": "Ups! Es gab ein Problem bei der Verbindung mit {{provider}}.",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Unknown File Type '{{file_type}}', but accepting and treating as plain text",
"Update password": "Passwort aktualisieren",
"Upload a GGUF model": "Upload a GGUF model",
"Upload files": "Dateien hochladen",
"Upload Progress": "Upload Progress",
"URL Mode": "URL Mode",
"Use '#' in the prompt input to load and select your documents.": "Verwende '#' in der Prompt-Eingabe, um Deine Dokumente zu laden und auszuwählen.",
"Use Gravatar": "",
"user": "Benutzer",
"User Permissions": "Benutzerberechtigungen",
"Users": "Benutzer",
"Utilize": "Nutze die",
"Valid time units:": "Gültige Zeiteinheiten:",
"variable": "Variable",
"variable to have them replaced with clipboard content.": "Variable, um den Inhalt der Zwischenablage beim Nutzen des Prompts zu ersetzen.",
"Version": "Version",
"Web": "Web",
"WebUI Add-ons": "WebUI-Add-Ons",
"WebUI Settings": "WebUI-Einstellungen",
"WebUI will make requests to": "Wenn aktiviert sendet WebUI externe Anfragen an",
"What’s New in": "Was gibt's Neues in",
"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "Wenn die Historie ausgeschaltet ist, werden neue Chats nicht in Deiner Historie auf Deine Geräte angezeigt.",
"Whisper (Local)": "Whisper (Lokal)",
"Write a prompt suggestion (e.g. Who are you?)": "Gebe einen Prompt-Vorschlag ein (z.B. Wer bist du?)",
"Write a summary in 50 words that summarizes [topic or keyword].": "Schreibe eine kurze Zusammenfassung in 50 Wörtern, die [Thema oder Schlüsselwort] zusammenfasst.",
"You": "Du",
"You're a helpful assistant.": "Du bist ein hilfreicher Assistent.",
"You're now logged in.": "Du bist nun eingeloggt."
}
{
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.",
"(Beta)": "(Beta)",
"(e.g. `sh webui.sh --api`)": "(e.g. `sh webui.sh --api`)",
"(latest)": "(latest)",
"{{modelName}} is thinking...": "{{modelName}} is thinking...",
"{{webUIName}} Backend Required": "{{webUIName}} Backend Required",
"a user": "",
"About": "About",
"Account": "Account",
"Action": "Action",
"Add a model": "Add a model",
"Add a model tag name": "Add a model tag name",
"Add a short description about what this modelfile does": "Add a short description about what this modelfile does",
"Add a short title for this prompt": "Add a short title for this prompt",
"Add a tag": "Add a tag",
"Add Docs": "Add Docs",
"Add Files": "Add Files",
"Add message": "Add message",
"add tags": "add tags",
"Adjusting these settings will apply changes universally to all users.": "Adjusting these settings will apply changes universally to all users.",
"admin": "admin",
"Admin Panel": "Admin Panel",
"Admin Settings": "Admin Settings",
"Advanced Parameters": "Advanced Parameters",
"all": "all",
"All Users": "All Users",
"Allow": "Allow",
"Allow Chat Deletion": "Allow Chat Deletion",
"alphanumeric characters and hyphens": "alphanumeric characters and hyphens",
"Already have an account?": "Already have an account?",
"an assistant": "",
"and": "and",
"API Base URL": "API Base URL",
"API Key": "API Key",
"API RPM": "API RPM",
"are allowed - Activate this command by typing": "are allowed - Activate this command by typing",
"Are you sure?": "",
"Audio": "Audio",
"Auto-playback response": "Auto-playback response",
"Auto-send input after 3 sec.": "Auto-send input after 3 sec.",
"AUTOMATIC1111 Base URL": "AUTOMATIC1111 Base URL",
"AUTOMATIC1111 Base URL is required.": "",
"available!": "available!",
"Back": "Back",
"Builder Mode": "Builder Mode",
"Cancel": "Cancel",
"Categories": "Categories",
"Change Password": "Change Password",
"Chat": "Chat",
"Chat History": "Chat History",
"Chat History is off for this browser.": "Chat History is off for this browser.",
"Chats": "Chats",
"Check Again": "Check Again",
"Check for updates": "Check for updates",
"Checking for updates...": "Checking for updates...",
"Choose a model before saving...": "Choose a model before saving...",
"Chunk Overlap": "Chunk Overlap",
"Chunk Params": "Chunk Params",
"Chunk Size": "Chunk Size",
"Click here for help.": "Click here for help.",
"Click here to check other modelfiles.": "Click here to check other modelfiles.",
"Click here to select": "",
"Click here to select documents.": "",
"click here.": "click here.",
"Click on the user role button to change a user's role.": "Click on the user role button to change a user's role.",
"Close": "Close",
"Collection": "Collection",
"Command": "Command",
"Confirm Password": "Confirm Password",
"Connections": "Connections",
"Content": "Content",
"Context Length": "Context Length",
"Conversation Mode": "Conversation Mode",
"Copy last code block": "Copy last code block",
"Copy last response": "Copy last response",
"Copying to clipboard was successful!": "Copying to clipboard was successful!",
"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':": "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':",
"Create a modelfile": "Create a modelfile",
"Create Account": "Create Account",
"Created at": "Created at",
"Created by": "Created by",
"Current Model": "Current Model",
"Current Password": "Current Password",
"Custom": "Custom",
"Customize Ollama models for a specific purpose": "Customize Ollama models for a specific purpose",
"Dark": "Dark",
"Database": "Database",
"DD/MM/YYYY HH:mm": "DD/MM/YYYY HH:mm",
"Default": "Default",
"Default (Automatic1111)": "",
"Default (Web API)": "Default (Web API)",
"Default model updated": "Default model updated",
"Default Prompt Suggestions": "Default Prompt Suggestions",
"Default User Role": "Default User Role",
"delete": "delete",
"Delete a model": "Delete a model",
"Delete chat": "Delete chat",
"Delete Chats": "Delete Chats",
"Deleted {{deleteModelTag}}": "Deleted {{deleteModelTag}}",
"Deleted {tagName}": "Deleted {tagName}",
"Description": "Description",
"Desktop Notifications": "Notification",
"Disabled": "Disabled",
"Discover a modelfile": "Discover a modelfile",
"Discover a prompt": "Discover a prompt",
"Discover, download, and explore custom prompts": "Discover, download, and explore custom prompts",
"Discover, download, and explore model presets": "Discover, download, and explore model presets",
"Display the username instead of You in the Chat": "Display the username instead of 'You' in the Chat",
"Document": "Document",
"Document Settings": "Document Settings",
"Documents": "Documents",
"does not make any external connections, and your data stays securely on your locally hosted server.": "does not make any external connections, and your data stays securely on your locally hosted server.",
"Don't Allow": "Don't Allow",
"Don't have an account?": "Don't have an account?",
"Download as a File": "Download as a File",
"Download Database": "Download Database",
"Drop any files here to add to the conversation": "Drop any files here to add to the conversation",
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.",
"Edit Doc": "Edit Doc",
"Edit User": "Edit User",
"Email": "Email",
"Enable Chat History": "Enable Chat History",
"Enable New Sign Ups": "Enable New Sign Ups",
"Enabled": "Enabled",
"Enter {{role}} message here": "",
"Enter API Key": "",
"Enter Chunk Overlap": "",
"Enter Chunk Size": "",
"Enter Image Size (e.g. 512x512)": "",
"Enter LiteLLM API Base URL (litellm_params.api_base)": "",
"Enter LiteLLM API Key (litellm_params.api_key)": "",
"Enter LiteLLM API RPM (litellm_params.rpm)": "",
"Enter LiteLLM Model (litellm_params.model)": "",
"Enter Max Tokens (litellm_params.max_tokens)": "",
"Enter model tag (e.g. {{modelTag}})": "",
"Enter Number of Steps (e.g. 50)": "",
"Enter stop sequence": "Enter stop sequence",
"Enter Top K": "",
"Enter URL (e.g. http://127.0.0.1:7860/)": "",
"Enter Your Email": "Enter Your Email",
"Enter Your Full Name": "Enter Your Full Name",
"Enter Your Password": "Enter Your Password",
"Experimental": "Experimental",
"Export All Chats (All Users)": "Export All Chats (All Users)",
"Export Chats": "Export Chats",
"Export Documents Mapping": "Export Documents Mapping",
"Export Modelfiles": "Export Modelfiles",
"Export Prompts": "Export Prompts",
"Failed to read clipboard contents": "Failed to read clipboard contents",
"File Mode": "File Mode",
"File not found.": "File not found.",
"Focus chat input": "Focus chat input",
"Format your variables using square brackets like this:": "Format your variables using square brackets like this:",
"From (Base Model)": "From (Base Model)",
"Full Screen Mode": "Full Screen Mode",
"General": "General",
"General Settings": "General Settings",
"Hello, {{name}}": "Hello, {{name}}",
"Hide": "Hide",
"Hide Additional Params": "Hide Additional Params",
"How can I help you today?": "How can I help you today?",
"Image Generation (Experimental)": "Image Generation (Experimental)",
"Image Generation Engine": "",
"Image Settings": "Image Settings",
"Images": "Images",
"Import Chats": "Import Chats",
"Import Documents Mapping": "Import Documents Mapping",
"Import Modelfiles": "Import Modelfiles",
"Import Prompts": "Import Prompts",
"Include `--api` flag when running stable-diffusion-webui": "Include `--api` flag when running stable-diffusion-webui",
"Interface": "Interface",
"join our Discord for help.": "join our Discord for help.",
"JSON": "JSON",
"JWT Expiration": "JWT Expiration",
"JWT Token": "JWT Token",
"Keep Alive": "Keep Alive",
"Keyboard shortcuts": "Keyboard shortcuts",
"Language": "Language",
"Light": "Light",
"Listening...": "Listening...",
"LLMs can make mistakes. Verify important information.": "LLMs can make mistakes. Verify important information.",
"Made by OpenWebUI Community": "Made by OpenWebUI Community",
"Make sure to enclose them with": "Make sure to enclose them with",
"Manage LiteLLM Models": "Manage LiteLLM Models",
"Manage Models": "Manage Models",
"Manage Ollama Models": "Manage Ollama Models",
"Max Tokens": "Max Tokens",
"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Maximum of 3 models can be downloaded simultaneously. Please try again later.",
"Mirostat": "Mirostat",
"Mirostat Eta": "Mirostat Eta",
"Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "MMMM DD, YYYY",
"Model '{{modelName}}' has been successfully downloaded.": "Model '{{modelName}}' has been successfully downloaded.",
"Model '{{modelTag}}' is already in queue for downloading.": "Model '{{modelTag}}' is already in queue for downloading.",
"Model {{modelId}} not found": "Model {{modelId}} not found",
"Model {{modelName}} already exists.": "Model {{modelName}} already exists.",
"Model Name": "Model Name",
"Model not selected": "Model not selected",
"Model Tag Name": "Model Tag Name",
"Model Whitelisting": "Model Whitelisting",
"Model(s) Whitelisted": "Model(s) Whitelisted",
"Modelfile": "Modelfile",
"Modelfile Advanced Settings": "Modelfile Advanced Settings",
"Modelfile Content": "Modelfile Content",
"Modelfiles": "Modelfiles",
"Models": "Models",
"My Documents": "My Documents",
"My Modelfiles": "My Modelfiles",
"My Prompts": "My Prompts",
"Name": "Name",
"Name Tag": "Name Tag",
"Name your modelfile": "Name your modelfile",
"New Chat": "New Chat",
"New Password": "New Password",
"Not sure what to add?": "Not sure what to add?",
"Not sure what to write? Switch to": "Not sure what to write? Switch to",
"Off": "Off",
"Okay, Let's Go!": "Okay, Let's Go!",
"Ollama Base URL": "",
"Ollama Version": "Ollama Version",
"On": "On",
"Only": "Only",
"Only alphanumeric characters and hyphens are allowed in the command string.": "Only alphanumeric characters and hyphens are allowed in the command string.",
"Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.": "Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.",
"Oops! Looks like the URL is invalid. Please double-check and try again.": "Oops! Looks like the URL is invalid. Please double-check and try again.",
"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.",
"Open": "Open",
"Open AI": "Open AI",
"Open AI (Dall-E)": "",
"Open new chat": "Open new chat",
"OpenAI API": "OpenAI API",
"OpenAI API Key": "",
"OpenAI API Key is required.": "",
"or": "or",
"Parameters": "Parameters",
"Password": "Password",
"PDF Extract Images (OCR)": "PDF Extract Images (OCR)",
"pending": "pending",
"Permission denied when accessing microphone: {{error}}": "Permission denied when accessing microphone: {{error}}",
"Playground": "Playground",
"Profile": "Profile",
"Prompt Content": "Prompt Content",
"Prompt suggestions": "Prompt suggestions",
"Prompts": "Prompts",
"Pull a model from Ollama.com": "Pull a model from Ollama.com",
"Pull Progress": "Pull Progress",
"Query Params": "Query Params",
"RAG Template": "RAG Template",
"Raw Format": "Raw Format",
"Record voice": "Record voice",
"Redirecting you to OpenWebUI Community": "Redirecting you to OpenWebUI Community",
"Release Notes": "Release Notes",
"Repeat Last N": "Repeat Last N",
"Repeat Penalty": "Repeat Penalty",
"Request Mode": "Request Mode",
"Reset Vector Storage": "Reset Vector Storage",
"Response AutoCopy to Clipboard": "Response AutoCopy to Clipboard",
"Role": "Role",
"Rosé Pine": "Rosé Pine",
"Rosé Pine Dawn": "Rosé Pine Dawn",
"Save": "Save",
"Save & Create": "Save & Create",
"Save & Submit": "Save & Submit",
"Save & Update": "Save & Update",
"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through",
"Scan": "Scan",
"Scan complete!": "Scan complete!",
"Scan for documents from {{path}}": "Scan for documents from {{path}}",
"Search": "Search",
"Search Documents": "Search Documents",
"Search Prompts": "Search Prompts",
"See readme.md for instructions": "See readme.md for instructions",
"See what's new": "See what's new",
"Seed": "Seed",
"Select a mode": "",
"Select a model": "Select a model",
"Select an Ollama instance": "",
"Send a Messsage": "Send a Messsage",
"Send message": "Send message",
"Server connection verified": "Server connection verified",
"Set as default": "Set as default",
"Set Default Model": "Set Default Model",
"Set Image Size": "Set Image Size",
"Set Steps": "Set Steps",
"Set Title Auto-Generation Model": "Set Title Auto-Generation Model",
"Set Voice": "Set Voice",
"Settings": "Settings",
"Settings saved successfully!": "Settings saved successfully!",
"Share to OpenWebUI Community": "Share to OpenWebUI Community",
"short-summary": "short-summary",
"Show": "Show",
"Show Additional Params": "Show Additional Params",
"Show shortcuts": "Show shortcuts",
"sidebar": "sidebar",
"Sign in": "Sign in",
"Sign Out": "Sign Out",
"Sign up": "Sign up",
"Speech recognition error: {{error}}": "Speech recognition error: {{error}}",
"Speech-to-Text Engine": "Speech-to-Text Engine",
"SpeechRecognition API is not supported in this browser.": "SpeechRecognition API is not supported in this browser.",
"Stop Sequence": "Stop Sequence",
"STT Settings": "STT Settings",
"Submit": "Submit",
"Success": "Success",
"Successfully updated.": "Successfully updated.",
"Sync All": "Sync All",
"System": "System",
"System Prompt": "System Prompt",
"Tags": "Tags",
"Temperature": "Temperature",
"Template": "Template",
"Text Completion": "Text Completion",
"Text-to-Speech Engine": "Text-to-Speech Engine",
"Tfs Z": "Tfs Z",
"Theme": "Theme",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "This ensures that your valuable conversations are securely saved to your backend database. Thank you!",
"This setting does not sync across browsers or devices.": "This setting does not sync across browsers or devices.",
"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.",
"Title": "Title",
"Title Auto-Generation": "Title Auto-Generation",
"Title Generation Prompt": "Title Generation Prompt",
"to": "to",
"To access the available model names for downloading,": "To access the available model names for downloading,",
"To access the GGUF models available for downloading,": "To access the GGUF models available for downloading,",
"to chat input.": "to chat input.",
"Toggle settings": "Toggle settings",
"Toggle sidebar": "Toggle sidebar",
"Top K": "Top K",
"Top P": "Top P",
"Trouble accessing Ollama?": "Trouble accessing Ollama?",
"TTS Settings": "TTS Settings",
"Type Hugging Face Resolve (Download) URL": "",
"Uh-oh! There was an issue connecting to {{provider}}.": "Uh-oh! There was an issue connecting to {{provider}}.",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Unknown File Type '{{file_type}}', but accepting and treating as plain text",
"Update password": "Update password",
"Upload a GGUF model": "Upload a GGUF model",
"Upload files": "Upload files",
"Upload Progress": "Upload Progress",
"URL Mode": "URL Mode",
"Use '#' in the prompt input to load and select your documents.": "Use '#' in the prompt input to load and select your documents.",
"Use Gravatar": "",
"user": "user",
"User Permissions": "User Permissions",
"Users": "Users",
"Utilize": "Utilize",
"Valid time units:": "Valid time units:",
"variable": "variable",
"variable to have them replaced with clipboard content.": "variable to have them replaced with clipboard content.",
"Version": "Version",
"Web": "Web",
"WebUI Add-ons": "WebUI Add-ons",
"WebUI Settings": "WebUI Settings",
"WebUI will make requests to": "WebUI will make requests to",
"What’s New in": "What’s New in",
"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "When history is turned off, new chats on this browser won't appear in your history on any of your devices.",
"Whisper (Local)": "Whisper (Local)",
"Write a prompt suggestion (e.g. Who are you?)": "Write a prompt suggestion (e.g. Who are you?)",
"Write a summary in 50 words that summarizes [topic or keyword].": "Write a summary in 50 words that summarizes [topic or keyword].",
"You": "You",
"You're a helpful assistant.": "You're a helpful assistant.",
"You're now logged in.": "You're now logged in."
}
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