+layout.svelte 4.11 KB
Newer Older
Timothy J. Baek's avatar
Timothy J. Baek committed
1
<script>
Timothy J. Baek's avatar
Timothy J. Baek committed
2
	import { io } from 'socket.io-client';
Timothy J. Baek's avatar
Timothy J. Baek committed
3
4
	import { spring } from 'svelte/motion';

Timothy J. Baek's avatar
Timothy J. Baek committed
5
6
7
	let loadingProgress = spring(0, {
		stiffness: 0.05
	});
Timothy J. Baek's avatar
Timothy J. Baek committed
8

9
	import { onMount, tick, setContext } from 'svelte';
10
11
12
13
14
15
16
17
18
19
	import {
		config,
		user,
		theme,
		WEBUI_NAME,
		mobile,
		socket,
		activeUserCount,
		USAGE_POOL
	} from '$lib/stores';
20
	import { goto } from '$app/navigation';
Jannik Streidl's avatar
Jannik Streidl committed
21
	import { Toaster, toast } from 'svelte-sonner';
Timothy J. Baek's avatar
Timothy J. Baek committed
22

Timothy J. Baek's avatar
Timothy J. Baek committed
23
24
25
	import { getBackendConfig } from '$lib/apis';
	import { getSessionUser } from '$lib/apis/auths';

Timothy J. Baek's avatar
Timothy J. Baek committed
26
	import '../tailwind.css';
Timothy J. Baek's avatar
Timothy J. Baek committed
27
28
	import '../app.css';

Timothy J. Baek's avatar
Timothy J. Baek committed
29
	import 'tippy.js/dist/tippy.css';
Timothy J. Baek's avatar
Timothy J. Baek committed
30

Timothy J. Baek's avatar
Timothy J. Baek committed
31
	import { WEBUI_BASE_URL, WEBUI_HOSTNAME } from '$lib/constants';
32
	import i18n, { initI18n, getLanguages } from '$lib/i18n';
33
34

	setContext('i18n', i18n);
Timothy J. Baek's avatar
Timothy J. Baek committed
35

36
	let loaded = false;
Timothy J. Baek's avatar
Timothy J. Baek committed
37
	const BREAKPOINT = 768;
38
39

	onMount(async () => {
Timothy J. Baek's avatar
Timothy J. Baek committed
40
		theme.set(localStorage.theme);
41
42
43
44
45
46
47
48
49
50
51
52

		mobile.set(window.innerWidth < BREAKPOINT);
		const onResize = () => {
			if (window.innerWidth < BREAKPOINT) {
				mobile.set(true);
			} else {
				mobile.set(false);
			}
		};

		window.addEventListener('resize', onResize);

Aarni Koskela's avatar
Aarni Koskela committed
53
54
55
		let backendConfig = null;
		try {
			backendConfig = await getBackendConfig();
56
			console.log('Backend config:', backendConfig);
Aarni Koskela's avatar
Aarni Koskela committed
57
		} catch (error) {
58
			console.error('Error loading backend config:', error);
Aarni Koskela's avatar
Aarni Koskela committed
59
60
61
		}
		// Initialize i18n even if we didn't get a backend config,
		// so `/error` can show something that's not `undefined`.
62
63
64
65
66
67
68
69

		const languages = await getLanguages();

		const browserLanguage = navigator.languages
			? navigator.languages[0]
			: navigator.language || navigator.userLanguage;

		initI18n(languages.includes(browserLanguage) ? browserLanguage : backendConfig?.default_locale);
70

Timothy J. Baek's avatar
Timothy J. Baek committed
71
		if (backendConfig) {
Timothy J. Baek's avatar
Timothy J. Baek committed
72
			// Save Backend Status to Store
Timothy J. Baek's avatar
Timothy J. Baek committed
73
			await config.set(backendConfig);
74
			await WEBUI_NAME.set(backendConfig.name);
75

76
			if ($config) {
Timothy J. Baek's avatar
Timothy J. Baek committed
77
78
79
80
81
82
83
84
85
				const _socket = io(`${WEBUI_BASE_URL}`, {
					path: '/ws/socket.io',
					auth: { token: localStorage.token }
				});

				_socket.on('connect', () => {
					console.log('connected');
				});

Timothy J. Baek's avatar
Timothy J. Baek committed
86
87
88
89
90
91
				await socket.set(_socket);

				_socket.on('user-count', (data) => {
					console.log('user-count', data);
					activeUserCount.set(data.count);
				});
Timothy J. Baek's avatar
Timothy J. Baek committed
92

93
94
95
96
97
				_socket.on('usage', (data) => {
					console.log('usage', data);
					USAGE_POOL.set(data['models']);
				});

98
				if (localStorage.token) {
99
					// Get Session User Info
Timothy J. Baek's avatar
Timothy J. Baek committed
100
101
102
103
					const sessionUser = await getSessionUser(localStorage.token).catch((error) => {
						toast.error(error);
						return null;
					});
104

105
					if (sessionUser) {
Timothy J. Baek's avatar
Timothy J. Baek committed
106
						// Save Session User to Store
107
						await user.set(sessionUser);
Timothy J. Baek's avatar
Timothy J. Baek committed
108
					} else {
Timothy J. Baek's avatar
Timothy J. Baek committed
109
						// Redirect Invalid Session User to /auth Page
Timothy J. Baek's avatar
Timothy J. Baek committed
110
111
112
						localStorage.removeItem('token');
						await goto('/auth');
					}
113
				} else {
Timothy J. Baek's avatar
Timothy J. Baek committed
114
					await goto('/auth');
115
116
				}
			}
117
		} else {
Timothy J. Baek's avatar
Timothy J. Baek committed
118
			// Redirect to /error when Backend Not Detected
119
			await goto(`/error`);
120
121
122
		}

		await tick();
Timothy J. Baek's avatar
Timothy J. Baek committed
123

Timothy J. Baek's avatar
Timothy J. Baek committed
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
		if (
			document.documentElement.classList.contains('her') &&
			document.getElementById('progress-bar')
		) {
			loadingProgress.subscribe((value) => {
				document.getElementById('progress-bar').style.width = `${value * 0.24}rem`;
			});

			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;
		}
149
150
151
152

		return () => {
			window.removeEventListener('resize', onResize);
		};
153
	});
Timothy J. Baek's avatar
Timothy J. Baek committed
154
155
156
</script>

<svelte:head>
157
	<title>{$WEBUI_NAME}</title>
Timothy J. Baek's avatar
Timothy J. Baek committed
158
	<link crossorigin="anonymous" rel="icon" href="{WEBUI_BASE_URL}/static/favicon.png" />
Timothy J. Baek's avatar
Timothy J. Baek committed
159

Timothy J. Baek's avatar
Timothy J. Baek committed
160
161
	<!-- rosepine themes have been disabled as it's not up to date with our latest version. -->
	<!-- feel free to make a PR to fix if anyone wants to see it return -->
162
163
	<!-- <link rel="stylesheet" type="text/css" href="/themes/rosepine.css" />
	<link rel="stylesheet" type="text/css" href="/themes/rosepine-dawn.css" /> -->
Timothy J. Baek's avatar
Timothy J. Baek committed
164
</svelte:head>
165

166
{#if loaded}
167
168
	<slot />
{/if}
169

Timothy J. Baek's avatar
Timothy J. Baek committed
170
<Toaster richColors position="top-center" />