|
<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 { |
|
config, |
|
user, |
|
theme, |
|
WEBUI_NAME, |
|
mobile, |
|
socket, |
|
activeUserCount, |
|
USAGE_POOL |
|
} from '$lib/stores'; |
|
import { goto } from '$app/navigation'; |
|
import { page } from '$app/stores'; |
|
import { Toaster, toast } from 'svelte-sonner'; |
|
|
|
import { getBackendConfig } from '$lib/apis'; |
|
import { getSessionUser } from '$lib/apis/auths'; |
|
|
|
import '../tailwind.css'; |
|
import '../app.css'; |
|
|
|
import 'tippy.js/dist/tippy.css'; |
|
|
|
import { WEBUI_BASE_URL, WEBUI_HOSTNAME } from '$lib/constants'; |
|
import i18n, { initI18n, getLanguages } from '$lib/i18n'; |
|
import { bestMatchingLanguage } from '$lib/utils'; |
|
|
|
setContext('i18n', i18n); |
|
|
|
let loaded = false; |
|
const BREAKPOINT = 768; |
|
|
|
const setupSocket = () => { |
|
const _socket = io(`${WEBUI_BASE_URL}` || undefined, { |
|
reconnection: true, |
|
reconnectionDelay: 1000, |
|
reconnectionDelayMax: 5000, |
|
randomizationFactor: 0.5, |
|
path: '/ws/socket.io', |
|
auth: { token: localStorage.token } |
|
}); |
|
|
|
socket.set(_socket); |
|
|
|
_socket.on('connect_error', (err) => { |
|
console.log('connect_error', err); |
|
}); |
|
|
|
_socket.on('connect', () => { |
|
console.log('connected', _socket.id); |
|
}); |
|
|
|
_socket.on('reconnect_attempt', (attempt) => { |
|
console.log('reconnect_attempt', attempt); |
|
}); |
|
|
|
_socket.on('reconnect_failed', () => { |
|
console.log('reconnect_failed'); |
|
}); |
|
|
|
_socket.on('disconnect', (reason, details) => { |
|
console.log(`Socket ${_socket.id} disconnected due to ${reason}`); |
|
if (details) { |
|
console.log('Additional details:', details); |
|
} |
|
}); |
|
|
|
_socket.on('user-count', (data) => { |
|
console.log('user-count', data); |
|
activeUserCount.set(data.count); |
|
}); |
|
|
|
_socket.on('usage', (data) => { |
|
console.log('usage', data); |
|
USAGE_POOL.set(data['models']); |
|
}); |
|
}; |
|
|
|
onMount(async () => { |
|
theme.set(localStorage.theme); |
|
|
|
mobile.set(window.innerWidth < BREAKPOINT); |
|
const onResize = () => { |
|
if (window.innerWidth < BREAKPOINT) { |
|
mobile.set(true); |
|
} else { |
|
mobile.set(false); |
|
} |
|
}; |
|
|
|
window.addEventListener('resize', onResize); |
|
|
|
let backendConfig = null; |
|
try { |
|
backendConfig = await getBackendConfig(); |
|
console.log('Backend config:', backendConfig); |
|
} catch (error) { |
|
console.error('Error loading backend config:', error); |
|
} |
|
|
|
|
|
|
|
initI18n(); |
|
if (!localStorage.locale) { |
|
const languages = await getLanguages(); |
|
const browserLanguages = navigator.languages |
|
? navigator.languages |
|
: [navigator.language || navigator.userLanguage]; |
|
const lang = backendConfig.default_locale |
|
? backendConfig.default_locale |
|
: bestMatchingLanguage(languages, browserLanguages, 'en-US'); |
|
$i18n.changeLanguage(lang); |
|
} |
|
|
|
if (backendConfig) { |
|
// Save Backend Status to Store |
|
await config.set(backendConfig); |
|
await WEBUI_NAME.set(backendConfig.name); |
|
|
|
if ($config) { |
|
setupSocket(); |
|
|
|
if (localStorage.token) { |
|
// Get Session User Info |
|
const sessionUser = await getSessionUser(localStorage.token).catch((error) => { |
|
toast.error(error); |
|
return null; |
|
}); |
|
|
|
if (sessionUser) { |
|
// Save Session User to Store |
|
await user.set(sessionUser); |
|
await config.set(await getBackendConfig()); |
|
} else { |
|
// Redirect Invalid Session User to /auth Page |
|
localStorage.removeItem('token'); |
|
await goto('/auth'); |
|
} |
|
} else { |
|
// Don't redirect if we're already on the auth page |
|
// Needed because we pass in tokens from OAuth logins via URL fragments |
|
if ($page.url.pathname !== '/auth') { |
|
await goto('/auth'); |
|
} |
|
} |
|
} |
|
} else { |
|
// Redirect to /error when Backend Not Detected |
|
await goto(`/error`); |
|
} |
|
|
|
await tick(); |
|
|
|
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); |
|
}; |
|
}); |
|
</script> |
|
|
|
<svelte:head> |
|
<title>{$WEBUI_NAME}</title> |
|
<link crossorigin="anonymous" rel="icon" href="{WEBUI_BASE_URL}/static/favicon.png" /> |
|
|
|
|
|
|
|
|
|
|
|
</svelte:head> |
|
|
|
{#if loaded} |
|
<slot /> |
|
{/if} |
|
|
|
<Toaster |
|
theme={$theme.includes('dark') |
|
? 'dark' |
|
: $theme === 'system' |
|
? window.matchMedia('(prefers-color-scheme: dark)').matches |
|
? 'dark' |
|
: 'light' |
|
: 'light'} |
|
richColors |
|
position="top-center" |
|
/> |
|
|