+layout.svelte 4.85 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

Timothy J. Baek's avatar
Timothy J. Baek committed
39
40
	let wakeLock = null;

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

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

		window.addEventListener('resize', onResize);

Timothy J. Baek's avatar
Timothy J. Baek committed
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
		const setWakeLock = async () => {
			try {
				wakeLock = await navigator.wakeLock.request('screen');
			} catch (err) {
				// The Wake Lock request has failed - usually system related, such as battery.
				console.log(err);
			}

			wakeLock.addEventListener('release', () => {
				// the wake lock has been released
				console.log('Wake Lock released');
			});
		};

		if ('wakeLock' in navigator) {
			await setWakeLock();

			document.addEventListener('visibilitychange', async () => {
				// Re-request the wake lock if the document becomes visible
				if (wakeLock !== null && document.visibilityState === 'visible') {
					await setWakeLock();
				}
			});
		}

Aarni Koskela's avatar
Aarni Koskela committed
80
81
82
		let backendConfig = null;
		try {
			backendConfig = await getBackendConfig();
83
			console.log('Backend config:', backendConfig);
Aarni Koskela's avatar
Aarni Koskela committed
84
		} catch (error) {
85
			console.error('Error loading backend config:', error);
Aarni Koskela's avatar
Aarni Koskela committed
86
87
88
		}
		// Initialize i18n even if we didn't get a backend config,
		// so `/error` can show something that's not `undefined`.
89
90
91
92
93
94
95
96

		const languages = await getLanguages();

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

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

Timothy J. Baek's avatar
Timothy J. Baek committed
98
		if (backendConfig) {
Timothy J. Baek's avatar
Timothy J. Baek committed
99
			// Save Backend Status to Store
Timothy J. Baek's avatar
Timothy J. Baek committed
100
			await config.set(backendConfig);
101
			await WEBUI_NAME.set(backendConfig.name);
102

103
			if ($config) {
Timothy J. Baek's avatar
Timothy J. Baek committed
104
105
106
107
108
109
110
111
112
				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
113
114
115
116
117
118
				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
119

120
121
122
123
124
				_socket.on('usage', (data) => {
					console.log('usage', data);
					USAGE_POOL.set(data['models']);
				});

125
				if (localStorage.token) {
126
					// Get Session User Info
Timothy J. Baek's avatar
Timothy J. Baek committed
127
128
129
130
					const sessionUser = await getSessionUser(localStorage.token).catch((error) => {
						toast.error(error);
						return null;
					});
131

132
					if (sessionUser) {
Timothy J. Baek's avatar
Timothy J. Baek committed
133
						// Save Session User to Store
134
						await user.set(sessionUser);
Timothy J. Baek's avatar
Timothy J. Baek committed
135
					} else {
Timothy J. Baek's avatar
Timothy J. Baek committed
136
						// Redirect Invalid Session User to /auth Page
Timothy J. Baek's avatar
Timothy J. Baek committed
137
138
139
						localStorage.removeItem('token');
						await goto('/auth');
					}
140
				} else {
Timothy J. Baek's avatar
Timothy J. Baek committed
141
					await goto('/auth');
142
143
				}
			}
144
		} else {
Timothy J. Baek's avatar
Timothy J. Baek committed
145
			// Redirect to /error when Backend Not Detected
146
			await goto(`/error`);
147
148
149
		}

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

Timothy J. Baek's avatar
Timothy J. Baek committed
151
152
153
154
155
		if (
			document.documentElement.classList.contains('her') &&
			document.getElementById('progress-bar')
		) {
			loadingProgress.subscribe((value) => {
Timothy J. Baek's avatar
fix  
Timothy J. Baek committed
156
157
158
				const progressBar = document.getElementById('progress-bar');

				if (progressBar) {
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
159
					progressBar.style.width = `${value}%`;
Timothy J. Baek's avatar
fix  
Timothy J. Baek committed
160
				}
Timothy J. Baek's avatar
Timothy J. Baek committed
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
			});

			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;
		}
180
181
182
183

		return () => {
			window.removeEventListener('resize', onResize);
		};
184
	});
Timothy J. Baek's avatar
Timothy J. Baek committed
185
186
187
</script>

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

Timothy J. Baek's avatar
Timothy J. Baek committed
191
192
	<!-- 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 -->
193
194
	<!-- <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
195
</svelte:head>
196

197
{#if loaded}
198
199
	<slot />
{/if}
200

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