mirror of
https://github.com/open-webui/open-webui.git
synced 2026-10-07 02:58:21 +00:00
refac
This commit is contained in:
parent
24e30d1cbd
commit
5bb1c47080
10 changed files with 152 additions and 91 deletions
|
|
@ -92,6 +92,11 @@
|
|||
toast.error(`${error}`);
|
||||
return null;
|
||||
});
|
||||
if (res?.sessions_revoked) {
|
||||
localStorage.removeItem('token');
|
||||
window.location.href = '/auth?state=logout&form=signin';
|
||||
return false;
|
||||
}
|
||||
if (res) {
|
||||
adminConfig = res;
|
||||
}
|
||||
|
|
@ -99,11 +104,13 @@
|
|||
};
|
||||
|
||||
const submitHandler = async () => {
|
||||
const adminSaved = await updateAdminHandler();
|
||||
const ldapSaved = await updateLdapServerHandler();
|
||||
const oauthSaved = await updateOAuthHandler();
|
||||
if (!ldapSaved || !oauthSaved) return;
|
||||
|
||||
if (adminSaved && ldapSaved && oauthSaved) {
|
||||
// Save MFA policy last because changing it revokes the current session.
|
||||
const adminSaved = await updateAdminHandler();
|
||||
if (adminSaved) {
|
||||
toast.success($i18n.t('Settings saved successfully!'));
|
||||
await config.set(await getBackendConfig());
|
||||
}
|
||||
|
|
@ -255,6 +262,32 @@
|
|||
</AdminSettingField>
|
||||
</AdminSettingSection>
|
||||
|
||||
<AdminSettingSection title={$i18n.t('Multi-factor authentication')}>
|
||||
<AdminSettingRow label={$i18n.t('Require an authenticator for all users')}>
|
||||
<Switch
|
||||
bind:state={adminConfig.ENABLE_MFA}
|
||||
ariaLabel={$i18n.t('Require an authenticator for all users')}
|
||||
/>
|
||||
</AdminSettingRow>
|
||||
<p class="-mt-1 text-[0.6875rem] leading-4 text-gray-400 dark:text-gray-600">
|
||||
{$i18n.t('Changes sign out all devices. Users enroll on their next sign-in.')}
|
||||
</p>
|
||||
{#if adminConfig.ENABLE_MFA}
|
||||
<AdminSettingRow label={$i18n.t('Allow OAuth sign-in without an authenticator')}
|
||||
><Switch
|
||||
bind:state={adminConfig.MFA_ALLOW_OAUTH_BYPASS}
|
||||
ariaLabel={$i18n.t('Allow OAuth sign-in without an authenticator')}
|
||||
/></AdminSettingRow
|
||||
>
|
||||
<AdminSettingRow label={$i18n.t('Allow trusted-header sign-in without an authenticator')}
|
||||
><Switch
|
||||
bind:state={adminConfig.MFA_ALLOW_TRUSTED_HEADER_BYPASS}
|
||||
ariaLabel={$i18n.t('Allow trusted-header sign-in without an authenticator')}
|
||||
/></AdminSettingRow
|
||||
>
|
||||
{/if}
|
||||
</AdminSettingSection>
|
||||
|
||||
<AdminSettingSection
|
||||
title={$i18n.t('settings.admin.authentication.sections.pendingAccounts.title')}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -275,31 +275,7 @@
|
|||
{/if}
|
||||
</div>
|
||||
</AdminSettingSection>
|
||||
<AdminSettingSection title={$i18n.t('Multi-factor authentication')}>
|
||||
<AdminSettingRow label={$i18n.t('Require an authenticator for all users')}>
|
||||
<Switch
|
||||
bind:state={adminConfig.ENABLE_MFA}
|
||||
ariaLabel={$i18n.t('Require an authenticator for all users')}
|
||||
/>
|
||||
</AdminSettingRow>
|
||||
<p class="-mt-1 text-[0.6875rem] leading-4 text-gray-400 dark:text-gray-600">
|
||||
{$i18n.t('Changes sign out all devices. Users enroll on their next sign-in.')}
|
||||
</p>
|
||||
{#if adminConfig.ENABLE_MFA}
|
||||
<AdminSettingRow label={$i18n.t('Allow OAuth sign-in without an authenticator')}
|
||||
><Switch
|
||||
bind:state={adminConfig.MFA_ALLOW_OAUTH_BYPASS}
|
||||
ariaLabel={$i18n.t('Allow OAuth sign-in without an authenticator')}
|
||||
/></AdminSettingRow
|
||||
>
|
||||
<AdminSettingRow label={$i18n.t('Allow trusted-header sign-in without an authenticator')}
|
||||
><Switch
|
||||
bind:state={adminConfig.MFA_ALLOW_TRUSTED_HEADER_BYPASS}
|
||||
ariaLabel={$i18n.t('Allow trusted-header sign-in without an authenticator')}
|
||||
/></AdminSettingRow
|
||||
>
|
||||
{/if}
|
||||
</AdminSettingSection>
|
||||
|
||||
|
||||
<AdminSettingSection title={$i18n.t('settings.admin.general.sections.features.title')}>
|
||||
<AdminSettingRow
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@
|
|||
<div class="flex justify-between pt-3 text-sm font-normal">
|
||||
<button
|
||||
type="button"
|
||||
class="text-sm text-red-500"
|
||||
class="text-sm text-gray-500 transition-colors hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
|
||||
on:click={() => {
|
||||
showRevokeConfirmation = true;
|
||||
}}>{$i18n.t('Sign out all devices')}</button
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@
|
|||
/>
|
||||
{:else}
|
||||
<div>
|
||||
<h2 class="text-base font-medium tracking-tight text-gray-900 dark:text-white">
|
||||
<h2 class="text-base font-medium tracking-tight text-gray-700 dark:text-gray-300">
|
||||
{$i18n.t(
|
||||
challenge.next_step === 'enroll'
|
||||
? 'Set up your authenticator'
|
||||
|
|
@ -103,7 +103,7 @@
|
|||
/>
|
||||
<details class="group text-[0.6875rem] text-gray-500 dark:text-gray-400">
|
||||
<summary
|
||||
class="flex cursor-pointer list-none items-center gap-1 hover:text-gray-900 dark:hover:text-white [&::-webkit-details-marker]:hidden"
|
||||
class="flex cursor-pointer list-none items-center gap-1 hover:text-gray-700 dark:hover:text-gray-200 [&::-webkit-details-marker]:hidden"
|
||||
>
|
||||
{$i18n.t('Enter the key manually')}
|
||||
<ChevronRight className="size-2.5 shrink-0 transition-transform group-open:rotate-90" />
|
||||
|
|
@ -118,11 +118,11 @@
|
|||
</p>
|
||||
{:else}<button
|
||||
type="button"
|
||||
class="text-gray-500 hover:text-gray-900 dark:hover:text-white"
|
||||
class="text-gray-500 hover:text-gray-700 dark:hover:text-gray-200"
|
||||
on:click={loadSetup}>{$i18n.t('Retry setup')}</button
|
||||
>{/if}
|
||||
{/if}
|
||||
<label class="block text-[0.8125rem] leading-5 font-normal text-left text-black dark:text-white">
|
||||
<label class="block text-[0.8125rem] leading-5 font-normal text-left text-gray-600 dark:text-gray-400">
|
||||
{$i18n.t(
|
||||
challenge.next_step === 'recover'
|
||||
? 'Operator recovery token'
|
||||
|
|
@ -156,10 +156,10 @@
|
|||
{#if error}<p role="alert" class="text-xs leading-4 text-red-600 dark:text-red-400">
|
||||
{error}
|
||||
</p>{/if}
|
||||
<div class="flex justify-end text-black dark:text-white">
|
||||
<div class="flex justify-end text-gray-700 dark:text-gray-300">
|
||||
<button
|
||||
type="button"
|
||||
class="bg-gray-700/5 hover:bg-gray-700/10 dark:bg-gray-100/5 dark:hover:bg-gray-100/10 dark:text-gray-300 dark:hover:text-white transition w-full rounded-full font-normal text-[0.8125rem] leading-5 py-2.5 disabled:opacity-50 flex justify-center"
|
||||
class="bg-gray-700/5 hover:bg-gray-700/10 dark:bg-gray-100/5 dark:hover:bg-gray-100/10 dark:text-gray-300 dark:hover:text-gray-200 transition w-full rounded-full font-normal text-[0.8125rem] leading-5 py-2.5 disabled:opacity-50 flex justify-center"
|
||||
disabled={busy || !code.trim() || (challenge.next_step === 'enroll' && !setup)}
|
||||
on:click={verify}>{$i18n.t(busy ? 'Verifying…' : 'Continue')}</button
|
||||
>
|
||||
|
|
@ -169,13 +169,13 @@
|
|||
>
|
||||
<button
|
||||
type="button"
|
||||
class="transition-colors hover:text-gray-900 dark:hover:text-white"
|
||||
class="transition-colors hover:text-gray-700 dark:hover:text-gray-200"
|
||||
disabled={busy}
|
||||
on:click={onCancel}>{$i18n.t('Back to sign in')}</button
|
||||
>
|
||||
{#if challenge.next_step === 'verify'}<button
|
||||
type="button"
|
||||
class="transition-colors hover:text-gray-900 dark:hover:text-white"
|
||||
class="transition-colors hover:text-gray-700 dark:hover:text-gray-200"
|
||||
disabled={busy}
|
||||
on:click={() => {
|
||||
recovery = !recovery;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
<div class="space-y-3 text-left text-xs">
|
||||
<div>
|
||||
<h2 class="text-base font-medium tracking-tight text-gray-900 dark:text-white">
|
||||
<h2 class="text-base font-medium tracking-tight text-gray-700 dark:text-gray-300">
|
||||
{$i18n.t('Save your recovery codes')}
|
||||
</h2>
|
||||
<p class="mt-1 text-xs leading-5 text-gray-500 dark:text-gray-400">
|
||||
|
|
@ -31,7 +31,7 @@
|
|||
</ul>
|
||||
<button
|
||||
type="button"
|
||||
class="text-[0.6875rem] text-gray-500 transition-colors hover:text-gray-900 dark:text-gray-400 dark:hover:text-white"
|
||||
class="text-[0.6875rem] text-gray-500 transition-colors hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
|
||||
on:click={download}>{$i18n.t('Download recovery codes')}</button
|
||||
>
|
||||
<label class="flex items-center gap-2 text-[0.6875rem] text-gray-600 dark:text-gray-400"
|
||||
|
|
@ -41,10 +41,10 @@
|
|||
bind:checked={saved}
|
||||
/>{$i18n.t('I have saved my recovery codes')}</label
|
||||
>
|
||||
<div class="flex justify-end text-black dark:text-white">
|
||||
<div class="flex justify-end text-gray-700 dark:text-gray-300">
|
||||
<button
|
||||
type="button"
|
||||
class="bg-gray-700/5 hover:bg-gray-700/10 dark:bg-gray-100/5 dark:hover:bg-gray-100/10 dark:text-gray-300 dark:hover:text-white transition w-full rounded-full font-normal text-[0.8125rem] leading-5 py-2.5 disabled:opacity-50 flex justify-center"
|
||||
class="bg-gray-700/5 hover:bg-gray-700/10 dark:bg-gray-100/5 dark:hover:bg-gray-100/10 dark:text-gray-300 dark:hover:text-gray-200 transition w-full rounded-full font-normal text-[0.8125rem] leading-5 py-2.5 disabled:opacity-50 flex justify-center"
|
||||
disabled={!saved}
|
||||
on:click={onContinue}>{$i18n.t('Continue')}</button
|
||||
>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
let reauthenticate = false;
|
||||
let show = false;
|
||||
const actionButtonClass =
|
||||
'text-xs text-gray-500 transition-colors hover:text-gray-900 disabled:opacity-40 dark:text-gray-500 dark:hover:text-white';
|
||||
'text-xs text-gray-500 transition-colors hover:text-gray-700 disabled:opacity-40 dark:text-gray-500 dark:hover:text-gray-200';
|
||||
onMount(async () => {
|
||||
try {
|
||||
status = await mfaRequest('status', undefined, localStorage.token);
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@
|
|||
} from '$lib/utils/localizedContent';
|
||||
|
||||
import EllipsisHorizontal from '../icons/EllipsisHorizontal.svelte';
|
||||
import ArrowRight from '../icons/ArrowRight.svelte';
|
||||
import CheckCircle from '../icons/CheckCircle.svelte';
|
||||
import Minus from '../icons/Minus.svelte';
|
||||
import ModelMenu from './Models/ModelMenu.svelte';
|
||||
|
|
@ -80,6 +81,7 @@
|
|||
let showModelDeleteConfirm = false;
|
||||
|
||||
let selectedModel = null;
|
||||
let updatingModels: Record<string, boolean> = {};
|
||||
|
||||
let groupIds = [];
|
||||
|
||||
|
|
@ -151,6 +153,43 @@
|
|||
return target instanceof Element && !!target.closest('button, a, input, [role="menu"]');
|
||||
};
|
||||
|
||||
const confirmDeleteModel = (model: any) => {
|
||||
selectedModel = model;
|
||||
showModelDeleteConfirm = true;
|
||||
};
|
||||
|
||||
const toggleModelHandler = async (model: any) => {
|
||||
if (updatingModels[model.id]) return;
|
||||
const previousState = !model.is_active;
|
||||
updatingModels = { ...updatingModels, [model.id]: true };
|
||||
|
||||
try {
|
||||
const updatedModel = await toggleModelById(localStorage.token, model.id);
|
||||
model.is_active = updatedModel.is_active;
|
||||
models = models;
|
||||
} catch (error: any) {
|
||||
model.is_active = previousState;
|
||||
models = models;
|
||||
toast.error(`${error?.detail ?? error}`);
|
||||
return;
|
||||
} finally {
|
||||
updatingModels = { ...updatingModels, [model.id]: false };
|
||||
}
|
||||
|
||||
try {
|
||||
_models.set(
|
||||
await getModels(
|
||||
localStorage.token,
|
||||
$config?.features?.enable_direct_connections
|
||||
? ($settings?.directConnections ?? null)
|
||||
: null
|
||||
)
|
||||
);
|
||||
} catch (error: any) {
|
||||
toast.error(`${error?.detail ?? error}`);
|
||||
}
|
||||
};
|
||||
|
||||
const getModelList = async () => {
|
||||
if (!loaded) return;
|
||||
|
||||
|
|
@ -662,11 +701,10 @@
|
|||
{#if models !== null}
|
||||
{#if (models ?? []).length !== 0}
|
||||
<div class="my-1" id="model-list">
|
||||
<div
|
||||
class="flex w-full items-center gap-2 px-1.5 pb-0.5 text-xs text-gray-400 dark:text-gray-600"
|
||||
>
|
||||
<div class="flex items-center gap-3 px-2 pb-0.5 text-xs text-gray-400 dark:text-gray-500">
|
||||
<span>{$i18n.t('Sort by')}</span>
|
||||
<button
|
||||
class="flex min-w-0 flex-1 items-center gap-1 py-0.5 text-left"
|
||||
class="flex items-center gap-1 py-0.5"
|
||||
type="button"
|
||||
on:click={() => setSortKey('name')}
|
||||
>
|
||||
|
|
@ -680,10 +718,8 @@
|
|||
{/if}
|
||||
</button>
|
||||
|
||||
<div class="hidden w-44 shrink-0 md:block"></div>
|
||||
|
||||
<button
|
||||
class="flex w-36 shrink-0 items-center justify-end gap-1 py-0.5 text-right"
|
||||
class="flex items-center gap-1 py-0.5"
|
||||
type="button"
|
||||
on:click={() => setSortKey('updated_at')}
|
||||
>
|
||||
|
|
@ -702,7 +738,7 @@
|
|||
{#each models as model (model.id)}
|
||||
{@const localizedModelName = resolveLocalizedModelName(model, $i18n.language)}
|
||||
<div
|
||||
class="group flex min-h-8 w-full items-center gap-2 overflow-hidden rounded-xl px-2 py-1 text-left {model.write_access
|
||||
class="group flex min-h-8 w-full items-center gap-1.5 overflow-hidden rounded-xl px-2 py-0.5 text-left {model.write_access
|
||||
? 'cursor-pointer'
|
||||
: ''} {model?.meta?.hidden ? 'opacity-50 dark:opacity-50' : ''}"
|
||||
id="model-item-{model.id}"
|
||||
|
|
@ -720,7 +756,7 @@
|
|||
}
|
||||
}}
|
||||
>
|
||||
<div class="mr-1 shrink-0 self-center">
|
||||
<div class="shrink-0 self-center">
|
||||
<div class="{model.is_active ? '' : 'opacity-50 dark:opacity-50'} bg-transparent">
|
||||
<img
|
||||
src={`${WEBUI_API_BASE_URL}/models/model/profile/image?id=${model.id}&lang=${$i18n.language}`}
|
||||
|
|
@ -741,18 +777,36 @@
|
|||
<div class="flex min-w-0 flex-1 items-center gap-1 overflow-hidden">
|
||||
<div class="flex min-w-0 flex-1 flex-col overflow-hidden">
|
||||
<div class="flex min-w-0 items-center gap-2 overflow-hidden">
|
||||
<div class="flex min-w-0 flex-1 items-center gap-2 overflow-hidden">
|
||||
<div class="flex min-w-0 flex-1 items-center gap-1 overflow-hidden">
|
||||
<Tooltip
|
||||
content={localizedModelName}
|
||||
className="min-w-0"
|
||||
placement="top-start"
|
||||
>
|
||||
{#if model.write_access}
|
||||
<a
|
||||
href={`/workspace/models/edit?id=${encodeURIComponent(model.id)}`}
|
||||
class="focus-ring block truncate rounded text-[0.8125rem] leading-5 text-gray-800 group-hover:underline focus-visible:underline dark:text-gray-200"
|
||||
>{localizedModelName}</a
|
||||
>
|
||||
{:else}
|
||||
<span
|
||||
class="block truncate text-[0.8125rem] leading-5 text-gray-800 dark:text-gray-200"
|
||||
>{localizedModelName}</span
|
||||
>
|
||||
{/if}
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
content={$i18n.t('Try in chat')}
|
||||
className="-ml-0.5 h-5 w-4 shrink-0"
|
||||
tippyOptions={{ trigger: 'mouseenter focusin' }}
|
||||
>
|
||||
<a
|
||||
href={`/?model=${encodeURIComponent(model.id)}`}
|
||||
class="block truncate text-[0.8125rem] leading-5 text-gray-800 group-hover:underline dark:text-gray-200"
|
||||
aria-label={`${$i18n.t('Try in chat')}: ${localizedModelName}`}
|
||||
class="focus-ring flex h-5 w-4 items-center justify-center rounded text-gray-400 hover:text-gray-700 dark:text-gray-500 dark:hover:text-gray-200"
|
||||
><ArrowRight className="size-3 -rotate-45" strokeWidth="2" /></a
|
||||
>
|
||||
{localizedModelName}
|
||||
</a>
|
||||
</Tooltip>
|
||||
|
||||
<div
|
||||
|
|
@ -761,12 +815,13 @@
|
|||
{model.id}
|
||||
</div>
|
||||
|
||||
<Tooltip content={dayjs(model.updated_at * 1000).format('LLLL')}>
|
||||
<div
|
||||
class="shrink-0 truncate text-[0.6875rem] leading-5 text-gray-400 dark:text-gray-600"
|
||||
<Tooltip
|
||||
content={dayjs(model.updated_at * 1000).format('LLLL')}
|
||||
className="hidden shrink-0 sm:flex"
|
||||
>
|
||||
<span class="text-[0.6875rem] leading-5 text-gray-400 dark:text-gray-500"
|
||||
>{dayjs(model.updated_at * 1000).fromNow()}</span
|
||||
>
|
||||
{dayjs(model.updated_at * 1000).fromNow()}
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
{#if !model.write_access}
|
||||
|
|
@ -811,7 +866,7 @@
|
|||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<div class="ml-2 flex shrink-0 flex-row items-center self-center">
|
||||
<div class="ml-1 flex shrink-0 flex-row items-center self-center">
|
||||
{#if shiftKey && model.write_access}
|
||||
<Tooltip content={model?.meta?.hidden ? $i18n.t('Show') : $i18n.t('Hide')}>
|
||||
<button
|
||||
|
|
@ -840,14 +895,14 @@
|
|||
on:click={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
deleteModelHandler(model);
|
||||
confirmDeleteModel(model);
|
||||
}}
|
||||
>
|
||||
<GarbageBin className="size-4" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
{:else}
|
||||
<div class="flex shrink-0 flex-row items-center gap-1.5 self-center">
|
||||
<div class="flex shrink-0 flex-row items-center gap-1 self-center">
|
||||
<ModelMenu
|
||||
user={$user}
|
||||
{model}
|
||||
|
|
@ -874,8 +929,7 @@
|
|||
copyLinkHandler(model);
|
||||
}}
|
||||
deleteHandler={() => {
|
||||
selectedModel = model;
|
||||
showModelDeleteConfirm = true;
|
||||
confirmDeleteModel(model);
|
||||
}}
|
||||
onClose={() => {}}
|
||||
>
|
||||
|
|
@ -887,32 +941,20 @@
|
|||
</ModelMenu>
|
||||
|
||||
{#if model.write_access}
|
||||
<button
|
||||
class="flex h-6 items-center"
|
||||
type="button"
|
||||
on:click={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}}
|
||||
<Tooltip
|
||||
content={updatingModels[model.id]
|
||||
? $i18n.t('Saving...')
|
||||
: model.is_active
|
||||
? $i18n.t('Enabled')
|
||||
: $i18n.t('Disabled')}
|
||||
>
|
||||
<Tooltip
|
||||
content={model.is_active ? $i18n.t('Enabled') : $i18n.t('Disabled')}
|
||||
>
|
||||
<Switch
|
||||
bind:state={model.is_active}
|
||||
on:change={async () => {
|
||||
toggleModelById(localStorage.token, model.id);
|
||||
_models.set(
|
||||
await getModels(
|
||||
localStorage.token,
|
||||
$config?.features?.enable_direct_connections &&
|
||||
($settings?.directConnections ?? null)
|
||||
)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
</button>
|
||||
<Switch
|
||||
bind:state={model.is_active}
|
||||
disabled={updatingModels[model.id] ?? false}
|
||||
ariaLabel={`${$i18n.t('Enabled')}: ${localizedModelName}`}
|
||||
on:change={() => toggleModelHandler(model)}
|
||||
/>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,9 @@
|
|||
|
||||
import { config, user as currentUser, pinnedModels, settings } from '$lib/stores';
|
||||
import Link from '$lib/components/icons/Link.svelte';
|
||||
import { resolveLocalizedModelName } from '$lib/utils/localizedContent';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const i18n = getContext<any>('i18n');
|
||||
|
||||
export let user;
|
||||
export let model;
|
||||
|
|
@ -54,6 +55,11 @@
|
|||
>
|
||||
<Tooltip content={$i18n.t('More')}>
|
||||
<button
|
||||
type="button"
|
||||
class="focus-ring rounded-lg"
|
||||
aria-label={`${$i18n.t('More')}: ${resolveLocalizedModelName(model, $i18n.language)}`}
|
||||
aria-haspopup="menu"
|
||||
aria-expanded={show}
|
||||
on:click={(e) => {
|
||||
e.stopPropagation();
|
||||
show = !show;
|
||||
|
|
|
|||
|
|
@ -3128,6 +3128,7 @@
|
|||
"Sonar Pro": "",
|
||||
"Sonar Reasoning": "",
|
||||
"Sonar Reasoning Pro": "",
|
||||
"Sort by": "",
|
||||
"Sort": "",
|
||||
"Source": "",
|
||||
"Specific users or groups": "",
|
||||
|
|
@ -3326,6 +3327,7 @@
|
|||
"Transformers": "",
|
||||
"Translation key": "",
|
||||
"Trial License": "",
|
||||
"Try in chat": "",
|
||||
"Try adjusting your search or filter to find what you are looking for.": "",
|
||||
"Try Again": "",
|
||||
"TTS Voice": "",
|
||||
|
|
|
|||
|
|
@ -274,7 +274,9 @@
|
|||
/>
|
||||
</div>
|
||||
{:else if pendingApproval}
|
||||
<p class="my-auto">{$i18n.t('Your account is awaiting administrator approval.')}</p>
|
||||
<p class="my-auto text-sm text-gray-500 dark:text-gray-400">
|
||||
{$i18n.t('Your account is awaiting administrator approval.')}
|
||||
</p>
|
||||
{:else if ($config?.features.auth_trusted_header ?? false) || $config?.features.auth === false}
|
||||
<div class=" my-auto pb-10 w-full sm:max-w-md">
|
||||
<div
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue