Spaces:
Paused
Paused
| <script lang="ts"> | |
| import { toast } from 'svelte-sonner'; | |
| import { onMount, tick, getContext } from 'svelte'; | |
| import { openDB, deleteDB } from 'idb'; | |
| import fileSaver from 'file-saver'; | |
| const { saveAs } = fileSaver; | |
| import { goto } from '$app/navigation'; | |
| import { getModels as _getModels } from '$lib/apis'; | |
| import { getAllChatTags } from '$lib/apis/chats'; | |
| import { getPrompts } from '$lib/apis/prompts'; | |
| import { getDocs } from '$lib/apis/documents'; | |
| import { getTools } from '$lib/apis/tools'; | |
| import { getBanners } from '$lib/apis/configs'; | |
| import { getUserSettings } from '$lib/apis/users'; | |
| import { | |
| user, | |
| showSettings, | |
| settings, | |
| models, | |
| prompts, | |
| documents, | |
| tags, | |
| banners, | |
| showChangelog, | |
| config, | |
| showCallOverlay, | |
| tools, | |
| functions | |
| } from '$lib/stores'; | |
| import SettingsModal from '$lib/components/chat/SettingsModal.svelte'; | |
| import Sidebar from '$lib/components/layout/Sidebar.svelte'; | |
| import ChangelogModal from '$lib/components/ChangelogModal.svelte'; | |
| import AccountPending from '$lib/components/layout/Overlay/AccountPending.svelte'; | |
| import { getFunctions } from '$lib/apis/functions'; | |
| const i18n = getContext('i18n'); | |
| let loaded = false; | |
| let DB = null; | |
| let localDBChats = []; | |
| const getModels = async () => { | |
| return _getModels(localStorage.token); | |
| }; | |
| onMount(async () => { | |
| if ($user === undefined) { | |
| await goto('/auth'); | |
| } else if (['user', 'admin'].includes($user.role)) { | |
| try { | |
| // Check if IndexedDB exists | |
| DB = await openDB('Chats', 1); | |
| if (DB) { | |
| const chats = await DB.getAllFromIndex('chats', 'timestamp'); | |
| localDBChats = chats.map((item, idx) => chats[chats.length - 1 - idx]); | |
| if (localDBChats.length === 0) { | |
| await deleteDB('Chats'); | |
| } | |
| } | |
| console.log(DB); | |
| } catch (error) { | |
| // IndexedDB Not Found | |
| } | |
| const userSettings = await getUserSettings(localStorage.token).catch((error) => { | |
| console.error(error); | |
| return null; | |
| }); | |
| if (userSettings) { | |
| await settings.set(userSettings.ui); | |
| } else { | |
| await settings.set(JSON.parse(localStorage.getItem('settings') ?? '{}')); | |
| } | |
| await Promise.all([ | |
| (async () => { | |
| models.set(await getModels()); | |
| })(), | |
| (async () => { | |
| prompts.set(await getPrompts(localStorage.token)); | |
| })(), | |
| (async () => { | |
| documents.set(await getDocs(localStorage.token)); | |
| })(), | |
| (async () => { | |
| tools.set(await getTools(localStorage.token)); | |
| })(), | |
| (async () => { | |
| functions.set(await getFunctions(localStorage.token)); | |
| })(), | |
| (async () => { | |
| banners.set(await getBanners(localStorage.token)); | |
| })(), | |
| (async () => { | |
| tags.set(await getAllChatTags(localStorage.token)); | |
| })() | |
| ]); | |
| document.addEventListener('keydown', function (event) { | |
| const isCtrlPressed = event.ctrlKey || event.metaKey; // metaKey is for Cmd key on Mac | |
| // Check if the Shift key is pressed | |
| const isShiftPressed = event.shiftKey; | |
| // Check if Ctrl + Shift + O is pressed | |
| if (isCtrlPressed && isShiftPressed && event.key.toLowerCase() === 'o') { | |
| event.preventDefault(); | |
| console.log('newChat'); | |
| document.getElementById('sidebar-new-chat-button')?.click(); | |
| } | |
| // Check if Shift + Esc is pressed | |
| if (isShiftPressed && event.key === 'Escape') { | |
| event.preventDefault(); | |
| console.log('focusInput'); | |
| document.getElementById('chat-textarea')?.focus(); | |
| } | |
| // Check if Ctrl + Shift + ; is pressed | |
| if (isCtrlPressed && isShiftPressed && event.key === ';') { | |
| event.preventDefault(); | |
| console.log('copyLastCodeBlock'); | |
| const button = [...document.getElementsByClassName('copy-code-button')]?.at(-1); | |
| button?.click(); | |
| } | |
| // Check if Ctrl + Shift + C is pressed | |
| if (isCtrlPressed && isShiftPressed && event.key.toLowerCase() === 'c') { | |
| event.preventDefault(); | |
| console.log('copyLastResponse'); | |
| const button = [...document.getElementsByClassName('copy-response-button')]?.at(-1); | |
| console.log(button); | |
| button?.click(); | |
| } | |
| // Check if Ctrl + Shift + S is pressed | |
| if (isCtrlPressed && isShiftPressed && event.key.toLowerCase() === 's') { | |
| event.preventDefault(); | |
| console.log('toggleSidebar'); | |
| document.getElementById('sidebar-toggle-button')?.click(); | |
| } | |
| // Check if Ctrl + Shift + Backspace is pressed | |
| if (isCtrlPressed && isShiftPressed && event.key === 'Backspace') { | |
| event.preventDefault(); | |
| console.log('deleteChat'); | |
| document.getElementById('delete-chat-button')?.click(); | |
| } | |
| // Check if Ctrl + . is pressed | |
| if (isCtrlPressed && event.key === '.') { | |
| event.preventDefault(); | |
| console.log('openSettings'); | |
| showSettings.set(!$showSettings); | |
| } | |
| // Check if Ctrl + / is pressed | |
| if (isCtrlPressed && event.key === '/') { | |
| event.preventDefault(); | |
| console.log('showShortcuts'); | |
| document.getElementById('show-shortcuts-button')?.click(); | |
| } | |
| }); | |
| if ($user.role === 'admin') { | |
| showChangelog.set(localStorage.version !== $config.version); | |
| } | |
| await tick(); | |
| } | |
| loaded = true; | |
| }); | |
| </script> | |
| <SettingsModal bind:show={$showSettings} /> | |
| <ChangelogModal bind:show={$showChangelog} /> | |
| <div class="app relative"> | |
| <div | |
| class=" text-gray-700 dark:text-gray-100 bg-white dark:bg-gray-900 h-screen max-h-[100dvh] overflow-auto flex flex-row" | |
| > | |
| {#if loaded} | |
| {#if !['user', 'admin'].includes($user.role)} | |
| <AccountPending /> | |
| {:else if localDBChats.length > 0} | |
| <div class="fixed w-full h-full flex z-50"> | |
| <div | |
| class="absolute w-full h-full backdrop-blur-md bg-white/20 dark:bg-gray-900/50 flex justify-center" | |
| > | |
| <div class="m-auto pb-44 flex flex-col justify-center"> | |
| <div class="max-w-md"> | |
| <div class="text-center dark:text-white text-2xl font-medium z-50"> | |
| Important Update<br /> Action Required for Chat Log Storage | |
| </div> | |
| <div class=" mt-4 text-center text-sm dark:text-gray-200 w-full"> | |
| {$i18n.t( | |
| "Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through" | |
| )} | |
| <span class="font-semibold dark:text-white" | |
| >{$i18n.t('Settings')} > {$i18n.t('Chats')} > {$i18n.t('Import Chats')}</span | |
| >. {$i18n.t( | |
| 'This ensures that your valuable conversations are securely saved to your backend database. Thank you!' | |
| )} | |
| </div> | |
| <div class=" mt-6 mx-auto relative group w-fit"> | |
| <button | |
| class="relative z-20 flex px-5 py-2 rounded-full bg-white border border-gray-100 dark:border-none hover:bg-gray-100 transition font-medium text-sm" | |
| on:click={async () => { | |
| let blob = new Blob([JSON.stringify(localDBChats)], { | |
| type: 'application/json' | |
| }); | |
| saveAs(blob, `chat-export-${Date.now()}.json`); | |
| const tx = DB.transaction('chats', 'readwrite'); | |
| await Promise.all([tx.store.clear(), tx.done]); | |
| await deleteDB('Chats'); | |
| localDBChats = []; | |
| }} | |
| > | |
| Download & Delete | |
| </button> | |
| <button | |
| class="text-xs text-center w-full mt-2 text-gray-400 underline" | |
| on:click={async () => { | |
| localDBChats = []; | |
| }}>{$i18n.t('Close')}</button | |
| > | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| {/if} | |
| <Sidebar /> | |
| <slot /> | |
| {/if} | |
| </div> | |
| </div> | |
| <style> | |
| .loading { | |
| display: inline-block; | |
| clip-path: inset(0 1ch 0 0); | |
| animation: l 1s steps(3) infinite; | |
| letter-spacing: -0.5px; | |
| } | |
| @keyframes l { | |
| to { | |
| clip-path: inset(0 -1ch 0 0); | |
| } | |
| } | |
| pre[class*='language-'] { | |
| position: relative; | |
| overflow: auto; | |
| /* make space */ | |
| margin: 5px 0; | |
| padding: 1.75rem 0 1.75rem 1rem; | |
| border-radius: 10px; | |
| } | |
| pre[class*='language-'] button { | |
| position: absolute; | |
| top: 5px; | |
| right: 5px; | |
| font-size: 0.9rem; | |
| padding: 0.15rem; | |
| background-color: #828282; | |
| border: ridge 1px #7b7b7c; | |
| border-radius: 5px; | |
| text-shadow: #c4c4c4 0 0 2px; | |
| } | |
| pre[class*='language-'] button:hover { | |
| cursor: pointer; | |
| background-color: #bcbabb; | |
| } | |
| </style> | |