Merge remote-tracking branch 'upstream/dev' into feat/oauth

This commit is contained in:
Jun Siang Cheah
2024-06-16 08:31:05 +01:00
169 changed files with 13859 additions and 3870 deletions

View File

@@ -8,12 +8,15 @@
import { goto } from '$app/navigation';
import { getModels as _getModels } from '$lib/apis';
import { getOllamaVersion } from '$lib/apis/ollama';
import { getPrompts } from '$lib/apis/prompts';
import { getDocs } from '$lib/apis/documents';
import { getAllChatTags } from '$lib/apis/chats';
import { getPrompts } from '$lib/apis/prompts';
import { getDocs } from '$lib/apis/documents';
import { getTools } from '$lib/apis/tools';
import { getBanners } from '$lib/apis/configs';
import { getUserSettings } from '$lib/apis/users';
import {
user,
showSettings,
@@ -24,32 +27,22 @@
tags,
banners,
showChangelog,
config
config,
showCallOverlay,
tools
} from '$lib/stores';
import { REQUIRED_OLLAMA_VERSION, WEBUI_API_BASE_URL } from '$lib/constants';
import { compareVersion } from '$lib/utils';
import SettingsModal from '$lib/components/chat/SettingsModal.svelte';
import Sidebar from '$lib/components/layout/Sidebar.svelte';
import ShortcutsModal from '$lib/components/chat/ShortcutsModal.svelte';
import ChangelogModal from '$lib/components/ChangelogModal.svelte';
import Tooltip from '$lib/components/common/Tooltip.svelte';
import { getBanners } from '$lib/apis/configs';
import { getUserSettings } from '$lib/apis/users';
import Help from '$lib/components/layout/Help.svelte';
import AccountPending from '$lib/components/layout/Overlay/AccountPending.svelte';
import { error } from '@sveltejs/kit';
const i18n = getContext('i18n');
let ollamaVersion = '';
let loaded = false;
let showShortcutsButtonElement: HTMLButtonElement;
let DB = null;
let localDBChats = [];
let showShortcuts = false;
const getModels = async () => {
return _getModels(localStorage.token);
};
@@ -97,6 +90,9 @@
(async () => {
documents.set(await getDocs(localStorage.token));
})(),
(async () => {
tools.set(await getTools(localStorage.token));
})(),
(async () => {
banners.set(await getBanners(localStorage.token));
})(),
@@ -181,7 +177,6 @@
});
</script>
<Help />
<SettingsModal bind:show={$showSettings} />
<ChangelogModal bind:show={$showChangelog} />

View File

@@ -1,5 +1,7 @@
<script lang="ts">
import Chat from '$lib/components/chat/Chat.svelte';
import Help from '$lib/components/layout/Help.svelte';
</script>
<Help />
<Chat />

View File

@@ -14,7 +14,11 @@
</title>
</svelte:head>
<div class=" flex flex-col w-full min-h-screen max-h-screen">
<div
class=" flex flex-col w-full min-h-screen max-h-screen {$showSidebar
? 'md:max-w-[calc(100%-260px)]'
: ''}"
>
<div class=" px-4 pt-3 mt-0.5 mb-1">
<div class=" flex items-center gap-1">
<div class="{$showSidebar ? 'md:hidden' : ''} mr-1 self-start flex flex-none items-center">
@@ -30,29 +34,29 @@
</div>
</button>
</div>
<div class="flex items-center text-xl font-semibold">{$i18n.t('Workspace')}</div>
<div class="flex items-center text-xl font-semibold">{$i18n.t('Admin Panel')}</div>
</div>
</div>
<!-- <div class="px-4 my-1">
<div class="px-4 my-1">
<div
class="flex scrollbar-none overflow-x-auto w-fit text-center text-sm font-medium rounded-xl bg-transparent/10 p-1"
>
<a
class="min-w-fit rounded-lg p-1.5 px-3 {$page.url.pathname.includes('/workspace/models')
class="min-w-fit rounded-lg p-1.5 px-3 {['/admin', '/admin/'].includes($page.url.pathname)
? 'bg-gray-50 dark:bg-gray-850'
: ''} transition"
href="/workspace/models">{$i18n.t('Models')}</a
href="/admin">{$i18n.t('Dashboard')}</a
>
<a
class="min-w-fit rounded-lg p-1.5 px-3 {$page.url.pathname.includes('/workspace/prompts')
class="min-w-fit rounded-lg p-1.5 px-3 {$page.url.pathname.includes('/admin/settings')
? 'bg-gray-50 dark:bg-gray-850'
: ''} transition"
href="/workspace/prompts">{$i18n.t('Prompts')}</a
href="/admin/settings">{$i18n.t('Settings')}</a
>
<a
<!-- <a
class="min-w-fit rounded-lg p-1.5 px-3 {$page.url.pathname.includes('/workspace/documents')
? 'bg-gray-50 dark:bg-gray-850'
: ''} transition"
@@ -66,9 +70,9 @@
? 'bg-gray-50 dark:bg-gray-850'
: ''} transition"
href="/workspace/playground">{$i18n.t('Playground')}</a
>
> -->
</div>
</div> -->
</div>
<hr class=" my-2 dark:border-gray-850" />

