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

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

53
54
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
		window.addEventListener('message', (event) => {
			if (event.origin === window.origin) {
				// Replace with your iframe's origin
				console.log('Message received from iframe:', event.data);
				if (event.data.type === 'input:prompt') {
					console.log(event.data.text);

					const inputElement = document.getElementById('chat-textarea');

					if (inputElement) {
						inputElement.value = event.data.text;
						inputElement.focus();
					}
				}

				if (event.data.type === 'action:submit-prompt') {
					console.log(event.data.text);

					const submitButtonElement = document.getElementById('send-message-button');

					if (submitButtonElement) {
						submitButtonElement.click();
					}
				}
			}
		});

80
81
		window.addEventListener('resize', onResize);

Timothy J. Baek's avatar
Timothy J. Baek committed
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
		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
107
108
109
		let backendConfig = null;
		try {
			backendConfig = await getBackendConfig();
110
			console.log('Backend config:', backendConfig);
Aarni Koskela's avatar
Aarni Koskela committed
111
		} catch (error) {
112
			console.error('Error loading backend config:', error);
Aarni Koskela's avatar
Aarni Koskela committed
113
114
115
		}
		// Initialize i18n even if we didn't get a backend config,
		// so `/error` can show something that's not `undefined`.
116
117
118
119
120
121
122
123

		const languages = await getLanguages();

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

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

Timothy J. Baek's avatar
Timothy J. Baek committed
125
		if (backendConfig) {
Timothy J. Baek's avatar
Timothy J. Baek committed
126
			// Save Backend Status to Store
Timothy J. Baek's avatar
Timothy J. Baek committed
127
			await config.set(backendConfig);
128
			await WEBUI_NAME.set(backendConfig.name);
129

130
			if ($config) {
Timothy J. Baek's avatar
Timothy J. Baek committed
131
132
133
134
135
136
137
138
139
				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
140
141
142
143
144
145
				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
146

147
148
149
150
151
				_socket.on('usage', (data) => {
					console.log('usage', data);
					USAGE_POOL.set(data['models']);
				});

152
				if (localStorage.token) {
153
					// Get Session User Info
Timothy J. Baek's avatar
Timothy J. Baek committed
154
155
156
157
					const sessionUser = await getSessionUser(localStorage.token).catch((error) => {
						toast.error(error);
						return null;
					});
158

159
					if (sessionUser) {
Timothy J. Baek's avatar
Timothy J. Baek committed
160
						// Save Session User to Store
161
						await user.set(sessionUser);
Timothy J. Baek's avatar
Timothy J. Baek committed
162
					} else {
Timothy J. Baek's avatar
Timothy J. Baek committed
163
						// Redirect Invalid Session User to /auth Page
Timothy J. Baek's avatar
Timothy J. Baek committed
164
165
166
						localStorage.removeItem('token');
						await goto('/auth');
					}
167
				} else {
Timothy J. Baek's avatar
Timothy J. Baek committed
168
					await goto('/auth');
169
170
				}
			}
171
		} else {
Timothy J. Baek's avatar
Timothy J. Baek committed
172
			// Redirect to /error when Backend Not Detected
173
			await goto(`/error`);
174
175
176
		}

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

Timothy J. Baek's avatar
Timothy J. Baek committed
178
179
180
181
182
		if (
			document.documentElement.classList.contains('her') &&
			document.getElementById('progress-bar')
		) {
			loadingProgress.subscribe((value) => {
Timothy J. Baek's avatar
fix  
Timothy J. Baek committed
183
184
185
				const progressBar = document.getElementById('progress-bar');

				if (progressBar) {
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
186
					progressBar.style.width = `${value}%`;
Timothy J. Baek's avatar
fix  
Timothy J. Baek committed
187
				}
Timothy J. Baek's avatar
Timothy J. Baek committed
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
			});

			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;
		}
207
208
209
210

		return () => {
			window.removeEventListener('resize', onResize);
		};
211
	});
Timothy J. Baek's avatar
Timothy J. Baek committed
212
213
214
</script>

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

Timothy J. Baek's avatar
Timothy J. Baek committed
218
219
	<!-- 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 -->
220
221
	<!-- <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
222
</svelte:head>
223

224
{#if loaded}
225
226
	<slot />
{/if}
227

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