refac
This commit is contained in:
1 parent
b7b7b64d31
commit
60f3ba6b59
3 files changed
+483
-160
No files matched your search
@@ -7,7 +7,15 @@
|
||||
import { getUsage } from '$lib/apis';
|
||||
import { getSessionUser, userSignOut } from '$lib/apis/auths';
|
||||
|
||||
import { showSettings, mobile, showSidebar, showShortcuts, user, config } from '$lib/stores';
|
||||
import {
|
||||
showSettings,
|
||||
mobile,
|
||||
showSidebar,
|
||||
showShortcuts,
|
||||
user,
|
||||
config,
|
||||
settings
|
||||
} from '$lib/stores';
|
||||
|
||||
import { WEBUI_API_BASE_URL } from '$lib/constants';
|
||||
|
||||
@@ -26,7 +34,10 @@
|
||||
import UserStatusModal from './UserStatusModal.svelte';
|
||||
import Emoji from '$lib/components/common/Emoji.svelte';
|
||||
import XMark from '$lib/components/icons/XMark.svelte';
|
||||
import { updateUserStatus } from '$lib/apis/users';
|
||||
import Note from '$lib/components/icons/Note.svelte';
|
||||
import Pin from '$lib/components/icons/Pin.svelte';
|
||||
import PinSlash from '$lib/components/icons/PinSlash.svelte';
|
||||
import { updateUserStatus, updateUserSettings } from '$lib/apis/users';
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
@@ -43,9 +54,29 @@
|
||||
export let showActiveUsers = true;
|
||||
|
||||
let showUserStatusModal = false;
|
||||
let shiftKey = false;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
const DEFAULT_PINNED_ITEMS = ['notes', 'workspace'];
|
||||
|
||||
$: pinnedItems = $settings?.pinnedMenuItems ?? DEFAULT_PINNED_ITEMS;
|
||||
|
||||
const isPinned = (id: string) => {
|
||||
return pinnedItems.includes(id);
|
||||
};
|
||||
|
||||
const togglePin = async (id: string) => {
|
||||
let updated;
|
||||
if (isPinned(id)) {
|
||||
updated = pinnedItems.filter((item) => item !== id);
|
||||
} else {
|
||||
updated = [...pinnedItems, id];
|
||||
}
|
||||
await settings.set({ ...$settings, pinnedMenuItems: updated });
|
||||
await updateUserSettings(localStorage.token, { ui: $settings });
|
||||
};
|
||||
|
||||
let usage = null;
|
||||
const getUsageInfo = async () => {
|
||||
const res = await getUsage(localStorage.token).catch((error) => {
|
||||
@@ -69,6 +100,15 @@
|
||||
};
|
||||
</script>
|
||||
|
||||
<svelte:window
|
||||
on:keydown={(e) => {
|
||||
if (e.key === 'Shift') shiftKey = true;
|
||||
}}
|
||||
on:keyup={(e) => {
|
||||
if (e.key === 'Shift') shiftKey = false;
|
||||
}}
|
||||
/>
|
||||
|
||||
<ShortcutsModal bind:show={$showShortcuts} />
|
||||
<UserStatusModal
|
||||
bind:show={showUserStatusModal}
|
||||
@@ -194,119 +234,263 @@
|
||||
<hr class=" border-gray-50/30 dark:border-gray-800/30 my-1.5 p-0" />
|
||||
{/if}
|
||||
|
||||
<button
|
||||
class="flex rounded-xl py-1.5 px-3 w-full hover:bg-gray-50 dark:hover:bg-gray-800 transition cursor-pointer select-none"
|
||||
type="button"
|
||||
on:click={async () => {
|
||||
show = false;
|
||||
|
||||
await showSettings.set(true);
|
||||
|
||||
if ($mobile) {
|
||||
await tick();
|
||||
showSidebar.set(false);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div class=" self-center mr-3">
|
||||
<Settings className="w-5 h-5" strokeWidth="1.5" />
|
||||
{#if ($config?.features?.enable_notes ?? false) && ($user?.role === 'admin' || ($user?.permissions?.features?.notes ?? true))}
|
||||
<div class="flex items-center w-full">
|
||||
<a
|
||||
href="/notes"
|
||||
draggable="false"
|
||||
class="flex flex-1 rounded-xl py-1.5 px-3 hover:bg-gray-50 dark:hover:bg-gray-800 transition cursor-pointer select-none"
|
||||
on:click={async (e) => {
|
||||
if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1) return;
|
||||
e.preventDefault();
|
||||
show = false;
|
||||
goto('/notes');
|
||||
if ($mobile) {
|
||||
await tick();
|
||||
showSidebar.set(false);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div class="self-center mr-3">
|
||||
<Note className="size-5" strokeWidth="1.5" />
|
||||
</div>
|
||||
<div class="self-center truncate">{$i18n.t('Notes')}</div>
|
||||
</a>
|
||||
{#if shiftKey}
|
||||
<Tooltip
|
||||
content={isPinned('notes')
|
||||
? $i18n.t('Unpin from Sidebar')
|
||||
: $i18n.t('Pin to Sidebar')}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="p-1 mr-1 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 transition"
|
||||
on:click|preventDefault|stopPropagation={() => togglePin('notes')}
|
||||
>
|
||||
{#if isPinned('notes')}
|
||||
<PinSlash className="size-3.5" strokeWidth="1.5" />
|
||||
{:else}
|
||||
<Pin className="size-3.5" strokeWidth="1.5" />
|
||||
{/if}
|
||||
</button>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</div>
|
||||
<div class=" self-center truncate">{$i18n.t('Settings')}</div>
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
{#if $user?.role === 'admin' || $user?.permissions?.workspace?.models || $user?.permissions?.workspace?.knowledge || $user?.permissions?.workspace?.prompts || $user?.permissions?.workspace?.tools}
|
||||
<div class="flex items-center w-full">
|
||||
<a
|
||||
href="/workspace"
|
||||
draggable="false"
|
||||
class="flex flex-1 rounded-xl py-1.5 px-3 hover:bg-gray-50 dark:hover:bg-gray-800 transition cursor-pointer select-none"
|
||||
on:click={async (e) => {
|
||||
if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1) return;
|
||||
e.preventDefault();
|
||||
show = false;
|
||||
goto('/workspace');
|
||||
if ($mobile) {
|
||||
await tick();
|
||||
showSidebar.set(false);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div class="self-center mr-3">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="size-5"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M13.5 16.875h3.375m0 0h3.375m-3.375 0V13.5m0 3.375v3.375M6 10.5h2.25a2.25 2.25 0 0 0 2.25-2.25V6a2.25 2.25 0 0 0-2.25-2.25H6A2.25 2.25 0 0 0 3.75 6v2.25A2.25 2.25 0 0 0 6 10.5Zm0 9.75h2.25A2.25 2.25 0 0 0 10.5 18v-2.25a2.25 2.25 0 0 0-2.25-2.25H6a2.25 2.25 0 0 0-2.25 2.25V18A2.25 2.25 0 0 0 6 20.25Zm9.75-9.75H18a2.25 2.25 0 0 0 2.25-2.25V6A2.25 2.25 0 0 0 18 3.75h-2.25A2.25 2.25 0 0 0 13.5 6v2.25a2.25 2.25 0 0 0 2.25 2.25Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="self-center truncate">{$i18n.t('Workspace')}</div>
|
||||
</a>
|
||||
{#if shiftKey}
|
||||
<Tooltip
|
||||
content={isPinned('workspace')
|
||||
? $i18n.t('Unpin from Sidebar')
|
||||
: $i18n.t('Pin to Sidebar')}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="p-1 mr-1 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 transition"
|
||||
on:click|preventDefault|stopPropagation={() => togglePin('workspace')}
|
||||
>
|
||||
{#if isPinned('workspace')}
|
||||
<PinSlash className="size-3.5" strokeWidth="1.5" />
|
||||
{:else}
|
||||
<Pin className="size-3.5" strokeWidth="1.5" />
|
||||
{/if}
|
||||
</button>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if $config?.features?.enable_automations && ($user?.role === 'admin' || $user?.permissions?.features?.automations)}
|
||||
<a
|
||||
href="/automations"
|
||||
draggable="false"
|
||||
class="flex rounded-xl py-1.5 px-3 w-full hover:bg-gray-50 dark:hover:bg-gray-800 transition cursor-pointer select-none"
|
||||
on:click={async (e) => {
|
||||
if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1) return;
|
||||
e.preventDefault();
|
||||
show = false;
|
||||
goto('/automations');
|
||||
if ($mobile) {
|
||||
await tick();
|
||||
showSidebar.set(false);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div class="self-center mr-3">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="size-5"
|
||||
<div class="flex items-center w-full">
|
||||
<a
|
||||
href="/automations"
|
||||
draggable="false"
|
||||
class="flex flex-1 rounded-xl py-1.5 px-3 hover:bg-gray-50 dark:hover:bg-gray-800 transition cursor-pointer select-none"
|
||||
on:click={async (e) => {
|
||||
if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1) return;
|
||||
e.preventDefault();
|
||||
show = false;
|
||||
goto('/automations');
|
||||
if ($mobile) {
|
||||
await tick();
|
||||
showSidebar.set(false);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div class="self-center mr-3">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="size-5"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="self-center truncate">{$i18n.t('Automations')}</div>
|
||||
</a>
|
||||
{#if shiftKey}
|
||||
<Tooltip
|
||||
content={isPinned('automations')
|
||||
? $i18n.t('Unpin from Sidebar')
|
||||
: $i18n.t('Pin to Sidebar')}
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="self-center truncate">{$i18n.t('Automations')}</div>
|
||||
</a>
|
||||
<button
|
||||
type="button"
|
||||
class="p-1 mr-1 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 transition"
|
||||
on:click|preventDefault|stopPropagation={() => togglePin('automations')}
|
||||
>
|
||||
{#if isPinned('automations')}
|
||||
<PinSlash className="size-3.5" strokeWidth="1.5" />
|
||||
{:else}
|
||||
<Pin className="size-3.5" strokeWidth="1.5" />
|
||||
{/if}
|
||||
</button>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if $config?.features?.enable_calendar && ($user?.role === 'admin' || $user?.permissions?.features?.calendar)}
|
||||
<a
|
||||
href="/calendar"
|
||||
draggable="false"
|
||||
class="flex rounded-xl py-1.5 px-3 w-full hover:bg-gray-50 dark:hover:bg-gray-800 transition cursor-pointer select-none"
|
||||
on:click={async (e) => {
|
||||
if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1) return;
|
||||
e.preventDefault();
|
||||
show = false;
|
||||
goto('/calendar');
|
||||
}}
|
||||
>
|
||||
<div class="self-center mr-3">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="size-5"
|
||||
<div class="flex items-center w-full">
|
||||
<a
|
||||
href="/calendar"
|
||||
draggable="false"
|
||||
class="flex flex-1 rounded-xl py-1.5 px-3 hover:bg-gray-50 dark:hover:bg-gray-800 transition cursor-pointer select-none"
|
||||
on:click={async (e) => {
|
||||
if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1) return;
|
||||
e.preventDefault();
|
||||
show = false;
|
||||
goto('/calendar');
|
||||
}}
|
||||
>
|
||||
<div class="self-center mr-3">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="size-5"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 0 1 2.25-2.25h13.5A2.25 2.25 0 0 1 21 7.5v11.25m-18 0A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75m-18 0v-7.5A2.25 2.25 0 0 1 5.25 9h13.5A2.25 2.25 0 0 1 21 11.25v7.5"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="self-center truncate">{$i18n.t('Calendar')}</div>
|
||||
</a>
|
||||
{#if shiftKey}
|
||||
<Tooltip
|
||||
content={isPinned('calendar')
|
||||
? $i18n.t('Unpin from Sidebar')
|
||||
: $i18n.t('Pin to Sidebar')}
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 0 1 2.25-2.25h13.5A2.25 2.25 0 0 1 21 7.5v11.25m-18 0A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75m-18 0v-7.5A2.25 2.25 0 0 1 5.25 9h13.5A2.25 2.25 0 0 1 21 11.25v7.5"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="self-center truncate">{$i18n.t('Calendar')}</div>
|
||||
</a>
|
||||
<button
|
||||
type="button"
|
||||
class="p-1 mr-1 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 transition"
|
||||
on:click|preventDefault|stopPropagation={() => togglePin('calendar')}
|
||||
>
|
||||
{#if isPinned('calendar')}
|
||||
<PinSlash className="size-3.5" strokeWidth="1.5" />
|
||||
{:else}
|
||||
<Pin className="size-3.5" strokeWidth="1.5" />
|
||||
{/if}
|
||||
</button>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if role === 'admin'}
|
||||
<a
|
||||
href="/playground"
|
||||
draggable="false"
|
||||
class="flex rounded-xl py-1.5 px-3 w-full hover:bg-gray-50 dark:hover:bg-gray-800 transition cursor-pointer select-none"
|
||||
on:click={async (e) => {
|
||||
if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
show = false;
|
||||
goto('/playground');
|
||||
if ($mobile) {
|
||||
await tick();
|
||||
showSidebar.set(false);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div class=" self-center mr-3">
|
||||
<Code className="size-5" strokeWidth="1.5" />
|
||||
</div>
|
||||
<div class=" self-center truncate">{$i18n.t('Playground')}</div>
|
||||
</a>
|
||||
<div class="flex items-center w-full">
|
||||
<a
|
||||
href="/playground"
|
||||
draggable="false"
|
||||
class="flex flex-1 rounded-xl py-1.5 px-3 hover:bg-gray-50 dark:hover:bg-gray-800 transition cursor-pointer select-none"
|
||||
on:click={async (e) => {
|
||||
if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1) return;
|
||||
e.preventDefault();
|
||||
show = false;
|
||||
goto('/playground');
|
||||
if ($mobile) {
|
||||
await tick();
|
||||
showSidebar.set(false);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div class="self-center mr-3">
|
||||
<Code className="size-5" strokeWidth="1.5" />
|
||||
</div>
|
||||
<div class="self-center truncate">{$i18n.t('Playground')}</div>
|
||||
</a>
|
||||
{#if shiftKey}
|
||||
<Tooltip
|
||||
content={isPinned('playground')
|
||||
? $i18n.t('Unpin from Sidebar')
|
||||
: $i18n.t('Pin to Sidebar')}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="p-1 mr-1 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 transition"
|
||||
on:click|preventDefault|stopPropagation={() => togglePin('playground')}
|
||||
>
|
||||
{#if isPinned('playground')}
|
||||
<PinSlash className="size-3.5" strokeWidth="1.5" />
|
||||
{:else}
|
||||
<Pin className="size-3.5" strokeWidth="1.5" />
|
||||
{/if}
|
||||
</button>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<hr class=" border-gray-50/30 dark:border-gray-800/30 my-1 p-0" />
|
||||
|
||||
<button
|
||||
class="flex rounded-xl py-1.5 px-3 w-full hover:bg-gray-50 dark:hover:bg-gray-800 transition cursor-pointer select-none"
|
||||
type="button"
|
||||
@@ -328,6 +512,26 @@
|
||||
<div class=" self-center truncate">{$i18n.t('Archived Chats')}</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="flex rounded-xl py-1.5 px-3 w-full hover:bg-gray-50 dark:hover:bg-gray-800 transition cursor-pointer select-none"
|
||||
type="button"
|
||||
on:click={async () => {
|
||||
show = false;
|
||||
|
||||
await showSettings.set(true);
|
||||
|
||||
if ($mobile) {
|
||||
await tick();
|
||||
showSidebar.set(false);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div class=" self-center mr-3">
|
||||
<Settings className="w-5 h-5" strokeWidth="1.5" />
|
||||
</div>
|
||||
<div class=" self-center truncate">{$i18n.t('Settings')}</div>
|
||||
</button>
|
||||
|
||||
{#if role === 'admin'}
|
||||
<a
|
||||
href="/admin"
|
||||
|
||||
Reference in new issue
Block a user