View File

@@ -11,17 +11,14 @@
import { toast } from 'svelte-sonner';
import { updateUserRole, getUsers, deleteUserById } from '$lib/apis/users';
import { getSignUpEnabledStatus, toggleSignUpEnabledStatus } from '$lib/apis/auths';
import MenuLines from '$lib/components/icons/MenuLines.svelte';
import EditUserModal from '$lib/components/admin/EditUserModal.svelte';
import SettingsModal from '$lib/components/admin/SettingsModal.svelte';
import Pagination from '$lib/components/common/Pagination.svelte';
import ChatBubbles from '$lib/components/icons/ChatBubbles.svelte';
import Tooltip from '$lib/components/common/Tooltip.svelte';
import UserChatsModal from '$lib/components/admin/UserChatsModal.svelte';
import AddUserModal from '$lib/components/admin/AddUserModal.svelte';
import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
const i18n = getContext('i18n');
@@ -34,7 +31,7 @@
let page = 1;
let showSettingsModal = false;
let showDeleteConfirmDialog = false;
let showAddUserModal = false;
let showUserChatsModal = false;
@@ -82,6 +79,13 @@
});
</script>
<ConfirmDialog
bind:show={showDeleteConfirmDialog}
on:confirm={() => {
deleteUserHandler(selectedUser.id);
}}
/>
{#key selectedUser}
<EditUserModal
bind:show={showEditUserModal}
@@ -100,7 +104,6 @@
}}
/>
<UserChatsModal bind:show={showUserChatsModal} user={selectedUser} />
<SettingsModal bind:show={showSettingsModal} />
{#if loaded}
<div class="mt-0.5 mb-3 gap-1 flex flex-col md:flex-row justify-between">
@@ -118,7 +121,7 @@
/>
<div class="flex gap-0.5">
<Tooltip content="Add User">
<Tooltip content={$i18n.t('Add User')}>
<button
class=" px-2 py-2 rounded-xl border border-gray-200 dark:border-gray-600 dark:border-0 hover:bg-gray-100 dark:bg-gray-850 dark:hover:bg-gray-800 transition font-medium text-sm flex items-center space-x-1"
on:click={() => {
@@ -137,28 +140,6 @@
</svg>
</button>
</Tooltip>
<Tooltip content={$i18n.t('Admin Settings')}>
<button
class=" px-2 py-2 rounded-xl border border-gray-200 dark:border-gray-600 dark:border-0 hover:bg-gray-100 dark:bg-gray-850 dark:hover:bg-gray-800 transition font-medium text-sm flex items-center space-x-1"
on:click={() => {
showSettingsModal = !showSettingsModal;
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
fill-rule="evenodd"
d="M6.955 1.45A.5.5 0 0 1 7.452 1h1.096a.5.5 0 0 1 .497.45l.17 1.699c.484.12.94.312 1.356.562l1.321-1.081a.5.5 0 0 1 .67.033l.774.775a.5.5 0 0 1 .034.67l-1.08 1.32c.25.417.44.873.561 1.357l1.699.17a.5.5 0 0 1 .45.497v1.096a.5.5 0 0 1-.45.497l-1.699.17c-.12.484-.312.94-.562 1.356l1.082 1.322a.5.5 0 0 1-.034.67l-.774.774a.5.5 0 0 1-.67.033l-1.322-1.08c-.416.25-.872.44-1.356.561l-.17 1.699a.5.5 0 0 1-.497.45H7.452a.5.5 0 0 1-.497-.45l-.17-1.699a4.973 4.973 0 0 1-1.356-.562L4.108 13.37a.5.5 0 0 1-.67-.033l-.774-.775a.5.5 0 0 1-.034-.67l1.08-1.32a4.971 4.971 0 0 1-.561-1.357l-1.699-.17A.5.5 0 0 1 1 8.548V7.452a.5.5 0 0 1 .45-.497l1.699-.17c.12-.484.312-.94.562-1.356L2.629 4.107a.5.5 0 0 1 .034-.67l.774-.774a.5.5 0 0 1 .67-.033L5.43 3.71a4.97 4.97 0 0 1 1.356-.561l.17-1.699ZM6 8c0 .538.212 1.026.558 1.385l.057.057a2 2 0 0 0 2.828-2.828l-.058-.056A2 2 0 0 0 6 8Z"
clip-rule="evenodd"
/>
</svg>
</button>
</Tooltip>
</div>
</div>
</div>
@@ -189,7 +170,7 @@
}
})
.slice((page - 1) * 20, page * 20) as user}
<tr class="bg-white border-b dark:bg-gray-900 dark:border-gray-700 text-xs">
<tr class="bg-white border-b dark:bg-gray-900 dark:border-gray-850 text-xs">
<td class="px-3 py-2 min-w-[7rem] w-28">
<button
class=" flex items-center gap-2 text-xs px-3 py-0.5 rounded-lg {user.role ===
@@ -284,7 +265,8 @@
<button
class="self-center w-fit text-sm px-2 py-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
on:click={async () => {
deleteUserHandler(user.id);
showDeleteConfirmDialog = true;
selectedUser = user;
}}
>
<svg

View File

@@ -0,0 +1,5 @@
<script>
import Settings from '$lib/components/admin/Settings.svelte';
</script>
<Settings />

View File

@@ -14,7 +14,11 @@
</title>
</svelte:head>
<div class=" flex flex-col w-full min-h-screen max-h-screen">
<div
class=" flex flex-col w-full min-h-screen max-h-screen {$showSidebar
? 'md:max-w-[calc(100%-260px)]'
: ''}"
>
<div class=" px-4 pt-3 mt-0.5 mb-1">
<div class=" flex items-center gap-1">
<div class="{$showSidebar ? 'md:hidden' : ''} mr-1 self-start flex flex-none items-center">
@@ -61,6 +65,15 @@
{$i18n.t('Documents')}
</a>
<a
class="min-w-fit rounded-lg p-1.5 px-3 {$page.url.pathname.includes('/workspace/tools')
? 'bg-gray-50 dark:bg-gray-850'
: ''} transition"
href="/workspace/tools"
>
{$i18n.t('Tools')}
</a>
<a
class="min-w-fit rounded-lg p-1.5 px-3 {$page.url.pathname.includes('/workspace/playground')
? 'bg-gray-50 dark:bg-gray-850'

