+layout.svelte 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. <script>
  2. import { io } from 'socket.io-client';
  3. import { spring } from 'svelte/motion';
  4. let loadingProgress = spring(0, {
  5. stiffness: 0.05
  6. });
  7. import { onMount, tick, setContext } from 'svelte';
  8. import {
  9. config,
  10. user,
  11. theme,
  12. WEBUI_NAME,
  13. mobile,
  14. socket,
  15. activeUserCount,
  16. USAGE_POOL
  17. } from '$lib/stores';
  18. import { goto } from '$app/navigation';
  19. import { page } from '$app/stores';
  20. import { Toaster, toast } from 'svelte-sonner';
  21. import { getBackendConfig } from '$lib/apis';
  22. import { getSessionUser } from '$lib/apis/auths';
  23. import '../tailwind.css';
  24. import '../app.css';
  25. import 'tippy.js/dist/tippy.css';
  26. import { WEBUI_BASE_URL, WEBUI_HOSTNAME } from '$lib/constants';
  27. import i18n, { initI18n, getLanguages } from '$lib/i18n';
  28. setContext('i18n', i18n);
  29. let loaded = false;
  30. const BREAKPOINT = 768;
  31. let wakeLock = null;
  32. onMount(async () => {
  33. theme.set(localStorage.theme);
  34. mobile.set(window.innerWidth < BREAKPOINT);
  35. const onResize = () => {
  36. if (window.innerWidth < BREAKPOINT) {
  37. mobile.set(true);
  38. } else {
  39. mobile.set(false);
  40. }
  41. };
  42. window.addEventListener('resize', onResize);
  43. const setWakeLock = async () => {
  44. try {
  45. wakeLock = await navigator.wakeLock.request('screen');
  46. } catch (err) {
  47. // The Wake Lock request has failed - usually system related, such as battery.
  48. console.log(err);
  49. }
  50. if (wakeLock) {
  51. // Add a listener to release the wake lock when the page is unloaded
  52. wakeLock.addEventListener('release', () => {
  53. // the wake lock has been released
  54. console.log('Wake Lock released');
  55. });
  56. }
  57. };
  58. if ('wakeLock' in navigator) {
  59. await setWakeLock();
  60. document.addEventListener('visibilitychange', async () => {
  61. // Re-request the wake lock if the document becomes visible
  62. if (wakeLock !== null && document.visibilityState === 'visible') {
  63. await setWakeLock();
  64. }
  65. });
  66. }
  67. let backendConfig = null;
  68. try {
  69. backendConfig = await getBackendConfig();
  70. console.log('Backend config:', backendConfig);
  71. } catch (error) {
  72. console.error('Error loading backend config:', error);
  73. }
  74. // Initialize i18n even if we didn't get a backend config,
  75. // so `/error` can show something that's not `undefined`.
  76. const languages = await getLanguages();
  77. const browserLanguage = navigator.languages
  78. ? navigator.languages[0]
  79. : navigator.language || navigator.userLanguage;
  80. initI18n(languages.includes(browserLanguage) ? browserLanguage : backendConfig?.default_locale);
  81. if (backendConfig) {
  82. // Save Backend Status to Store
  83. await config.set(backendConfig);
  84. await WEBUI_NAME.set(backendConfig.name);
  85. if ($config) {
  86. const _socket = io(`${WEBUI_BASE_URL}`, {
  87. path: '/ws/socket.io',
  88. auth: { token: localStorage.token }
  89. });
  90. _socket.on('connect', () => {
  91. console.log('connected');
  92. });
  93. await socket.set(_socket);
  94. _socket.on('user-count', (data) => {
  95. console.log('user-count', data);
  96. activeUserCount.set(data.count);
  97. });
  98. _socket.on('usage', (data) => {
  99. console.log('usage', data);
  100. USAGE_POOL.set(data['models']);
  101. });
  102. if (localStorage.token) {
  103. // Get Session User Info
  104. const sessionUser = await getSessionUser(localStorage.token).catch((error) => {
  105. toast.error(error);
  106. return null;
  107. });
  108. if (sessionUser) {
  109. // Save Session User to Store
  110. await user.set(sessionUser);
  111. } else {
  112. // Redirect Invalid Session User to /auth Page
  113. localStorage.removeItem('token');
  114. await goto('/auth');
  115. }
  116. } else {
  117. // Don't redirect if we're already on the auth page
  118. // Needed because we pass in tokens from OAuth logins via URL fragments
  119. if ($page.url.pathname !== '/auth') {
  120. await goto('/auth');
  121. }
  122. }
  123. }
  124. } else {
  125. // Redirect to /error when Backend Not Detected
  126. await goto(`/error`);
  127. }
  128. await tick();
  129. if (
  130. document.documentElement.classList.contains('her') &&
  131. document.getElementById('progress-bar')
  132. ) {
  133. loadingProgress.subscribe((value) => {
  134. const progressBar = document.getElementById('progress-bar');
  135. if (progressBar) {
  136. progressBar.style.width = `${value}%`;
  137. }
  138. });
  139. await loadingProgress.set(100);
  140. document.getElementById('splash-screen')?.remove();
  141. const audio = new Audio(`/audio/greeting.mp3`);
  142. const playAudio = () => {
  143. audio.play();
  144. document.removeEventListener('click', playAudio);
  145. };
  146. document.addEventListener('click', playAudio);
  147. loaded = true;
  148. } else {
  149. document.getElementById('splash-screen')?.remove();
  150. loaded = true;
  151. }
  152. return () => {
  153. window.removeEventListener('resize', onResize);
  154. };
  155. });
  156. </script>
  157. <svelte:head>
  158. <title>{$WEBUI_NAME}</title>
  159. <link crossorigin="anonymous" rel="icon" href="{WEBUI_BASE_URL}/static/favicon.png" />
  160. <!-- rosepine themes have been disabled as it's not up to date with our latest version. -->
  161. <!-- feel free to make a PR to fix if anyone wants to see it return -->
  162. <!-- <link rel="stylesheet" type="text/css" href="/themes/rosepine.css" />
  163. <link rel="stylesheet" type="text/css" href="/themes/rosepine-dawn.css" /> -->
  164. </svelte:head>
  165. {#if loaded}
  166. <slot />
  167. {/if}
  168. <Toaster richColors position="top-center" />