diff --git a/src/app.css b/src/app.css index ca5249bbd..9d073d05d 100644 --- a/src/app.css +++ b/src/app.css @@ -16,6 +16,12 @@ font-display: swap; } +@font-face { + font-family: 'InstrumentSerif'; + src: url('/assets/fonts/InstrumentSerif-Regular.ttf'); + font-display: swap; +} + html { word-break: break-word; } @@ -26,6 +32,10 @@ code { width: auto; } +.font-secondary { + font-family: 'InstrumentSerif', sans-serif; +} + math { margin-top: 1rem; } diff --git a/src/lib/components/common/SlideShow.svelte b/src/lib/components/common/SlideShow.svelte new file mode 100644 index 000000000..cf48b62b5 --- /dev/null +++ b/src/lib/components/common/SlideShow.svelte @@ -0,0 +1,39 @@ +<script lang="ts"> + import { onMount } from 'svelte'; + + export let imageUrls = [ + '/assets/images/adam.jpg', + '/assets/images/galaxy.jpg', + '/assets/images/earth.jpg', + '/assets/images/space.jpg' + ]; + export let duration = 5000; + let selectedImageIdx = 0; + + onMount(() => { + setInterval(() => { + selectedImageIdx = (selectedImageIdx + 1) % 5; + }, duration); + }); +</script> + +{#each imageUrls as imageUrl, idx (idx)} + <div + class="image w-full h-full absolute top-0 left-0 bg-cover bg-center transition-opacity duration-1000" + style="opacity: {selectedImageIdx === idx ? 1 : 0}; background-image: url('{imageUrl}')" + ></div> +{/each} + +<style> + .image { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-size: cover; + background-position: center; /* Center the background images */ + transition: opacity 1s ease-in-out; /* Smooth fade effect */ + opacity: 0; /* Make images initially not visible */ + } +</style> diff --git a/src/routes/auth/+page.svelte b/src/routes/auth/+page.svelte index c4700d264..5c2f3d179 100644 --- a/src/routes/auth/+page.svelte +++ b/src/routes/auth/+page.svelte @@ -9,6 +9,7 @@ import { generateInitialsImage, canvasPixelTest } from '$lib/utils'; import { page } from '$app/stores'; import { getBackendConfig } from '$lib/apis'; + import SlideShow from '$lib/components/common/SlideShow.svelte'; const i18n = getContext('i18n'); @@ -104,259 +105,248 @@ </title> </svelte:head> -{#if loaded} - <div class="fixed m-10 z-50"> - <div class="flex space-x-2"> - <div class=" self-center"> - <img - crossorigin="anonymous" - src="{WEBUI_BASE_URL}/static/favicon.png" - class=" w-8 rounded-full" - alt="logo" - /> +<div class="w-full h-screen max-h-[100dvh] text-white relative"> + <div class="w-full h-full absolute top-0 left-0 bg-white dark:bg-black"></div> + + <!-- <div + class="w-full h-full absolute top-0 left-0 bg-gradient-to-t dark:from-black dark:to-black/60" + ></div> --> + + <!-- <div class="w-full h-full absolute top-0 left-0 backdrop-blur-xl bg-black/50"></div> --> + + {#if loaded} + <div class="fixed m-10 z-50"> + <div class="flex space-x-2"> + <div class=" self-center"> + <img + crossorigin="anonymous" + src="{WEBUI_BASE_URL}/static/favicon.png" + class=" w-6 rounded-full" + alt="logo" + /> + </div> </div> </div> - </div> - <div class=" bg-white dark:bg-gray-950 min-h-screen w-full flex justify-center font-primary"> - <div class="w-full sm:max-w-md px-10 min-h-screen flex flex-col text-center"> - {#if ($config?.features.auth_trusted_header ?? false) || $config?.features.auth === false} - <div class=" my-auto pb-10 w-full"> - <div - class="flex items-center justify-center gap-3 text-xl sm:text-2xl text-center font-semibold dark:text-gray-200" - > - <div> - {$i18n.t('Signing in to {{WEBUI_NAME}}', { WEBUI_NAME: $WEBUI_NAME })} - </div> + <div + class="fixed bg-transparent min-h-screen w-full flex justify-center font-primary z-50 text-black dark:text-white" + > + <div class="w-full sm:max-w-md px-10 min-h-screen flex flex-col text-center"> + {#if ($config?.features.auth_trusted_header ?? false) || $config?.features.auth === false} + <div class=" my-auto pb-10 w-full"> + <div + class="flex items-center justify-center gap-3 text-xl sm:text-2xl text-center font-semibold dark:text-gray-200" + > + <div> + {$i18n.t('Signing in to {{WEBUI_NAME}}', { WEBUI_NAME: $WEBUI_NAME })} + </div> - <div> - <Spinner /> + <div> + <Spinner /> + </div> </div> </div> - </div> - {:else} - <div class=" my-auto pb-10 w-full dark:text-gray-100"> - <form - class=" flex flex-col justify-center" - on:submit|preventDefault={() => { - submitHandler(); - }} - > - <div class="mb-1"> - <div class=" text-2xl font-medium"> - {#if mode === 'signin'} - {$i18n.t(`Sign in to {{WEBUI_NAME}}`, { WEBUI_NAME: $WEBUI_NAME })} - {:else} - {$i18n.t(`Sign up to {{WEBUI_NAME}}`, { WEBUI_NAME: $WEBUI_NAME })} + {:else} + <div class=" my-auto pb-10 w-full dark:text-gray-100"> + <form + class=" flex flex-col justify-center" + on:submit|preventDefault={() => { + submitHandler(); + }} + > + <div class="mb-1"> + <div class=" text-2xl font-medium"> + {#if mode === 'signin'} + {$i18n.t(`Sign in to {{WEBUI_NAME}}`, { WEBUI_NAME: $WEBUI_NAME })} + {:else} + {$i18n.t(`Sign up to {{WEBUI_NAME}}`, { WEBUI_NAME: $WEBUI_NAME })} + {/if} + </div> + + {#if mode === 'signup'} + <div class=" mt-1 text-xs font-medium text-gray-500"> + ⓘ {$WEBUI_NAME} + {$i18n.t( + 'does not make any external connections, and your data stays securely on your locally hosted server.' + )} + </div> {/if} </div> - {#if mode === 'signup'} - <div class=" mt-1 text-xs font-medium text-gray-500"> - ⓘ {$WEBUI_NAME} - {$i18n.t( - 'does not make any external connections, and your data stays securely on your locally hosted server.' - )} - </div> - {/if} - </div> + {#if $config?.features.enable_login_form} + <div class="flex flex-col mt-4"> + {#if mode === 'signup'} + <div class="mb-2"> + <div class=" text-sm font-medium text-left mb-1">{$i18n.t('Name')}</div> + <input + bind:value={name} + type="text" + class="my-0.5 w-full text-sm outline-none bg-transparent" + autocomplete="name" + placeholder={$i18n.t('Enter Your Full Name')} + required + /> + </div> + {/if} - {#if $config?.features.enable_login_form} - <div class="flex flex-col mt-4"> - {#if mode === 'signup'} - <div> - <div class=" text-sm font-medium text-left mb-1">{$i18n.t('Name')}</div> + <div class="mb-2"> + <div class=" text-sm font-medium text-left mb-1">{$i18n.t('Email')}</div> <input - bind:value={name} - type="text" - class="w-full text-sm outline-none bg-transparent" - autocomplete="name" - placeholder={$i18n.t('Enter Your Full Name')} + bind:value={email} + type="email" + class="my-0.5 w-full text-sm outline-none bg-transparent" + autocomplete="email" + placeholder={$i18n.t('Enter Your Email')} required /> </div> - <hr class=" my-3 dark:border-gray-900" /> - {/if} + <div> + <div class=" text-sm font-medium text-left mb-1">{$i18n.t('Password')}</div> - <div class="mb-2"> - <div class=" text-sm font-medium text-left mb-1">{$i18n.t('Email')}</div> - <input - bind:value={email} - type="email" - class=" w-full text-sm outline-none bg-transparent" - autocomplete="email" - placeholder={$i18n.t('Enter Your Email')} - required - /> - </div> - - <div> - <div class=" text-sm font-medium text-left mb-1">{$i18n.t('Password')}</div> - - <input - bind:value={password} - type="password" - class="w-full text-sm outline-none bg-transparent" - placeholder={$i18n.t('Enter Your Password')} - autocomplete="current-password" - required - /> - </div> - </div> - {/if} - - {#if $config?.features.enable_login_form} - <div class="mt-5"> - <button - class=" bg-gray-900 hover:bg-gray-800 w-full rounded-2xl text-white font-medium text-sm py-2.5 transition" - type="submit" - > - {mode === 'signin' ? $i18n.t('Sign in') : $i18n.t('Create Account')} - </button> - - {#if $config?.features.enable_signup} - <div class=" mt-4 text-sm text-center"> - {mode === 'signin' - ? $i18n.t("Don't have an account?") - : $i18n.t('Already have an account?')} - - <button - class=" font-medium underline" - type="button" - on:click={() => { - if (mode === 'signin') { - mode = 'signup'; - } else { - mode = 'signin'; - } - }} - > - {mode === 'signin' ? $i18n.t('Sign up') : $i18n.t('Sign in')} - </button> + <input + bind:value={password} + type="password" + class="my-0.5 w-full text-sm outline-none bg-transparent" + placeholder={$i18n.t('Enter Your Password')} + autocomplete="current-password" + required + /> </div> + </div> + {/if} + + {#if $config?.features.enable_login_form} + <div class="mt-5"> + <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-medium text-sm py-2.5" + type="submit" + > + {mode === 'signin' ? $i18n.t('Sign in') : $i18n.t('Create Account')} + </button> + + {#if $config?.features.enable_signup} + <div class=" mt-4 text-sm text-center"> + {mode === 'signin' + ? $i18n.t("Don't have an account?") + : $i18n.t('Already have an account?')} + + <button + class=" font-medium underline" + type="button" + on:click={() => { + if (mode === 'signin') { + mode = 'signup'; + } else { + mode = 'signin'; + } + }} + > + {mode === 'signin' ? $i18n.t('Sign up') : $i18n.t('Sign in')} + </button> + </div> + {/if} + </div> + {/if} + </form> + + {#if Object.keys($config?.oauth?.providers ?? {}).length > 0} + <div class="inline-flex items-center justify-center w-full"> + <hr class="w-32 h-px my-4 border-0 dark:bg-gray-100/10 bg-gray-700/10" /> + {#if $config?.features.enable_login_form} + <span + class="px-3 text-sm font-medium text-gray-900 dark:text-white bg-transparent" + >{$i18n.t('or')}</span + > + {/if} + + <hr class="w-32 h-px my-4 border-0 dark:bg-gray-100/10 bg-gray-700/10" /> + </div> + <div class="flex flex-col space-y-2"> + {#if $config?.oauth?.providers?.google} + <button + class="flex items-center px-6 border-2 dark:border-gray-800 duration-300 dark:bg-gray-900 hover:bg-gray-100 dark:hover:bg-gray-800 w-full rounded-2xl dark:text-white text-sm py-3 transition" + on:click={() => { + window.location.href = `${WEBUI_BASE_URL}/oauth/google/login`; + }} + > + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" class="size-6 mr-3"> + <path + fill="#EA4335" + d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z" + /><path + fill="#4285F4" + d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z" + /><path + fill="#FBBC05" + d="M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z" + /><path + fill="#34A853" + d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z" + /><path fill="none" d="M0 0h48v48H0z" /> + </svg> + <span>{$i18n.t('Continue with {{provider}}', { provider: 'Google' })}</span> + </button> + {/if} + {#if $config?.oauth?.providers?.microsoft} + <button + class="flex items-center px-6 border-2 dark:border-gray-800 duration-300 dark:bg-gray-900 hover:bg-gray-100 dark:hover:bg-gray-800 w-full rounded-2xl dark:text-white text-sm py-3 transition" + on:click={() => { + window.location.href = `${WEBUI_BASE_URL}/oauth/microsoft/login`; + }} + > + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 21 21" class="size-6 mr-3"> + <rect x="1" y="1" width="9" height="9" fill="#f25022" /><rect + x="1" + y="11" + width="9" + height="9" + fill="#00a4ef" + /><rect x="11" y="1" width="9" height="9" fill="#7fba00" /><rect + x="11" + y="11" + width="9" + height="9" + fill="#ffb900" + /> + </svg> + <span>{$i18n.t('Continue with {{provider}}', { provider: 'Microsoft' })}</span> + </button> + {/if} + {#if $config?.oauth?.providers?.oidc} + <button + class="flex items-center px-6 border-2 dark:border-gray-800 duration-300 dark:bg-gray-900 hover:bg-gray-100 dark:hover:bg-gray-800 w-full rounded-2xl dark:text-white text-sm py-3 transition" + on:click={() => { + window.location.href = `${WEBUI_BASE_URL}/oauth/oidc/login`; + }} + > + <svg + xmlns="http://www.w3.org/2000/svg" + fill="none" + viewBox="0 0 24 24" + stroke-width="1.5" + stroke="currentColor" + class="size-6 mr-3" + > + <path + stroke-linecap="round" + stroke-linejoin="round" + d="M15.75 5.25a3 3 0 0 1 3 3m3 0a6 6 0 0 1-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1 1 21.75 8.25Z" + /> + </svg> + + <span + >{$i18n.t('Continue with {{provider}}', { + provider: $config?.oauth?.providers?.oidc ?? 'SSO' + })}</span + > + </button> {/if} </div> {/if} - </form> - - {#if Object.keys($config?.oauth?.providers ?? {}).length > 0} - <div class="inline-flex items-center justify-center w-full"> - <hr class="w-64 h-px my-8 bg-gray-200 border-0 dark:bg-gray-700" /> - {#if $config?.features.enable_login_form} - <span - class="absolute px-3 font-medium text-gray-900 -translate-x-1/2 bg-white left-1/2 dark:text-white dark:bg-gray-950" - >{$i18n.t('or')}</span - > - {/if} - </div> - <div class="flex flex-col space-y-2"> - {#if $config?.oauth?.providers?.google} - <button - class="flex items-center px-6 border-2 dark:border-gray-800 duration-300 dark:bg-gray-900 hover:bg-gray-100 dark:hover:bg-gray-800 w-full rounded-2xl dark:text-white text-sm py-3 transition" - on:click={() => { - window.location.href = `${WEBUI_BASE_URL}/oauth/google/login`; - }} - > - <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" class="size-6 mr-3"> - <path - fill="#EA4335" - d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z" - /><path - fill="#4285F4" - d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z" - /><path - fill="#FBBC05" - d="M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z" - /><path - fill="#34A853" - d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z" - /><path fill="none" d="M0 0h48v48H0z" /> - </svg> - <span>{$i18n.t('Continue with {{provider}}', { provider: 'Google' })}</span> - </button> - {/if} - {#if $config?.oauth?.providers?.microsoft} - <button - class="flex items-center px-6 border-2 dark:border-gray-800 duration-300 dark:bg-gray-900 hover:bg-gray-100 dark:hover:bg-gray-800 w-full rounded-2xl dark:text-white text-sm py-3 transition" - on:click={() => { - window.location.href = `${WEBUI_BASE_URL}/oauth/microsoft/login`; - }} - > - <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 21 21" class="size-6 mr-3"> - <rect x="1" y="1" width="9" height="9" fill="#f25022" /><rect - x="1" - y="11" - width="9" - height="9" - fill="#00a4ef" - /><rect x="11" y="1" width="9" height="9" fill="#7fba00" /><rect - x="11" - y="11" - width="9" - height="9" - fill="#ffb900" - /> - </svg> - <span>{$i18n.t('Continue with {{provider}}', { provider: 'Microsoft' })}</span> - </button> - {/if} - {#if $config?.oauth?.providers?.oidc} - <button - class="flex items-center px-6 border-2 dark:border-gray-800 duration-300 dark:bg-gray-900 hover:bg-gray-100 dark:hover:bg-gray-800 w-full rounded-2xl dark:text-white text-sm py-3 transition" - on:click={() => { - window.location.href = `${WEBUI_BASE_URL}/oauth/oidc/login`; - }} - > - <svg - xmlns="http://www.w3.org/2000/svg" - fill="none" - viewBox="0 0 24 24" - stroke-width="1.5" - stroke="currentColor" - class="size-6 mr-3" - > - <path - stroke-linecap="round" - stroke-linejoin="round" - d="M15.75 5.25a3 3 0 0 1 3 3m3 0a6 6 0 0 1-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1 1 21.75 8.25Z" - /> - </svg> - - <span - >{$i18n.t('Continue with {{provider}}', { - provider: $config?.oauth?.providers?.oidc ?? 'SSO' - })}</span - > - </button> - {/if} - </div> - {/if} - </div> - {/if} + </div> + {/if} + </div> </div> - </div> -{/if} - -<style> - .font-mona { - font-family: - 'Mona Sans', - -apple-system, - 'Inter', - ui-sans-serif, - system-ui, - 'Segoe UI', - Roboto, - Ubuntu, - Cantarell, - 'Noto Sans', - sans-serif, - 'Helvetica Neue', - Arial, - 'Apple Color Emoji', - 'Segoe UI Emoji', - 'Segoe UI Symbol', - 'Noto Color Emoji'; - } -</style> + {/if} +</div> diff --git a/static/assets/fonts/InstrumentSerif-Italic.ttf b/static/assets/fonts/InstrumentSerif-Italic.ttf new file mode 100644 index 000000000..32c6b8e4a Binary files /dev/null and b/static/assets/fonts/InstrumentSerif-Italic.ttf differ diff --git a/static/assets/fonts/InstrumentSerif-Regular.ttf b/static/assets/fonts/InstrumentSerif-Regular.ttf new file mode 100644 index 000000000..7efd9364c Binary files /dev/null and b/static/assets/fonts/InstrumentSerif-Regular.ttf differ diff --git a/static/assets/images/adam.jpg b/static/assets/images/adam.jpg new file mode 100644 index 000000000..2d637d000 Binary files /dev/null and b/static/assets/images/adam.jpg differ diff --git a/static/assets/images/earth.jpg b/static/assets/images/earth.jpg new file mode 100644 index 000000000..39e51d5d7 Binary files /dev/null and b/static/assets/images/earth.jpg differ diff --git a/static/assets/images/galaxy.jpg b/static/assets/images/galaxy.jpg new file mode 100644 index 000000000..b4560af0b Binary files /dev/null and b/static/assets/images/galaxy.jpg differ diff --git a/static/assets/images/space.jpg b/static/assets/images/space.jpg new file mode 100644 index 000000000..5e05a01c3 Binary files /dev/null and b/static/assets/images/space.jpg differ