View File

@@ -2,7 +2,7 @@
import { v4 as uuidv4 } from 'uuid';
import { toast } from 'svelte-sonner';
import { goto } from '$app/navigation';
import { settings, user, config, models } from '$lib/stores';
import { settings, user, config, models, tools } from '$lib/stores';
import { onMount, tick, getContext } from 'svelte';
import { addNewModel, getModelById, getModelInfos } from '$lib/apis/models';
@@ -11,6 +11,9 @@
import AdvancedParams from '$lib/components/chat/Settings/Advanced/AdvancedParams.svelte';
import Checkbox from '$lib/components/common/Checkbox.svelte';
import Tags from '$lib/components/common/Tags.svelte';
import Knowledge from '$lib/components/workspace/Models/Knowledge.svelte';
import ToolsSelector from '$lib/components/workspace/Models/ToolsSelector.svelte';
import { stringify } from 'postcss';
const i18n = getContext('i18n');
@@ -30,11 +33,6 @@
let id = '';
let name = '';
let params = {};
let capabilities = {
vision: true
};
let info = {
id: '',
base_model_id: null,
@@ -53,14 +51,21 @@
}
};
let params = {};
let capabilities = {
vision: true
};
let toolIds = [];
let knowledge = [];
$: if (name) {
id = name.replace(/\s+/g, '-').toLowerCase();
}
let baseModel = null;
$: {
baseModel = $models.find((m) => m.id === info.base_model_id);
console.log(baseModel);
const addUsage = (base_model_id) => {
const baseModel = $models.find((m) => m.id === base_model_id);
if (baseModel) {
if (baseModel.owned_by === 'openai') {
capabilities.usage = baseModel.info?.meta?.capabilities?.usage ?? false;
@@ -69,7 +74,7 @@
}
capabilities = capabilities;
}
}
};
const submitHandler = async () => {
loading = true;
@@ -77,8 +82,24 @@
info.id = id;
info.name = name;
info.meta.capabilities = capabilities;
info.params.stop = params.stop ? params.stop.split(',').filter((s) => s.trim()) : null;
if (knowledge.length > 0) {
info.meta.knowledge = knowledge;
} else {
if (info.meta.knowledge) {
delete info.meta.knowledge;
}
}
if (toolIds.length > 0) {
info.meta.toolIds = toolIds;
} else {
if (info.meta.toolIds) {
delete info.meta.toolIds;
}
}
info.params.stop = params.stop ? params.stop.split(',').filter((s) => s.trim()) : null;
Object.keys(info.params).forEach((key) => {
if (info.params[key] === '' || info.params[key] === null) {
delete info.params[key];
@@ -124,15 +145,34 @@
id = model.id;
if (model.info.base_model_id) {
const base_model = $models
.filter((m) => !m?.preset)
.find((m) =>
[model.info.base_model_id, `${model.info.base_model_id}:latest`].includes(m.id)
);
console.log('base_model', base_model);
if (!base_model) {
model.info.base_model_id = null;
} else if ($models.find((m) => m.id === `${model.info.base_model_id}:latest`)) {
model.info.base_model_id = `${model.info.base_model_id}:latest`;
}
}
params = { ...params, ...model?.info?.params };
params.stop = params?.stop ? (params?.stop ?? []).join(',') : null;
capabilities = { ...capabilities, ...(model?.info?.meta?.capabilities ?? {}) };
toolIds = model?.info?.meta?.toolIds ?? [];
info = {
...info,
...model.info
};
console.log(info);
};
onMount(async () => {
@@ -233,7 +273,7 @@
<button
class="flex space-x-1"
on:click={() => {
history.back();
goto('/workspace/models');
}}
>
<div class=" self-center">
@@ -252,7 +292,7 @@
</div>
<div class=" self-center font-medium text-sm">{$i18n.t('Back')}</div>
</button>
<!-- <hr class="my-3 dark:border-gray-700" /> -->
<!-- <hr class="my-3 dark:border-gray-850" /> -->
<form
class="flex flex-col max-w-2xl mx-auto mt-4 mb-10"
@@ -331,6 +371,9 @@
class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg"
placeholder="Select a base model (e.g. llama3, gpt-4o)"
bind:value={info.base_model_id}
on:change={(e) => {
addUsage(e.target.value);
}}
required
>
<option value={null} class=" text-gray-900">{$i18n.t('Select a base model')}</option>
@@ -416,6 +459,7 @@
{#if showAdvanced}
<div class="my-2">
<AdvancedParams
admin={true}
bind:params
on:change={(e) => {
info.params = { ...info.params, ...params };
@@ -518,6 +562,14 @@
{/if}
</div>
<div class="my-2">
<Knowledge bind:knowledge />
</div>
<div class="my-2">
<ToolsSelector bind:selectedToolIds={toolIds} tools={$tools} />
</div>
<div class="my-1">
<div class="flex w-full justify-between mb-1">
<div class=" self-center text-sm font-semibold">{$i18n.t('Capabilities')}</div>

View File

@@ -5,7 +5,7 @@
import { onMount, getContext } from 'svelte';
import { page } from '$app/stores';
import { settings, user, config, models } from '$lib/stores';
import { settings, user, config, models, tools } from '$lib/stores';
import { splitStream } from '$lib/utils';
import { getModelInfos, updateModelById } from '$lib/apis/models';
@@ -14,6 +14,8 @@
import { getModels } from '$lib/apis';
import Checkbox from '$lib/components/common/Checkbox.svelte';
import Tags from '$lib/components/common/Tags.svelte';
import Knowledge from '$lib/components/workspace/Models/Knowledge.svelte';
import ToolsSelector from '$lib/components/workspace/Models/ToolsSelector.svelte';
const i18n = getContext('i18n');
@@ -54,19 +56,37 @@
};
let params = {};
let capabilities = {
vision: true
};
let knowledge = [];
let toolIds = [];
const updateHandler = async () => {
loading = true;
info.id = id;
info.name = name;
info.meta.capabilities = capabilities;
info.params.stop = params.stop ? params.stop.split(',').filter((s) => s.trim()) : null;
if (knowledge.length > 0) {
info.meta.knowledge = knowledge;
} else {
if (info.meta.knowledge) {
delete info.meta.knowledge;
}
}
if (toolIds.length > 0) {
info.meta.toolIds = toolIds;
} else {
if (info.meta.toolIds) {
delete info.meta.toolIds;
}
}
info.params.stop = params.stop ? params.stop.split(',').filter((s) => s.trim()) : null;
Object.keys(info.params).forEach((key) => {
if (info.params[key] === '' || info.params[key] === null) {
delete info.params[key];
@@ -119,6 +139,14 @@
)
: null;
if (model?.info?.meta?.knowledge) {
knowledge = [...model?.info?.meta?.knowledge];
}
if (model?.info?.meta?.toolIds) {
toolIds = [...model?.info?.meta?.toolIds];
}
if (model?.owned_by === 'openai') {
capabilities.usage = false;
}
@@ -126,6 +154,7 @@
if (model?.info?.meta?.capabilities) {
capabilities = { ...capabilities, ...model?.info?.meta?.capabilities };
}
console.log(model);
} else {
goto('/workspace/models');
@@ -205,7 +234,7 @@
<button
class="flex space-x-1"
on:click={() => {
history.back();
goto('/workspace/models');
}}
>
<div class=" self-center">
@@ -393,6 +422,7 @@
{#if showAdvanced}
<div class="my-2">
<AdvancedParams
admin={true}
bind:params
on:change={(e) => {
info.params = { ...info.params, ...params };
@@ -405,7 +435,7 @@
<hr class=" dark:border-gray-850 my-1" />
<div class="my-1">
<div class="my-2">
<div class="flex w-full justify-between items-center">
<div class="flex w-full justify-between items-center">
<div class=" self-center text-sm font-semibold">{$i18n.t('Prompt suggestions')}</div>
@@ -495,7 +525,15 @@
{/if}
</div>
<div class="my-1">
<div class="my-2">
<Knowledge bind:knowledge />
</div>
<div class="my-2">
<ToolsSelector bind:selectedToolIds={toolIds} tools={$tools} />
</div>
<div class="my-2">
<div class="flex w-full justify-between mb-1">
<div class=" self-center text-sm font-semibold">{$i18n.t('Capabilities')}</div>
</div>

View File

@@ -0,0 +1,5 @@
<script>
import Tools from '$lib/components/workspace/Tools.svelte';
</script>
<Tools />

View File

@@ -0,0 +1,57 @@
<script>
import { goto } from '$app/navigation';
import { createNewTool, getTools } from '$lib/apis/tools';
import ToolkitEditor from '$lib/components/workspace/Tools/ToolkitEditor.svelte';
import { tools } from '$lib/stores';
import { onMount } from 'svelte';
import { toast } from 'svelte-sonner';
let mounted = false;
let clone = false;
let tool = null;
const saveHandler = async (data) => {
console.log(data);
const res = await createNewTool(localStorage.token, {
id: data.id,
name: data.name,
meta: data.meta,
content: data.content
}).catch((error) => {
toast.error(error);
return null;
});
if (res) {
toast.success('Tool created successfully');
tools.set(await getTools(localStorage.token));
await goto('/workspace/tools');
}
};
onMount(() => {
if (sessionStorage.tool) {
tool = JSON.parse(sessionStorage.tool);
sessionStorage.removeItem('tool');
console.log(tool);
clone = true;
}
mounted = true;
});
</script>
{#if mounted}
<ToolkitEditor
id={tool?.id ?? ''}
name={tool?.name ?? ''}
meta={tool?.meta ?? { description: '' }}
content={tool?.content ?? ''}
{clone}
on:save={(e) => {
saveHandler(e.detail);
}}
/>
{/if}

View File

@@ -0,0 +1,66 @@
<script>
import { goto } from '$app/navigation';
import { page } from '$app/stores';
import { getToolById, getTools, updateToolById } from '$lib/apis/tools';
import Spinner from '$lib/components/common/Spinner.svelte';
import ToolkitEditor from '$lib/components/workspace/Tools/ToolkitEditor.svelte';
import { tools } from '$lib/stores';
import { onMount } from 'svelte';
import { toast } from 'svelte-sonner';
let tool = null;
const saveHandler = async (data) => {
console.log(data);
const res = await updateToolById(localStorage.token, tool.id, {
id: data.id,
name: data.name,
meta: data.meta,
content: data.content
}).catch((error) => {
toast.error(error);
return null;
});
if (res) {
toast.success('Tool updated successfully');
tools.set(await getTools(localStorage.token));
// await goto('/workspace/tools');
}
};
onMount(async () => {
console.log('mounted');
const id = $page.url.searchParams.get('id');
if (id) {
tool = await getToolById(localStorage.token, id).catch((error) => {
toast.error(error);
goto('/workspace/tools');
return null;
});
console.log(tool);
}
});
</script>
{#if tool}
<ToolkitEditor
edit={true}
id={tool.id}
name={tool.name}
meta={tool.meta}
content={tool.content}
on:save={(e) => {
saveHandler(e.detail);
}}
/>
{:else}
<div class="flex items-center justify-center h-full">
<div class=" pb-16">
<Spinner />
</div>
</div>
{/if}

View File

@@ -1,5 +1,10 @@
<script>
import { io } from 'socket.io-client';
import { spring } from 'svelte/motion';
let loadingProgress = spring(0, {
stiffness: 0.05
});
import { onMount, tick, setContext } from 'svelte';
import {
@@ -121,8 +126,35 @@
await tick();
document.getElementById('splash-screen')?.remove();
loaded = true;
if (
document.documentElement.classList.contains('her') &&
document.getElementById('progress-bar')
) {
loadingProgress.subscribe((value) => {
const progressBar = document.getElementById('progress-bar');
if (progressBar) {
progressBar.style.width = `${value}%`;
}
});
await loadingProgress.set(100);
document.getElementById('splash-screen')?.remove();
const audio = new Audio(`/audio/greeting.mp3`);
const playAudio = () => {
audio.play();
document.removeEventListener('click', playAudio);
};
document.addEventListener('click', playAudio);
loaded = true;
} else {
document.getElementById('splash-screen')?.remove();
loaded = true;
}
return () => {
window.removeEventListener('resize', onResize);

View File

@@ -135,7 +135,7 @@
{#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-bold dark:text-gray-200"
class="flex items-center justify-center gap-3 text-xl sm:text-2xl text-center font-medium dark:text-gray-200"
>
<div>
{$i18n.t('Signing in')}
@@ -157,7 +157,7 @@
}}
>
<div class="mb-1">
<div class=" text-2xl font-bold">
<div class=" text-2xl font-medium">
{mode === 'signin' ? $i18n.t('Sign in') : $i18n.t('Sign up')}
{$i18n.t('to')}
{$WEBUI_NAME}
@@ -176,7 +176,7 @@
<div class="flex flex-col mt-4">
{#if mode === 'signup'}
<div>
<div class=" text-sm font-semibold text-left mb-1">{$i18n.t('Name')}</div>
<div class=" text-sm font-medium text-left mb-1">{$i18n.t('Name')}</div>
<input
bind:value={name}
type="text"
@@ -191,7 +191,7 @@
{/if}
<div class="mb-2">
<div class=" text-sm font-semibold text-left mb-1">{$i18n.t('Email')}</div>
<div class=" text-sm font-medium text-left mb-1">{$i18n.t('Email')}</div>
<input
bind:value={email}
type="email"
@@ -203,7 +203,7 @@
</div>
<div>
<div class=" text-sm font-semibold text-left mb-1">{$i18n.t('Password')}</div>
<div class=" text-sm font-medium text-left mb-1">{$i18n.t('Password')}</div>
<input
bind:value={password}
@@ -218,7 +218,7 @@
<div class="mt-5">
<button
class=" bg-gray-900 hover:bg-gray-800 w-full rounded-2xl text-white font-semibold text-sm py-3 transition"
class=" bg-gray-900 hover:bg-gray-800 w-full rounded-2xl text-white font-medium text-sm py-3 transition"
type="submit"
>
{mode === 'signin' ? $i18n.t('Sign in') : $i18n.t('Create Account')}