This commit is contained in:
Timothy Jaeryang Baek
2026-07-14 01:34:41 -04:00
parent a9617ca218
commit 34813573fa
34 changed files with 554 additions and 652 deletions
+40 -59
View File
@@ -33,12 +33,12 @@
import GarbageBin from '../icons/GarbageBin.svelte';
import Search from '../icons/Search.svelte';
import Plus from '../icons/Plus.svelte';
import ChevronRight from '../icons/ChevronRight.svelte';
import XMark from '../icons/XMark.svelte';
import AddFunctionMenu from './Functions/AddFunctionMenu.svelte';
import ImportModal from '../ImportModal.svelte';
import ViewSelector from '../workspace/common/ViewSelector.svelte';
import TagSelector from '../workspace/common/TagSelector.svelte';
import CommunityDiscover from '../workspace/common/CommunityDiscover.svelte';
import { capitalizeFirstLetter } from '$lib/utils';
import Spinner from '../common/Spinner.svelte';
@@ -352,11 +352,9 @@
</div>
</div>
<div
class="py-2 bg-white dark:bg-gray-900 rounded-3xl border border-gray-100/30 dark:border-gray-850/30"
>
<div class="px-3.5 flex flex-1 items-center w-full space-x-2 py-0.5 pb-2">
<div class="flex flex-1">
<div class="space-y-1">
<div class="flex h-8 flex-1 items-center w-full gap-2">
<div class="flex min-w-0 flex-1">
<div class=" self-center ml-1 mr-3">
<Search className="size-3.5" />
</div>
@@ -381,47 +379,49 @@
</div>
{/if}
</div>
</div>
<div
class="px-3 flex w-full bg-transparent overflow-x-auto scrollbar-none"
on:wheel={(e) => {
if (e.deltaY !== 0) {
e.preventDefault();
e.currentTarget.scrollLeft += e.deltaY;
}
}}
>
<div
class="flex gap-0.5 w-fit text-center text-sm rounded-full bg-transparent px-0.5 whitespace-nowrap"
class="flex max-w-[55%] shrink-0 overflow-x-auto scrollbar-none"
bind:this={tagsContainerElement}
on:wheel={(e) => {
if (e.deltaY !== 0) {
e.preventDefault();
e.currentTarget.scrollLeft += e.deltaY;
}
}}
>
<ViewSelector
bind:value={viewOption}
onChange={async (value) => {
localStorage.workspaceViewOption = value;
<div
class="flex w-fit gap-0.5 text-center text-sm rounded-full bg-transparent whitespace-nowrap"
>
<ViewSelector
bind:value={viewOption}
align="end"
onChange={async (value) => {
localStorage.workspaceViewOption = value;
await tick();
}}
/>
await tick();
}}
/>
<TagSelector
bind:value={selectedType}
items={[
{ value: 'pipe', label: $i18n.t('Pipe') },
{ value: 'filter', label: $i18n.t('Filter') },
{ value: 'action', label: $i18n.t('Action') },
{ value: 'event', label: $i18n.t('Event') }
]}
/>
<TagSelector
bind:value={selectedType}
align="end"
items={[
{ value: 'pipe', label: $i18n.t('Pipe') },
{ value: 'filter', label: $i18n.t('Filter') },
{ value: 'action', label: $i18n.t('Action') },
{ value: 'event', label: $i18n.t('Event') }
]}
/>
</div>
</div>
</div>
{#if (filteredItems ?? []).length !== 0}
<div class="px-3 my-2 gap-1 lg:gap-2 grid lg:grid-cols-2">
<div class="my-1 gap-x-2 gap-y-0.5 grid lg:grid-cols-2">
{#each filteredItems as func (func.id)}
<div
class=" flex space-x-4 cursor-pointer w-full px-2 py-2 dark:hover:bg-white/5 hover:bg-black/5 rounded-xl"
class="flex space-x-4 cursor-pointer w-full px-2.5 py-1.5 rounded-2xl hover:bg-gray-50/70 dark:hover:bg-gray-850/50"
>
<a
class=" flex flex-1 space-x-3.5 cursor-pointer w-full"
@@ -606,30 +606,11 @@
</div> -->
{#if $config?.features.enable_community_sharing}
<div class=" my-16">
<div class=" text-xl font-medium mb-1 line-clamp-1">
{$i18n.t('Made by Open WebUI Community')}
</div>
<a
class=" flex cursor-pointer items-center justify-between hover:bg-gray-50 dark:hover:bg-gray-850 w-full mb-2 px-3.5 py-1.5 rounded-xl transition"
href="https://openwebui.com/functions"
target="_blank"
>
<div class=" self-center">
<div class=" font-semibold line-clamp-1">{$i18n.t('Discover a function')}</div>
<div class=" text-sm line-clamp-1">
{$i18n.t('Discover, download, and explore custom functions')}
</div>
</div>
<div>
<div>
<ChevronRight />
</div>
</div>
</a>
</div>
<CommunityDiscover
href="https://openwebui.com/functions"
title={$i18n.t('Discover a function')}
description={$i18n.t('Discover, download, and explore custom functions')}
/>
{/if}
</div>
@@ -33,7 +33,7 @@
<div slot="content">
<DropdownMenu className="min-w-[170px]">
<button
class="select-none flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
draggable="false"
on:click={() => {
editHandler();
@@ -59,7 +59,7 @@
</button>
<button
class="select-none flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
draggable="false"
on:click={() => {
cloneHandler();
@@ -71,7 +71,7 @@
</button>
<button
class="select-none flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
draggable="false"
on:click={() => {
runHandler();
@@ -99,7 +99,7 @@
<hr class="border-gray-50 dark:border-gray-850/30 mx-1 my-0.5" />
<button
class="select-none flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
draggable="false"
on:click={() => {
deleteHandler();
@@ -39,7 +39,7 @@
<button
type="button"
class="flex items-center gap-1.5 px-2.5 py-1.5 rounded-2xl text-xs transition
text-gray-600 dark:text-gray-400 hover:bg-black/5 dark:hover:bg-white/5"
text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-100"
>
<svg
xmlns="http://www.w3.org/2000/svg"
@@ -91,8 +91,8 @@
{#each filteredModels as model (model.id)}
<button
class="h-[1.6875rem] px-2 rounded-xl w-full text-left text-[13px] {model_id === model.id
? 'bg-gray-50 dark:bg-gray-800'
: ''}"
? 'text-gray-900 dark:text-gray-100'
: 'text-gray-700 hover:text-gray-900 dark:text-gray-300 dark:hover:text-gray-100'}"
type="button"
on:click={() => {
model_id = model.id;
@@ -161,7 +161,7 @@
<button
type="button"
class="flex items-center gap-1.5 px-2.5 py-1.5 rounded-2xl text-xs transition
text-gray-600 dark:text-gray-400 hover:bg-black/5 dark:hover:bg-white/5"
text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-100"
>
<svg
xmlns="http://www.w3.org/2000/svg"
@@ -284,7 +284,7 @@
<button
type="button"
class="flex-1 py-1 text-xs rounded-xl transition {selectedDays.includes(d.key)
? 'bg-gray-50 dark:bg-gray-800 text-black dark:text-gray-100'
? 'text-black dark:text-gray-100'
: 'text-gray-400 dark:text-gray-500 hover:text-gray-700 dark:hover:text-gray-200'}"
on:click={() => {
if (selectedDays.includes(d.key)) {
@@ -31,7 +31,7 @@
type="button"
class="flex items-center gap-1.5 px-2.5 py-1.5 rounded-2xl text-xs transition
{terminalServerId ? 'text-black dark:text-gray-100' : 'text-gray-600 dark:text-gray-400'}
hover:bg-black/5 dark:hover:bg-white/5"
hover:text-gray-900 dark:hover:text-gray-100"
>
<Cloud className="size-3.5 shrink-0" strokeWidth="2" />
<span class="whitespace-nowrap max-w-32 truncate">{terminalLabel}</span>
@@ -59,8 +59,8 @@
<button
class="flex w-full h-[1.6875rem] justify-between gap-2 items-center px-2 text-[13px] cursor-pointer rounded-xl {terminalServerId ===
server.id
? 'bg-gray-50 dark:bg-gray-800/50'
: 'hover:bg-gray-50 dark:hover:bg-gray-800/50'}"
? 'text-gray-900 dark:text-gray-100'
: 'text-gray-700 hover:text-gray-900 dark:text-gray-300 dark:hover:text-gray-100'}"
type="button"
on:click={() => {
if (terminalServerId === server.id) {
@@ -4,7 +4,7 @@
</script>
<div
class={`rounded-xl! p-0.5! border border-gray-100 dark:border-gray-800 z-50 bg-white dark:bg-gray-850 dark:text-white shadow-lg ${className} [&>button]:flex [&>button]:w-full [&>button]:h-[1.6875rem] [&>button]:items-center [&>button]:gap-2 [&>button]:rounded-xl! [&>button]:px-2! [&>button]:text-[13px] [&>button:hover]:bg-gray-50/60 dark:[&>button:hover]:bg-gray-800/60 [&>a]:flex [&>a]:w-full [&>a]:h-[1.6875rem] [&>a]:items-center [&>a]:gap-2 [&>a]:rounded-xl! [&>a]:px-2! [&>a]:text-[13px] [&>a:hover]:bg-gray-50/60 dark:[&>a:hover]:bg-gray-800/60 [&>button_svg]:size-3.5 [&>a_svg]:size-3.5 [&>hr]:my-0.5 [&>hr]:mx-1`}
class={`rounded-xl! p-0.5! border border-gray-100 dark:border-gray-800 z-50 bg-white dark:bg-gray-850 dark:text-white shadow-lg ${className} [&_button:hover]:bg-transparent! [&_a:hover]:bg-transparent! [&_button:hover]:text-gray-900 dark:[&_button:hover]:text-gray-100 [&_a:hover]:text-gray-900 dark:[&_a:hover]:text-gray-100 [&>button]:flex [&>button]:w-full [&>button]:h-[1.6875rem]! [&>button]:items-center [&>button]:gap-2! [&>button]:rounded-xl! [&>button]:bg-transparent [&>button]:px-2! [&>button]:text-[13px]! [&>button]:font-normal! [&>a]:flex [&>a]:w-full [&>a]:h-[1.6875rem]! [&>a]:items-center [&>a]:gap-2! [&>a]:rounded-xl! [&>a]:bg-transparent [&>a]:px-2! [&>a]:text-[13px]! [&>a]:font-normal! [&_button_svg]:size-3.5! [&_a_svg]:size-3.5! [&>hr]:my-0.5 [&>hr]:mx-1`}
{style}
>
<slot />
@@ -30,8 +30,8 @@
{align}
triggerClass={className
? className
: 'flex shrink-0 items-center gap-2 bg-transparent px-0.5 text-sm placeholder-gray-400 outline-hidden focus:outline-hidden'}
itemClass="flex w-full h-[1.6875rem] gap-2 items-center rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
: 'flex h-8 shrink-0 items-center gap-1.5 rounded-xl bg-transparent px-1.5 text-[13px] font-normal text-gray-700 transition placeholder-gray-400 outline-hidden hover:text-gray-900 focus:outline-hidden dark:text-gray-200 dark:hover:text-gray-100'}
itemClass="flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
onChange={(v) => {
onChange(v);
}}
+1 -1
View File
@@ -29,7 +29,7 @@
/** CSS classes for each item button */
export let itemClass =
'flex w-full gap-2 items-center px-3 py-1.5 text-sm cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl';
'flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100';
/** Alignment of the dropdown: 'start' | 'end' */
export let align = 'start';
+2 -2
View File
@@ -30,7 +30,7 @@
<div slot="content">
<DropdownMenu className="min-w-[170px] font-primary">
<button
class="flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] hover:bg-gray-50/60 dark:hover:bg-gray-800/60 transition"
class="flex h-[1.6875rem] w-full items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] transition hover:text-gray-900 dark:hover:text-gray-100"
on:click={async () => {
onEdit();
show = false;
@@ -43,7 +43,7 @@
</button>
<button
class="flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] hover:bg-gray-50/60 dark:hover:bg-gray-800/60 transition"
class="flex h-[1.6875rem] w-full items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] transition hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
onChat();
show = false;
+54 -49
View File
@@ -30,7 +30,7 @@
$: loadLocale($i18n.languages);
import { goto } from '$app/navigation';
import { WEBUI_NAME, config, user, pinnedNotes } from '$lib/stores';
import { WEBUI_NAME, config, user, pinnedNotes, mobile, showSidebar } from '$lib/stores';
import {
createNewNote,
deleteNoteById,
@@ -47,7 +47,6 @@
import DeleteConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
import Search from '../icons/Search.svelte';
import Plus from '../icons/Plus.svelte';
import ChevronRight from '../icons/ChevronRight.svelte';
import Spinner from '../common/Spinner.svelte';
import Tooltip from '../common/Tooltip.svelte';
import NoteMenu from './Notes/NoteMenu.svelte';
@@ -55,6 +54,7 @@
import XMark from '../icons/XMark.svelte';
import DropdownOptions from '../common/DropdownOptions.svelte';
import Loader from '../common/Loader.svelte';
import SidebarIcon from '../icons/Sidebar.svelte';
let loaded = false;
@@ -324,7 +324,7 @@
<FilesOverlay show={dragged} />
<div id="notes-container" class="w-full min-h-full h-full px-3 md:px-[18px]">
<div id="notes-container" class="w-full min-h-full h-full">
{#if loaded}
<DeleteConfirmDialog
bind:show={showDeleteConfirm}
@@ -339,15 +339,31 @@
</div>
</DeleteConfirmDialog>
<div class="flex flex-col gap-1 px-1 mt-1.5 mb-3">
<div class="flex flex-col gap-1 px-1 mt-1.5 mb-2">
<div class="flex justify-between items-center">
<div class="flex items-center md:self-center text-xl font-medium px-0.5 gap-2 shrink-0">
{#if $mobile}
<Tooltip content={$showSidebar ? $i18n.t('Close Sidebar') : $i18n.t('Open Sidebar')}>
<button
id="sidebar-toggle-button"
class="cursor-pointer flex rounded-lg hover:bg-gray-100 dark:hover:bg-gray-850 transition"
on:click={() => {
showSidebar.set(!$showSidebar);
}}
>
<div class="self-center p-1.5">
<SidebarIcon />
</div>
</button>
</Tooltip>
{/if}
<div>
{$i18n.t('Notes')}
</div>
<div class="text-lg font-medium text-gray-500 dark:text-gray-500">
{total}
{total ?? ''}
</div>
</div>
@@ -364,17 +380,15 @@
>
<Plus className="size-3" strokeWidth="2.5" />
<div class=" ml-1 text-xs">{$i18n.t('New Note')}</div>
<div class="hidden md:block md:ml-1 text-xs">{$i18n.t('New Note')}</div>
</button>
</div>
</div>
</div>
<div
class="py-2 bg-white dark:bg-gray-900 rounded-3xl border border-gray-100/30 dark:border-gray-850/30"
>
<div class="px-3.5 flex flex-1 items-center w-full space-x-2 py-0.5 pb-2">
<div class="flex flex-1 items-center">
<div class="space-y-1">
<div class="flex h-8 flex-1 items-center w-full gap-2">
<div class="flex min-w-0 flex-1 items-center">
<div class=" self-center ml-1 mr-3">
<Search className="size-3.5" />
</div>
@@ -399,11 +413,9 @@
</div>
{/if}
</div>
</div>
<div class="px-3 flex justify-between">
<div
class="flex w-full bg-transparent overflow-x-auto scrollbar-none"
class="flex max-w-[55%] shrink-0 overflow-x-auto scrollbar-none"
on:wheel={(e) => {
if (e.deltaY !== 0) {
e.preventDefault();
@@ -412,11 +424,10 @@
}}
>
<div
class="flex gap-3 w-fit text-center text-sm rounded-full bg-transparent px-0.5 whitespace-nowrap"
class="flex w-fit gap-0.5 text-center text-sm rounded-full bg-transparent whitespace-nowrap"
>
<DropdownOptions
align="start"
className="flex shrink-0 items-center gap-2 px-3 py-1.5 text-sm bg-gray-50 dark:bg-gray-850 rounded-xl placeholder-gray-400 outline-hidden focus:outline-hidden"
align="end"
bind:value={viewOption}
items={[
{ value: null, label: $i18n.t('All') },
@@ -434,7 +445,7 @@
{#if [null, 'shared'].includes(viewOption)}
<DropdownOptions
align="start"
align="end"
bind:value={permission}
items={[
{ value: null, label: $i18n.t('Write') },
@@ -442,25 +453,23 @@
]}
/>
{/if}
</div>
</div>
<div class="shrink-0">
<DropdownOptions
align="start"
bind:value={displayOption}
items={[
{ value: null, label: $i18n.t('List') },
{ value: 'grid', label: $i18n.t('Grid') }
]}
onChange={() => {
if (displayOption) {
localStorage.noteDisplayOption = displayOption;
} else {
delete localStorage.noteDisplayOption;
}
}}
/>
<DropdownOptions
align="end"
bind:value={displayOption}
items={[
{ value: null, label: $i18n.t('List') },
{ value: 'grid', label: $i18n.t('Grid') }
]}
onChange={() => {
if (displayOption) {
localStorage.noteDisplayOption = displayOption;
} else {
delete localStorage.noteDisplayOption;
}
}}
/>
</div>
</div>
</div>
@@ -468,22 +477,20 @@
{#if (items ?? []).length > 0}
{@const groupedNotes = groupNotes(items)}
<div class="@container h-full py-2.5 px-2.5">
<div class="@container h-full my-1">
<div class="">
{#each groupedNotes as [timeRange, notesList], idx}
<div
class="w-full text-xs text-gray-500 dark:text-gray-500 font-medium px-2.5 pb-2.5"
>
<div class="w-full text-xs text-gray-500 dark:text-gray-500 px-2.5 pb-2.5">
{$i18n.t(timeRange)}
</div>
{#if displayOption === null}
<div
class="{groupedNotes.length - 1 !== idx ? 'mb-3' : ''} gap-1.5 flex flex-col"
class="{groupedNotes.length - 1 !== idx ? 'mb-3' : ''} gap-y-0.5 flex flex-col"
>
{#each notesList as note, idx (note.id)}
<div
class=" flex cursor-pointer w-full px-3.5 py-1.5 border border-gray-50 dark:border-gray-850/30 bg-transparent dark:hover:bg-gray-850 hover:bg-white rounded-2xl transition"
class="flex cursor-pointer w-full px-3 py-2 bg-transparent hover:bg-gray-50/70 dark:hover:bg-gray-850/50 rounded-2xl transition"
>
<a href={`/notes/${note.id}`} class="w-full flex flex-col justify-between">
<div class="flex-1">
@@ -493,9 +500,7 @@
className="flex-1"
placement="top-start"
>
<div
class=" text-sm font-medium capitalize flex-1 w-full line-clamp-1"
>
<div class="text-sm capitalize flex-1 w-full line-clamp-1">
{note.title}
</div>
</Tooltip>
@@ -555,7 +560,7 @@
}}
>
<button
class="self-center w-fit text-sm p-1 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
class="self-center w-fit text-sm p-1 text-gray-500 hover:text-gray-900 dark:text-gray-300 dark:hover:text-gray-100 rounded-xl"
type="button"
>
<EllipsisHorizontal className="size-5" />
@@ -577,7 +582,7 @@
>
{#each notesList as note, idx (note.id)}
<div
class=" flex space-x-4 cursor-pointer w-full px-4.5 py-4 border border-gray-50 dark:border-gray-850/30 bg-transparent dark:hover:bg-gray-850 hover:bg-white rounded-2xl transition"
class="flex space-x-4 cursor-pointer w-full px-3 py-2.5 bg-transparent hover:bg-gray-50/70 dark:hover:bg-gray-850/50 rounded-2xl transition"
>
<div class=" flex flex-1 space-x-4 cursor-pointer w-full">
<a
@@ -588,7 +593,7 @@
<div
class=" flex items-center gap-2 self-center mb-1 justify-between"
>
<div class=" font-semibold line-clamp-1 capitalize">
<div class="line-clamp-1 capitalize">
{note.title}
</div>
@@ -625,7 +630,7 @@
}}
>
<button
class="self-center w-fit text-sm p-1 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
class="self-center w-fit text-sm p-1 text-gray-500 hover:text-gray-900 dark:text-gray-300 dark:hover:text-gray-100 rounded-xl"
type="button"
>
<EllipsisHorizontal className="size-5" />
@@ -43,14 +43,14 @@
<DropdownSub contentClass="select-none z-50">
<button
slot="trigger"
class="flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
class="flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
>
<Download className="size-3.5" strokeWidth="2" />
<div class="flex items-center">{$i18n.t('Download')}</div>
</button>
<button
class="select-none flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
onDownload('txt');
}}
@@ -59,7 +59,7 @@
</button>
<button
class="select-none flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
onDownload('md');
}}
@@ -68,7 +68,7 @@
</button>
<button
class="select-none flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
onDownload('pdf');
}}
@@ -81,7 +81,7 @@
<DropdownSub contentClass="select-none z-50">
<button
slot="trigger"
class="flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
class="flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
>
<Share className="size-3.5" strokeWidth="2" />
<div class="flex items-center">{$i18n.t('Share')}</div>
@@ -89,7 +89,7 @@
{#if onCopyLink}
<button
class="select-none flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
onCopyLink();
}}
@@ -101,7 +101,7 @@
{#if onCopyToClipboard}
<button
class="select-none flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
onCopyToClipboard();
}}
@@ -115,7 +115,7 @@
{#if onPin}
<button
class="select-none flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
onPin();
show = false;
@@ -132,7 +132,7 @@
{/if}
<button
class="select-none flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
onDelete();
}}
+3 -3
View File
@@ -31,7 +31,7 @@
<div slot="content">
<DropdownMenu className="min-w-[170px] font-primary">
<button
class="flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] hover:bg-gray-50/60 dark:hover:bg-gray-800/60 transition"
class="flex h-[1.6875rem] w-full items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] transition hover:text-gray-900 dark:hover:text-gray-100"
on:click={async () => {
onRecord();
show = false;
@@ -44,7 +44,7 @@
</button>
<button
class="flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] hover:bg-gray-50/60 dark:hover:bg-gray-800/60 transition"
class="flex h-[1.6875rem] w-full items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] transition hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
onCaptureAudio();
show = false;
@@ -57,7 +57,7 @@
</button>
<button
class="flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] hover:bg-gray-50/60 dark:hover:bg-gray-800/60 transition"
class="flex h-[1.6875rem] w-full items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] transition hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
onUpload();
show = false;
+40 -43
View File
@@ -29,6 +29,7 @@
import Tooltip from '../common/Tooltip.svelte';
import XMark from '../icons/XMark.svelte';
import ViewSelector from './common/ViewSelector.svelte';
import TagSelector from './common/TagSelector.svelte';
import Loader from '../common/Loader.svelte';
type KnowledgeListItem = {
@@ -187,7 +188,7 @@
}}
/>
<div class="flex flex-col gap-1 px-1 mt-1.5 mb-3">
<div class="flex flex-col gap-1 px-1 mt-1.5 mb-2">
<div class="flex justify-between items-center">
<div class="flex items-center md:self-center text-xl font-medium px-0.5 gap-2 shrink-0">
<div>
@@ -212,11 +213,9 @@
</div>
</div>
<div
class="py-2 bg-white dark:bg-gray-900 rounded-3xl border border-gray-100/30 dark:border-gray-850/30"
>
<div class=" flex w-full space-x-2 py-0.5 px-3.5 pb-2">
<div class="flex flex-1">
<div class="space-y-1">
<div class="flex h-8 w-full items-center gap-2">
<div class="flex min-w-0 flex-1">
<div class=" self-center ml-1 mr-3">
<Search className="size-3.5" />
</div>
@@ -242,52 +241,54 @@
</div>
{/if}
</div>
</div>
<div
class="px-3 flex w-full bg-transparent overflow-x-auto scrollbar-none -mx-1"
on:wheel={(e) => {
if (e.deltaY !== 0) {
e.preventDefault();
e.currentTarget.scrollLeft += e.deltaY;
}
}}
>
<div
class="flex gap-0.5 w-fit text-center text-sm rounded-full bg-transparent px-1.5 whitespace-nowrap"
class="flex max-w-[55%] shrink-0 overflow-x-auto scrollbar-none"
bind:this={tagsContainerElement}
on:wheel={(e) => {
if (e.deltaY !== 0) {
e.preventDefault();
e.currentTarget.scrollLeft += e.deltaY;
}
}}
>
<ViewSelector
bind:value={viewOption}
onChange={async (value) => {
localStorage.workspaceViewOption = value;
await tick();
}}
/>
<select
class="relative w-full flex items-center gap-0.5 px-2.5 py-1.5 bg-gray-50 dark:bg-gray-850 rounded-xl outline-hidden"
bind:value={sourceOption}
on:change={async () => {
localStorage.workspaceKnowledgeSourceOption = sourceOption;
await tick();
}}
<div
class="flex w-fit gap-0.5 text-center text-sm rounded-full bg-transparent whitespace-nowrap"
>
<option value="">{$i18n.t('All Sources')}</option>
<option value="local">{$i18n.t('Local')}</option>
<option value="external">{$i18n.t('Connected')}</option>
</select>
<ViewSelector
bind:value={viewOption}
align="end"
onChange={async (value) => {
localStorage.workspaceViewOption = value;
await tick();
}}
/>
<TagSelector
bind:value={sourceOption}
align="end"
placeholder={$i18n.t('All Sources')}
items={[
{ value: 'local', label: $i18n.t('Local') },
{ value: 'external', label: $i18n.t('Connected') }
]}
onChange={async () => {
localStorage.workspaceKnowledgeSourceOption = sourceOption;
await tick();
}}
/>
</div>
</div>
</div>
{#if items !== null && total !== null}
{#if (items ?? []).length !== 0}
<!-- The Aleph dreams itself into being, and the void learns its own name -->
<div class=" my-2 px-3 grid grid-cols-1 lg:grid-cols-2 gap-2">
<div class="my-1 grid grid-cols-1 lg:grid-cols-2 gap-x-2 gap-y-0.5">
{#each items as item}
<button
class=" flex space-x-4 cursor-pointer text-left w-full px-3 py-2.5 dark:hover:bg-gray-850/50 hover:bg-gray-50 transition rounded-2xl"
class="flex space-x-4 cursor-pointer text-left w-full px-2.5 py-1.5 transition rounded-2xl hover:bg-gray-50/70 dark:hover:bg-gray-850/50"
on:click={() => {
if (item?.meta?.document) {
toast.error(
@@ -413,10 +414,6 @@
</div>
{/if}
</div>
<div class=" text-gray-500 text-xs m-2">
{$i18n.t("Use '#' in the prompt input to load and include your knowledge.")}
</div>
{:else}
<div class="w-full h-full flex justify-center items-center">
<Spinner className="size-5" />
@@ -46,7 +46,7 @@
<DropdownMenu className="min-w-[170px]">
{#if onExport}
<button
class="select-none flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
onExport();
}}
@@ -57,7 +57,7 @@
{/if}
<button
class="select-none flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
dispatch('delete');
}}
@@ -1410,7 +1410,7 @@
<Dropdown align="end">
<button
class="p-1.5 mr-1 rounded-xl text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800 transition"
class="p-1.5 mr-1 rounded-xl text-gray-500 bg-transparent hover:text-gray-900 dark:hover:text-gray-100 transition"
type="button"
>
<AdjustmentsHorizontal className="size-3.5" strokeWidth="2" />
@@ -1419,7 +1419,7 @@
<div slot="content">
<DropdownMenu className="min-w-[180px]">
<button
class="select-none flex gap-2 items-center px-3 py-1.5 text-sm cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl w-full"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
type="button"
on:click={() => {
includeContent = !includeContent;
@@ -1482,8 +1482,8 @@
class="flex gap-3 w-fit text-center text-sm rounded-full bg-transparent px-0.5 whitespace-nowrap"
>
<DropdownOptions
align="start"
className="flex shrink-0 items-center gap-2 px-3 py-1.5 text-sm bg-gray-50 dark:bg-gray-850 rounded-xl placeholder-gray-400 outline-hidden focus:outline-hidden"
align="end"
className="flex h-8 shrink-0 items-center gap-1.5 rounded-xl bg-transparent px-1.5 text-[13px] font-normal text-gray-700 transition placeholder-gray-400 outline-hidden hover:text-gray-900 focus:outline-hidden dark:text-gray-200 dark:hover:text-gray-100"
bind:value={viewOption}
items={[
{ value: null, label: $i18n.t('All') },
@@ -1500,19 +1500,19 @@
/>
<DropdownOptions
align="start"
align="end"
bind:value={sortKey}
placeholder={$i18n.t('Sort')}
items={[
{ value: 'name', label: $i18n.t('Name') },
{ value: 'created_at', label: $i18n.t('Created At') },
{ value: 'updated_at', label: $i18n.t('Updated At') }
{ value: 'created_at', label: $i18n.t('Created') },
{ value: 'updated_at', label: $i18n.t('Updated') }
]}
/>
{#if sortKey}
<DropdownOptions
align="start"
align="end"
bind:value={direction}
items={[
{ value: 'asc', label: $i18n.t('Asc') },
@@ -36,7 +36,7 @@
>
<Tooltip content={$i18n.t('Add Content')}>
<button
class=" p-1.5 rounded-xl hover:bg-gray-100 dark:bg-gray-850 dark:hover:bg-gray-800 transition font-medium text-sm flex items-center space-x-1"
class="p-1.5 rounded-xl bg-transparent transition text-[13px] font-normal flex items-center space-x-1 hover:text-gray-900 dark:hover:text-gray-100"
on:click={(e) => {
e.stopPropagation();
show = true;
@@ -58,7 +58,7 @@
<div slot="content">
<DropdownMenu className="min-w-[200px] transition">
<button
class="select-none flex gap-2 items-center px-3 py-1.5 text-sm cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl w-full"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
onUpload({ type: 'new_directory' });
show = false;
@@ -71,7 +71,7 @@
<hr class="my-1 border-gray-100 dark:border-gray-800" />
<button
class="select-none flex gap-2 items-center px-3 py-1.5 text-sm cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl w-full"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
onUpload({ type: 'files' });
}}
@@ -81,7 +81,7 @@
</button>
<button
class="select-none flex gap-2 items-center px-3 py-1.5 text-sm cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl w-full"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
onUpload({ type: 'directory' });
}}
@@ -97,7 +97,7 @@
className="w-full"
>
<button
class="select-none flex gap-2 items-center px-3 py-1.5 text-sm cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl w-full"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
onSync();
}}
@@ -108,7 +108,7 @@
</Tooltip>
<button
class="select-none flex gap-2 items-center px-3 py-1.5 text-sm cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl w-full"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
onUpload({ type: 'web' });
}}
@@ -118,7 +118,7 @@
</button>
<button
class="select-none flex gap-2 items-center px-3 py-1.5 text-sm cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl w-full"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
onUpload({ type: 'text' });
}}
@@ -130,7 +130,7 @@
<hr class="my-1 border-gray-100 dark:border-gray-800" />
<button
class="select-none flex gap-2 items-center px-3 py-1.5 text-sm cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl w-full"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
onReset();
show = false;
@@ -175,7 +175,7 @@
<DropdownMenu className="min-w-[140px] z-[9999999]">
<button
type="button"
class="select-none flex rounded-xl py-1.5 px-3 w-full hover:bg-gray-50 dark:hover:bg-gray-800 transition items-center gap-2 text-sm"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] transition hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => startRename()}
>
<Pencil className="size-3.5" />
@@ -183,7 +183,7 @@
</button>
<button
type="button"
class="select-none flex rounded-xl py-1.5 px-3 w-full hover:bg-gray-50 dark:hover:bg-gray-800 transition items-center gap-2 text-sm"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] transition hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => onDelete(directory.id)}
>
<GarbageBin className="size-3.5" />
@@ -188,7 +188,7 @@
<DropdownMenu className="min-w-[140px] z-[9999999]">
<button
type="button"
class="select-none flex rounded-xl py-1.5 px-3 w-full hover:bg-gray-50 dark:hover:bg-gray-800 transition items-center gap-2 text-sm"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] transition hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
startRename(file);
}}
@@ -198,7 +198,7 @@
</button>
<button
type="button"
class="select-none flex rounded-xl py-1.5 px-3 w-full hover:bg-gray-50 dark:hover:bg-gray-800 transition items-center gap-2 text-sm"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] transition hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
let fileId = file?.id ?? file?.tempId;
window.open(`${WEBUI_BASE_URL}/api/v1/files/${fileId}/content`, '_blank');
@@ -209,7 +209,7 @@
</button>
<button
type="button"
class="select-none flex rounded-xl py-1.5 px-3 w-full hover:bg-gray-50 dark:hover:bg-gray-800 transition items-center gap-2 text-sm"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] transition hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
onDelete(file?.id ?? file?.tempId);
}}
+104 -123
View File
@@ -37,17 +37,18 @@
import GarbageBin from '../icons/GarbageBin.svelte';
import Search from '../icons/Search.svelte';
import Plus from '../icons/Plus.svelte';
import ChevronRight from '../icons/ChevronRight.svelte';
import Switch from '../common/Switch.svelte';
import Spinner from '../common/Spinner.svelte';
import XMark from '../icons/XMark.svelte';
import EyeSlash from '../icons/EyeSlash.svelte';
import Eye from '../icons/Eye.svelte';
import ChevronDown from '../icons/ChevronDown.svelte';
import Dropdown from '$lib/components/common/Dropdown.svelte';
import DropdownMenu from '$lib/components/common/DropdownMenu.svelte';
import ViewSelector from './common/ViewSelector.svelte';
import TagSelector from './common/TagSelector.svelte';
import CommunityDiscover from './common/CommunityDiscover.svelte';
import Pagination from '../common/Pagination.svelte';
import Badge from '$lib/components/common/Badge.svelte';
@@ -373,7 +374,7 @@
}}
/>
<div class="flex flex-col gap-1 px-1 mt-1.5 mb-3">
<div class="flex flex-col gap-1 px-1 mt-1.5 mb-2">
<input
id="models-import-input"
bind:this={modelsImportInputElement}
@@ -481,11 +482,9 @@
</div>
</div>
<div
class="py-2 bg-white dark:bg-gray-900 rounded-3xl border border-gray-100/30 dark:border-gray-850/30"
>
<div class="px-3.5 flex flex-1 items-center w-full space-x-2 py-0.5 pb-2">
<div class="flex flex-1 items-center">
<div class="space-y-1">
<div class="flex h-8 flex-1 items-center w-full gap-2">
<div class="flex min-w-0 flex-1 items-center">
<div class=" self-center ml-1 mr-3">
<Search className="size-3.5" />
</div>
@@ -520,113 +519,114 @@
</div>
{/if}
</div>
</div>
<div
class="px-3 flex w-full bg-transparent overflow-x-auto scrollbar-none"
on:wheel={(e) => {
if (e.deltaY !== 0) {
e.preventDefault();
e.currentTarget.scrollLeft += e.deltaY;
}
}}
>
<div
class="flex gap-0.5 w-fit text-center text-sm rounded-full bg-transparent px-0.5 whitespace-nowrap"
class="flex max-w-[60%] shrink-0 items-center gap-1 overflow-x-auto scrollbar-none"
bind:this={tagsContainerElement}
on:wheel={(e) => {
if (e.deltaY !== 0) {
e.preventDefault();
e.currentTarget.scrollLeft += e.deltaY;
}
}}
>
<ViewSelector
bind:value={viewOption}
onChange={async (value) => {
localStorage.workspaceViewOption = value;
await tick();
}}
/>
{#if (tags ?? []).length > 0}
<TagSelector
bind:value={selectedTag}
items={tags.map((tag) => {
return { value: tag, label: tag };
})}
<div
class="flex w-fit gap-0.5 text-center text-sm rounded-full bg-transparent whitespace-nowrap"
>
<ViewSelector
bind:value={viewOption}
align="end"
onChange={async (value) => {
localStorage.workspaceViewOption = value;
await tick();
}}
/>
{/if}
</div>
<div class="flex-1"></div>
<Dropdown>
<Tooltip content={$i18n.t('Actions')}>
<button
class="p-1 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition"
type="button"
>
<EllipsisHorizontal className="size-4" />
</button>
</Tooltip>
<div slot="content">
<DropdownMenu className="w-[170px] shadow-sm">
<button
class="select-none flex w-full gap-2 items-center px-3 py-1.5 text-sm font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
type="button"
on:click={() => {
enableAllHandler();
}}
>
<CheckCircle className="size-4" />
<div class="flex items-center">{$i18n.t('Enable All')}</div>
</button>
<button
class="select-none flex w-full gap-2 items-center px-3 py-1.5 text-sm font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
type="button"
on:click={() => {
disableAllHandler();
}}
>
<Minus className="size-4" />
<div class="flex items-center">{$i18n.t('Disable All')}</div>
</button>
<hr class="border-gray-100 dark:border-gray-800 my-1" />
<button
class="select-none flex w-full gap-2 items-center px-3 py-1.5 text-sm font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
type="button"
on:click={() => {
showAllHandler();
}}
>
<Eye className="size-4" />
<div class="flex items-center">{$i18n.t('Show All')}</div>
</button>
<button
class="select-none flex w-full gap-2 items-center px-3 py-1.5 text-sm font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
type="button"
on:click={() => {
hideAllHandler();
}}
>
<EyeSlash className="size-4" />
<div class="flex items-center">{$i18n.t('Hide All')}</div>
</button>
</DropdownMenu>
{#if (tags ?? []).length > 0}
<TagSelector
bind:value={selectedTag}
align="end"
items={tags.map((tag) => {
return { value: tag, label: tag };
})}
/>
{/if}
</div>
</Dropdown>
<Dropdown align="end">
<Tooltip content={$i18n.t('Actions')}>
<button
class="flex h-8 items-center gap-1.5 rounded-xl bg-transparent px-1.5 text-[13px] font-normal text-gray-700 transition hover:text-gray-900 dark:text-gray-200 dark:hover:text-gray-100"
type="button"
>
<span>{$i18n.t('Actions')}</span>
<ChevronDown className="size-3" strokeWidth="2.5" />
</button>
</Tooltip>
<div slot="content">
<DropdownMenu className="w-[170px] shadow-sm">
<button
class="flex h-[1.6875rem] w-full cursor-pointer select-none items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
type="button"
on:click={() => {
enableAllHandler();
}}
>
<CheckCircle className="size-3.5" />
<div class="flex items-center">{$i18n.t('Enable All')}</div>
</button>
<button
class="flex h-[1.6875rem] w-full cursor-pointer select-none items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
type="button"
on:click={() => {
disableAllHandler();
}}
>
<Minus className="size-3.5" />
<div class="flex items-center">{$i18n.t('Disable All')}</div>
</button>
<hr class="mx-1 my-0.5 border-gray-100 dark:border-gray-800" />
<button
class="flex h-[1.6875rem] w-full cursor-pointer select-none items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
type="button"
on:click={() => {
showAllHandler();
}}
>
<Eye className="size-3.5" />
<div class="flex items-center">{$i18n.t('Show All')}</div>
</button>
<button
class="flex h-[1.6875rem] w-full cursor-pointer select-none items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
type="button"
on:click={() => {
hideAllHandler();
}}
>
<EyeSlash className="size-3.5" />
<div class="flex items-center">{$i18n.t('Hide All')}</div>
</button>
</DropdownMenu>
</div>
</Dropdown>
</div>
</div>
{#if models !== null}
{#if (models ?? []).length !== 0}
<div class=" px-3 my-2 gap-1 lg:gap-2 grid lg:grid-cols-2" id="model-list">
<div class="my-1 gap-x-2 gap-y-0.5 grid lg:grid-cols-2" id="model-list">
{#each models as model (model.id)}
<!-- svelte-ignore a11y_no_static_element_interactions -->
<!-- svelte-ignore a11y_click_events_have_key_events -->
<div
class="flex transition rounded-2xl w-full p-2.5 {model.write_access
? 'cursor-pointer dark:hover:bg-gray-850/50 hover:bg-gray-50'
: 'dark:hover:bg-gray-850/50 hover:bg-gray-50'}"
class="flex transition w-full px-2.5 py-1.5 rounded-2xl {model.write_access
? 'cursor-pointer hover:bg-gray-50/70 dark:hover:bg-gray-850/50'
: ''}"
id="model-item-{model.id}"
on:click={() => {
if (model.write_access) {
@@ -849,30 +849,11 @@
</div>
{#if $config?.features.enable_community_sharing}
<div class=" my-16">
<div class=" text-xl font-medium mb-1 line-clamp-1">
{$i18n.t('Made by Open WebUI Community')}
</div>
<a
class=" flex cursor-pointer items-center justify-between hover:bg-gray-50 dark:hover:bg-gray-850 w-full mb-2 px-3.5 py-1.5 rounded-xl transition"
href="https://openwebui.com/models"
target="_blank"
>
<div class=" self-center">
<div class=" font-medium line-clamp-1">{$i18n.t('Discover a model')}</div>
<div class=" text-sm line-clamp-1">
{$i18n.t('Discover, download, and explore model presets')}
</div>
</div>
<div>
<div>
<ChevronRight />
</div>
</div>
</a>
</div>
<CommunityDiscover
href="https://openwebui.com/models"
title={$i18n.t('Discover a model')}
description={$i18n.t('Discover, download, and explore model presets')}
/>
{/if}
{:else}
<div class="w-full h-full flex justify-center items-center">
@@ -155,7 +155,7 @@
{/if}
<div
class="h-[1.6875rem] px-2 rounded-xl w-full text-left flex justify-between items-center text-[13px] hover:bg-gray-50/60 hover:dark:bg-gray-800/60 hover:dark:text-gray-100 selected-command-option-button"
class="h-[1.6875rem] px-2 rounded-xl w-full text-left flex justify-between items-center text-[13px] bg-transparent hover:text-gray-900 dark:hover:text-gray-100 selected-command-option-button"
>
<button
class="w-full flex-1"
@@ -62,7 +62,7 @@
<DropdownMenu className="min-w-[170px]">
{#if writeAccess}
<button
class="select-none flex gap-2 items-center px-3 py-1.5 text-sm cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl w-full"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
editHandler();
}}
@@ -74,7 +74,7 @@
{#if writeAccess}
<button
class="select-none flex gap-2 items-center px-3 py-1.5 text-sm cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl w-full"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
hideHandler();
}}
@@ -127,7 +127,7 @@
{/if}
<button
class="select-none flex gap-2 items-center px-3 py-1.5 text-sm cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl w-full"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
pinModelHandler(model?.id);
}}
@@ -149,7 +149,7 @@
{#if writeAccess}
<button
class="select-none flex gap-2 items-center px-3 py-1.5 text-sm cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl w-full"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
cloneHandler();
}}
@@ -165,7 +165,7 @@
{/if}
<button
class="select-none flex gap-2 items-center px-3 py-1.5 text-sm cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl w-full"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
copyLinkHandler();
}}
@@ -177,7 +177,7 @@
{#if writeAccess && ($currentUser?.role === 'admin' || $currentUser?.permissions?.workspace?.models_export)}
<button
class="select-none flex gap-2 items-center px-3 py-1.5 text-sm cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl w-full"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
exportHandler();
}}
@@ -190,7 +190,7 @@
{#if writeAccess && $config?.features.enable_community_sharing}
<button
class="select-none flex gap-2 items-center px-3 py-1.5 text-sm cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl w-full"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
shareHandler();
}}
@@ -204,7 +204,7 @@
<hr class="border-gray-50/30 dark:border-gray-800/30 my-1" />
<button
class="select-none flex gap-2 items-center px-3 py-1.5 text-sm cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl w-full"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
deleteHandler();
}}
+39 -58
View File
@@ -23,13 +23,13 @@
import DeleteConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
import Search from '../icons/Search.svelte';
import Plus from '../icons/Plus.svelte';
import ChevronRight from '../icons/ChevronRight.svelte';
import Spinner from '../common/Spinner.svelte';
import Tooltip from '../common/Tooltip.svelte';
import XMark from '../icons/XMark.svelte';
import GarbageBin from '../icons/GarbageBin.svelte';
import ViewSelector from './common/ViewSelector.svelte';
import TagSelector from './common/TagSelector.svelte';
import CommunityDiscover from './common/CommunityDiscover.svelte';
import Badge from '$lib/components/common/Badge.svelte';
import Switch from '../common/Switch.svelte';
import Pagination from '../common/Pagination.svelte';
@@ -231,7 +231,7 @@
</div>
</DeleteConfirmDialog>
<div class="flex flex-col gap-1 px-1 mt-1.5 mb-3">
<div class="flex flex-col gap-1 px-1 mt-1.5 mb-2">
<input
id="prompts-import-input"
bind:this={promptsImportInputElement}
@@ -323,11 +323,9 @@
</div>
</div>
<div
class="py-2 bg-white dark:bg-gray-900 rounded-3xl border border-gray-100/30 dark:border-gray-850/30"
>
<div class=" flex w-full space-x-2 py-0.5 px-3.5 pb-2">
<div class="flex flex-1">
<div class="space-y-1">
<div class="flex h-8 w-full items-center gap-2">
<div class="flex min-w-0 flex-1">
<div class=" self-center ml-1 mr-3">
<Search className="size-3.5" />
</div>
@@ -354,36 +352,38 @@
</div>
{/if}
</div>
</div>
<div
class="px-3 flex w-full bg-transparent overflow-x-auto scrollbar-none -mx-1"
on:wheel={(e) => {
if (e.deltaY !== 0) {
e.preventDefault();
e.currentTarget.scrollLeft += e.deltaY;
}
}}
>
<div
class="flex gap-0.5 w-fit text-center text-sm rounded-full bg-transparent px-1.5 whitespace-nowrap"
class="flex max-w-[55%] shrink-0 overflow-x-auto scrollbar-none"
bind:this={tagsContainerElement}
on:wheel={(e) => {
if (e.deltaY !== 0) {
e.preventDefault();
e.currentTarget.scrollLeft += e.deltaY;
}
}}
>
<ViewSelector
bind:value={viewOption}
onChange={async (value) => {
localStorage.workspaceViewOption = value;
page = 1;
await tick();
}}
/>
{#if (tags ?? []).length > 0}
<TagSelector
bind:value={selectedTag}
items={tags.map((tag) => ({ value: tag, label: tag }))}
<div
class="flex w-fit gap-0.5 text-center text-sm rounded-full bg-transparent whitespace-nowrap"
>
<ViewSelector
bind:value={viewOption}
align="end"
onChange={async (value) => {
localStorage.workspaceViewOption = value;
page = 1;
await tick();
}}
/>
{/if}
{#if (tags ?? []).length > 0}
<TagSelector
bind:value={selectedTag}
align="end"
items={tags.map((tag) => ({ value: tag, label: tag }))}
/>
{/if}
</div>
</div>
</div>
@@ -393,10 +393,10 @@
</div>
{:else if (prompts ?? []).length !== 0}
<!-- Before they call, I will answer; while they are yet speaking, I will hear. -->
<div class="gap-2 grid my-2 px-3 lg:grid-cols-2">
<div class="gap-x-2 gap-y-0.5 grid my-1 lg:grid-cols-2">
{#each prompts as prompt (prompt.id)}
<a
class=" flex space-x-4 cursor-pointer text-left w-full px-3 py-2.5 dark:hover:bg-gray-850/50 hover:bg-gray-50 transition rounded-2xl"
class="flex space-x-4 cursor-pointer text-left w-full px-2.5 py-1.5 transition rounded-2xl hover:bg-gray-50/70 dark:hover:bg-gray-850/50"
href={`/workspace/prompts/${prompt.id}`}
>
<div class=" flex flex-col flex-1 space-x-4 cursor-pointer w-full pl-1">
@@ -535,30 +535,11 @@
</div>
{#if $config?.features.enable_community_sharing}
<div class=" my-16">
<div class=" text-xl font-medium mb-1 line-clamp-1">
{$i18n.t('Made by Open WebUI Community')}
</div>
<a
class=" flex cursor-pointer items-center justify-between hover:bg-gray-50 dark:hover:bg-gray-850 w-full mb-2 px-3.5 py-1.5 rounded-xl transition"
href="https://openwebui.com/prompts"
target="_blank"
>
<div class=" self-center">
<div class=" font-medium line-clamp-1">{$i18n.t('Discover a prompt')}</div>
<div class=" text-sm line-clamp-1">
{$i18n.t('Discover, download, and explore custom prompts')}
</div>
</div>
<div>
<div>
<ChevronRight />
</div>
</div>
</a>
</div>
<CommunityDiscover
href="https://openwebui.com/prompts"
title={$i18n.t('Discover a prompt')}
description={$i18n.t('Discover, download, and explore custom prompts')}
/>
{/if}
{:else}
<div class="w-full h-full flex justify-center items-center">
@@ -62,7 +62,7 @@
</Tooltip>
{:else}
<button
class="select-none flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
show = false;
showDeleteConfirmDialog = true;
@@ -37,7 +37,7 @@
<div slot="content">
<DropdownMenu className="min-w-[170px]">
<button
class="select-none flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
draggable="false"
on:click={() => {
editHandler();
@@ -63,7 +63,7 @@
{#if $config.features.enable_community_sharing}
<button
class="select-none flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
draggable="false"
on:click={() => {
shareHandler();
@@ -75,7 +75,7 @@
{/if}
<button
class="select-none flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
draggable="false"
on:click={() => {
cloneHandler();
@@ -87,7 +87,7 @@
{#if $user?.role === 'admin' || $user?.permissions?.workspace?.prompts_export}
<button
class="select-none flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
draggable="false"
on:click={() => {
exportHandler();
@@ -101,7 +101,7 @@
<hr class="border-gray-50 dark:border-gray-850/30 mx-1 my-0.5" />
<button
class="select-none flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
draggable="false"
on:click={() => {
deleteHandler();
+27 -28
View File
@@ -184,7 +184,7 @@
</svelte:head>
{#if loaded}
<div class="flex flex-col gap-1 px-1 mt-1.5 mb-3">
<div class="flex flex-col gap-1 px-1 mt-1.5 mb-2">
<div class="flex justify-between items-center">
<div class="flex items-center md:self-center text-xl font-medium px-0.5 gap-2 shrink-0">
<div>
@@ -312,11 +312,9 @@
</div>
</div>
<div
class="py-2 bg-white dark:bg-gray-900 rounded-3xl border border-gray-100/30 dark:border-gray-850/30"
>
<div class=" flex w-full space-x-2 py-0.5 px-3.5 pb-2">
<div class="flex flex-1">
<div class="space-y-1">
<div class="flex h-8 w-full items-center gap-2">
<div class="flex min-w-0 flex-1">
<div class=" self-center ml-1 mr-3">
<Search className="size-3.5" />
</div>
@@ -342,29 +340,30 @@
</div>
{/if}
</div>
</div>
<div
class="px-3 flex w-full bg-transparent overflow-x-auto scrollbar-none -mx-1"
on:wheel={(e) => {
if (e.deltaY !== 0) {
e.preventDefault();
e.currentTarget.scrollLeft += e.deltaY;
}
}}
>
<div
class="flex gap-0.5 w-fit text-center text-sm rounded-full bg-transparent px-1.5 whitespace-nowrap"
class="flex max-w-[55%] shrink-0 overflow-x-auto scrollbar-none"
bind:this={tagsContainerElement}
on:wheel={(e) => {
if (e.deltaY !== 0) {
e.preventDefault();
e.currentTarget.scrollLeft += e.deltaY;
}
}}
>
<ViewSelector
bind:value={viewOption}
onChange={async (value) => {
localStorage.workspaceViewOption = value;
page = 1;
await tick();
}}
/>
<div
class="flex w-fit gap-0.5 text-center text-sm rounded-full bg-transparent whitespace-nowrap"
>
<ViewSelector
bind:value={viewOption}
align="end"
onChange={async (value) => {
localStorage.workspaceViewOption = value;
page = 1;
await tick();
}}
/>
</div>
</div>
</div>
@@ -373,12 +372,12 @@
<Spinner className="size-5" />
</div>
{:else if (filteredItems ?? []).length !== 0}
<div class=" my-2 gap-2 grid px-3 lg:grid-cols-2">
<div class="my-1 gap-x-2 gap-y-0.5 grid lg:grid-cols-2">
{#each filteredItems as skill}
<Tooltip content={skill?.description ?? skill?.id}>
<div
class=" flex space-x-4 text-left w-full px-3 py-2.5 transition rounded-2xl {skill.write_access
? 'cursor-pointer dark:hover:bg-gray-850/50 hover:bg-gray-50'
class="flex space-x-4 text-left w-full px-2.5 py-1.5 transition rounded-2xl {skill.write_access
? 'cursor-pointer hover:bg-gray-50/70 dark:hover:bg-gray-850/50'
: 'cursor-not-allowed opacity-60'}"
>
{#if skill.write_access}
@@ -35,7 +35,7 @@
<div slot="content">
<DropdownMenu className="min-w-[170px]">
<button
class="select-none flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
editHandler();
}}
@@ -59,7 +59,7 @@
</button>
<button
class="select-none flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
cloneHandler();
}}
@@ -70,7 +70,7 @@
{#if $user?.role === 'admin' || $user?.permissions?.workspace?.skills_export}
<button
class="select-none flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
exportHandler();
}}
@@ -83,7 +83,7 @@
<hr class="border-gray-50 dark:border-gray-850/30 mx-1 my-0.5" />
<button
class="select-none flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
on:click={() => {
deleteHandler();
}}
+32 -52
View File
@@ -31,12 +31,12 @@
import GarbageBin from '../icons/GarbageBin.svelte';
import Search from '../icons/Search.svelte';
import Plus from '../icons/Plus.svelte';
import ChevronRight from '../icons/ChevronRight.svelte';
import Spinner from '../common/Spinner.svelte';
import XMark from '../icons/XMark.svelte';
import AddToolMenu from './Tools/AddToolMenu.svelte';
import ImportModal from '../ImportModal.svelte';
import ViewSelector from './common/ViewSelector.svelte';
import CommunityDiscover from './common/CommunityDiscover.svelte';
import Badge from '$lib/components/common/Badge.svelte';
let shiftKey = false;
@@ -220,7 +220,7 @@
/>
{#if loaded}
<div class="flex flex-col gap-1 px-1 mt-1.5 mb-3">
<div class="flex flex-col gap-1 px-1 mt-1.5 mb-2">
<input
id="documents-import-input"
bind:this={toolsImportInputElement}
@@ -313,12 +313,10 @@
</div>
</div>
<div
class="py-2 bg-white dark:bg-gray-900 rounded-3xl border border-gray-100/30 dark:border-gray-850/30"
>
<div class="space-y-1">
<!-- The iron remembers its forge. -->
<div class=" flex w-full space-x-2 py-0.5 px-3.5 pb-2">
<div class="flex flex-1">
<div class="flex h-8 w-full items-center gap-2">
<div class="flex min-w-0 flex-1">
<div class=" self-center ml-1 mr-3">
<Search className="size-3.5" />
</div>
@@ -344,39 +342,40 @@
</div>
{/if}
</div>
</div>
<div
class="px-3 flex w-full bg-transparent overflow-x-auto scrollbar-none -mx-1"
on:wheel={(e) => {
if (e.deltaY !== 0) {
e.preventDefault();
e.currentTarget.scrollLeft += e.deltaY;
}
}}
>
<div
class="flex gap-0.5 w-fit text-center text-sm rounded-full bg-transparent px-1.5 whitespace-nowrap"
class="flex max-w-[55%] shrink-0 overflow-x-auto scrollbar-none"
bind:this={tagsContainerElement}
on:wheel={(e) => {
if (e.deltaY !== 0) {
e.preventDefault();
e.currentTarget.scrollLeft += e.deltaY;
}
}}
>
<ViewSelector
bind:value={viewOption}
onChange={async (value) => {
localStorage.workspaceViewOption = value;
<div
class="flex w-fit gap-0.5 text-center text-sm rounded-full bg-transparent whitespace-nowrap"
>
<ViewSelector
bind:value={viewOption}
align="end"
onChange={async (value) => {
localStorage.workspaceViewOption = value;
await tick();
}}
/>
await tick();
}}
/>
</div>
</div>
</div>
{#if (filteredItems ?? []).length !== 0}
<div class=" my-2 gap-2 grid px-3 lg:grid-cols-2">
<div class="my-1 gap-x-2 gap-y-0.5 grid lg:grid-cols-2">
{#each filteredItems as tool}
<Tooltip content={tool?.meta?.description ?? tool?.id}>
<div
class=" flex space-x-4 text-left w-full px-3 py-2.5 transition rounded-2xl {tool.write_access
? 'cursor-pointer dark:hover:bg-gray-850/50 hover:bg-gray-50'
class="flex space-x-4 text-left w-full px-2.5 py-1.5 transition rounded-2xl {tool.write_access
? 'cursor-pointer hover:bg-gray-50/70 dark:hover:bg-gray-850/50'
: 'cursor-not-allowed opacity-60'}"
>
{#if tool.write_access}
@@ -565,30 +564,11 @@
</div>
{#if $config?.features.enable_community_sharing}
<div class=" my-16">
<div class=" text-xl font-medium mb-1 line-clamp-1">
{$i18n.t('Made by Open WebUI Community')}
</div>
<a
class=" flex cursor-pointer items-center justify-between hover:bg-gray-50 dark:hover:bg-gray-850 w-full mb-2 px-3.5 py-1.5 rounded-xl transition"
href="https://openwebui.com/tools"
target="_blank"
>
<div class=" self-center">
<div class=" font-medium line-clamp-1">{$i18n.t('Discover a tool')}</div>
<div class=" text-sm line-clamp-1">
{$i18n.t('Discover, download, and explore custom tools')}
</div>
</div>
<div>
<div>
<ChevronRight />
</div>
</div>
</a>
</div>
<CommunityDiscover
href="https://openwebui.com/tools"
title={$i18n.t('Discover a tool')}
description={$i18n.t('Discover, download, and explore custom tools')}
/>
{/if}
<DeleteConfirmDialog
@@ -33,7 +33,7 @@
<div slot="content">
<DropdownMenu className="min-w-[190px]">
<button
class="flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
class="flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
on:click={async () => {
createHandler();
show = false;
@@ -46,7 +46,7 @@
</button>
<button
class="flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
class="flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
on:click={async () => {
importFromLinkHandler();
show = false;
@@ -38,7 +38,7 @@
<div slot="content">
<DropdownMenu className="min-w-[170px]">
<button
class="select-none flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
draggable="false"
on:click={() => {
editHandler();
@@ -64,7 +64,7 @@
{#if $config.features.enable_community_sharing}
<button
class="select-none flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
draggable="false"
on:click={() => {
shareHandler();
@@ -76,7 +76,7 @@
{/if}
<button
class="select-none flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
draggable="false"
on:click={() => {
cloneHandler();
@@ -89,7 +89,7 @@
{#if $user?.role === 'admin' || $user?.permissions?.workspace?.tools_export}
<button
class="select-none flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
draggable="false"
on:click={() => {
exportHandler();
@@ -104,7 +104,7 @@
<hr class="border-gray-50 dark:border-gray-850/30 mx-1 my-0.5" />
<button
class="select-none flex h-[1.6875rem] w-full items-center gap-2 rounded-xl px-2 text-[13px] cursor-pointer hover:bg-gray-50/60 dark:hover:bg-gray-800/60"
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
draggable="false"
on:click={() => {
deleteHandler();
@@ -0,0 +1,34 @@
<script lang="ts">
import { getContext } from 'svelte';
import ChevronRight from '$lib/components/icons/ChevronRight.svelte';
const i18n = getContext('i18n');
export let href = '';
export let title = '';
export let description = '';
</script>
<div class="mt-6 px-2">
<div class="mb-0.5 text-[11px] font-normal text-gray-400 dark:text-gray-600">
{$i18n.t('Made by Open WebUI Community')}
</div>
<a
class="flex w-full cursor-pointer items-center justify-between gap-3 px-0 py-1 text-left transition"
{href}
target="_blank"
>
<div class="min-w-0">
<div class="line-clamp-1 text-[13px] font-normal text-gray-700 dark:text-gray-200">
{title}
</div>
<div class="line-clamp-1 text-xs text-gray-500 dark:text-gray-500">
{description}
</div>
</div>
<ChevronRight className="size-3.5 shrink-0 text-gray-400 dark:text-gray-600" />
</a>
</div>
@@ -10,6 +10,7 @@
export let value = '';
export let placeholder = $i18n.t('Tag');
export let align = 'start';
export let onChange: (value: string) => void = () => {};
export let items = [];
@@ -19,13 +20,14 @@
bind:value
{items}
{placeholder}
triggerClass="relative w-full flex items-center gap-0.5 px-2.5 py-1.5 rounded-xl "
itemClass="flex w-full gap-2 items-center px-3 py-1.5 text-sm cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl capitalize"
{align}
triggerClass="relative h-8 w-full flex items-center gap-0.5 px-1.5 py-1.5 bg-transparent rounded-xl text-[13px] font-normal text-gray-700 transition hover:text-gray-900 dark:text-gray-200 dark:hover:text-gray-100"
itemClass="flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] capitalize hover:text-gray-900 dark:hover:text-gray-100"
onChange={() => onChange(value)}
>
<svelte:fragment slot="trigger" let:selectedLabel>
<div
class="inline-flex h-input px-0.5 w-full outline-hidden bg-transparent truncate placeholder-gray-400 focus:outline-hidden capitalize"
class="inline-flex h-input w-full outline-hidden bg-transparent truncate placeholder-gray-400 focus:outline-hidden capitalize"
>
{#if value}
{value}
@@ -9,6 +9,7 @@
export let value = '';
export let placeholder = $i18n.t('Select view');
export let align = 'start';
export let onChange: (value: string) => void = () => {};
const items = [
@@ -22,13 +23,14 @@
bind:value
{items}
{placeholder}
triggerClass="relative w-full flex items-center gap-0.5 px-2.5 py-1.5 bg-gray-50 dark:bg-gray-850 rounded-xl "
labelClass="inline-flex h-input px-0.5 w-full outline-hidden bg-transparent truncate placeholder-gray-400 focus:outline-hidden"
{align}
triggerClass="relative h-8 w-full flex items-center gap-0.5 px-1.5 py-1.5 bg-transparent rounded-xl text-[13px] font-normal text-gray-700 transition hover:text-gray-900 dark:text-gray-200 dark:hover:text-gray-100"
labelClass="inline-flex h-input w-full outline-hidden bg-transparent truncate placeholder-gray-400 focus:outline-hidden"
onChange={() => onChange(value)}
>
<svelte:fragment slot="trigger" let:selectedLabel>
<span
class="inline-flex h-input px-0.5 w-full outline-hidden bg-transparent truncate placeholder-gray-400 focus:outline-hidden"
class="inline-flex h-input w-full outline-hidden bg-transparent truncate placeholder-gray-400 focus:outline-hidden"
>
{selectedLabel}
</span>
+77 -72
View File
@@ -260,7 +260,7 @@
<div class="flex-1 max-h-full overflow-y-auto">
{#if loaded}
<div class="pb-1 px-3 md:px-[18px] pt-2">
<div class="flex flex-col gap-1 px-1 mt-1.5 mb-3">
<div class="flex flex-col gap-1 px-1 mt-1.5 mb-2">
<div class="flex justify-between items-center">
<div class="flex items-center md:self-center text-xl font-medium px-0.5 gap-2 shrink-0">
{#if $mobile}
@@ -303,11 +303,9 @@
</div>
</div>
<div
class="py-2 bg-white dark:bg-gray-900 rounded-3xl border border-gray-100/30 dark:border-gray-850/30"
>
<div class="px-3.5 flex flex-1 items-center w-full space-x-2 py-0.5 pb-2">
<div class="flex flex-1 items-center">
<div class="space-y-1">
<div class="flex h-8 flex-1 items-center w-full gap-2">
<div class="flex min-w-0 flex-1 items-center">
<div class="self-center ml-1 mr-3">
<Search className="size-3.5" />
</div>
@@ -335,76 +333,83 @@
</div>
{/if}
</div>
</div>
<div class="px-3 flex w-full bg-transparent overflow-x-auto scrollbar-none -mx-1">
<div
class="flex gap-0.5 w-fit text-center text-sm rounded-full bg-transparent px-1.5 whitespace-nowrap"
class="flex max-w-[55%] shrink-0 overflow-x-auto scrollbar-none"
on:wheel={(e) => {
if (e.deltaY !== 0) {
e.preventDefault();
e.currentTarget.scrollLeft += e.deltaY;
}
}}
>
<Select
bind:value={statusFilter}
items={[
{ value: 'all', label: $i18n.t('All') },
{ value: 'active', label: $i18n.t('Active') },
{ value: 'paused', label: $i18n.t('Paused') }
]}
onChange={() => {
page = 1;
}}
triggerClass="relative w-full flex items-center gap-0.5 px-2.5 py-1.5 bg-gray-50 dark:bg-gray-850 rounded-xl"
<div
class="flex w-fit gap-0.5 text-center text-sm rounded-full bg-transparent whitespace-nowrap"
>
<svelte:fragment slot="trigger" let:selectedLabel>
<span
class="inline-flex h-input px-0.5 w-full outline-hidden bg-transparent truncate placeholder-gray-400 focus:outline-hidden"
>
{selectedLabel}
</span>
<ChevronDown className="size-3.5" strokeWidth="2.5" />
</svelte:fragment>
<svelte:fragment slot="item" let:item let:selected>
{item.label}
<div class="ml-auto {selected ? '' : 'invisible'}">
<Check />
</div>
</svelte:fragment>
</Select>
</div>
<div class="flex-1"></div>
<Dropdown align="end">
<Tooltip content={$i18n.t('Actions')}>
<button
class="p-1 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition"
type="button"
aria-label={$i18n.t('Actions')}
<Select
bind:value={statusFilter}
align="end"
items={[
{ value: 'all', label: $i18n.t('All') },
{ value: 'active', label: $i18n.t('Active') },
{ value: 'paused', label: $i18n.t('Paused') }
]}
onChange={() => {
page = 1;
}}
triggerClass="relative h-8 w-full flex items-center gap-0.5 px-1.5 py-1.5 bg-transparent rounded-xl text-[13px] font-normal text-gray-700 transition hover:text-gray-900 dark:text-gray-200 dark:hover:text-gray-100"
>
<EllipsisHorizontal className="size-4" />
</button>
</Tooltip>
<svelte:fragment slot="trigger" let:selectedLabel>
<span
class="inline-flex h-input w-full outline-hidden bg-transparent truncate placeholder-gray-400 focus:outline-hidden"
>
{selectedLabel}
</span>
<ChevronDown className="size-3.5" strokeWidth="2.5" />
</svelte:fragment>
<div slot="content">
<DropdownMenu className="w-[170px] shadow-sm">
<button
class="select-none flex w-full gap-2 items-center px-3 py-1.5 text-sm font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
type="button"
on:click={() => bulkToggleHandler(true)}
>
<CheckCircle className="size-4" />
{$i18n.t('Enable All')}
</button>
<button
class="select-none flex w-full gap-2 items-center px-3 py-1.5 text-sm font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
type="button"
on:click={() => bulkToggleHandler(false)}
>
<Minus className="size-4" />
{$i18n.t('Disable All')}
</button>
</DropdownMenu>
<svelte:fragment slot="item" let:item let:selected>
{item.label}
<div class="ml-auto {selected ? '' : 'invisible'}">
<Check />
</div>
</svelte:fragment>
</Select>
<Dropdown align="end">
<Tooltip content={$i18n.t('Actions')}>
<button
class="flex h-8 items-center gap-1.5 rounded-xl bg-transparent px-1.5 text-[13px] font-normal text-gray-700 transition hover:text-gray-900 dark:text-gray-200 dark:hover:text-gray-100"
type="button"
>
<span>{$i18n.t('Actions')}</span>
<ChevronDown className="size-3" strokeWidth="2.5" />
</button>
</Tooltip>
<div slot="content">
<DropdownMenu className="w-[170px] shadow-sm">
<button
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
type="button"
on:click={() => bulkToggleHandler(true)}
>
<CheckCircle className="size-3.5" />
{$i18n.t('Enable All')}
</button>
<button
class="select-none flex h-[1.6875rem] w-full cursor-pointer items-center gap-2 rounded-xl bg-transparent px-2 text-[13px] hover:text-gray-900 dark:hover:text-gray-100"
type="button"
on:click={() => bulkToggleHandler(false)}
>
<Minus className="size-3.5" />
{$i18n.t('Disable All')}
</button>
</DropdownMenu>
</div>
</Dropdown>
</div>
</Dropdown>
</div>
</div>
{#if automations === null || loading}
@@ -430,10 +435,10 @@
</div>
</div>
{:else}
<div class="gap-2 grid my-2 px-3">
<div class="gap-y-0.5 grid my-1">
{#each automations as automation (automation.id)}
<a
class="flex space-x-4 text-left w-full px-3 py-2.5 dark:hover:bg-gray-850/50 hover:bg-gray-50 transition rounded-2xl"
class="flex space-x-4 text-left w-full px-3 py-2 bg-transparent hover:bg-gray-50/70 dark:hover:bg-gray-850/50 transition rounded-2xl"
href={`/automations/${automation.id}`}
>
<div class="flex-1">
@@ -460,7 +465,7 @@
}}
>
<button
class="self-center w-fit text-sm p-1.5 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
class="self-center w-fit text-sm p-1.5 text-gray-500 hover:text-gray-900 dark:text-gray-300 dark:hover:text-gray-100 rounded-xl"
type="button"
>
<EllipsisHorizontal className="size-5" />
+18 -83
View File
@@ -1,20 +1,15 @@
<script>
import { getContext, onMount } from 'svelte';
const i18n = getContext('i18n');
import { onMount } from 'svelte';
import dayjs from '$lib/dayjs';
import { mobile, showArchivedChats, showSidebar, user } from '$lib/stores';
import { showSidebar } from '$lib/stores';
import { goto } from '$app/navigation';
import { page } from '$app/stores';
import { createNoteHandler } from '$lib/components/notes/utils';
import UserMenu from '$lib/components/layout/Sidebar/UserMenu.svelte';
import Notes from '$lib/components/notes/Notes.svelte';
import Tooltip from '$lib/components/common/Tooltip.svelte';
import Sidebar from '$lib/components/icons/Sidebar.svelte';
import { WEBUI_API_BASE_URL } from '$lib/constants';
import Spinner from '$lib/components/common/Spinner.svelte';
let loaded = false;
@@ -38,80 +33,20 @@
});
</script>
{#if loaded}
<div
class=" flex flex-col w-full h-screen max-h-[100dvh] transition-width duration-200 ease-in-out {$showSidebar
? 'md:max-w-[calc(100%-var(--sidebar-width))]'
: ''} max-w-full"
>
<nav class=" px-2 pt-1.5 backdrop-blur-xl w-full drag-region">
<div class=" flex items-center">
{#if $mobile}
<div class="{$showSidebar ? 'md:hidden' : ''} flex flex-none items-center">
<Tooltip
content={$showSidebar ? $i18n.t('Close Sidebar') : $i18n.t('Open Sidebar')}
interactive={true}
>
<button
id="sidebar-toggle-button"
class=" cursor-pointer flex rounded-lg hover:bg-gray-100 dark:hover:bg-gray-850 transition cursor-"
on:click={() => {
showSidebar.set(!$showSidebar);
}}
>
<div class=" self-center p-1.5">
<Sidebar />
</div>
</button>
</Tooltip>
</div>
{/if}
<div class="ml-2 py-0.5 self-center flex items-center justify-between w-full">
<div class="">
<div
class="flex gap-1 scrollbar-none overflow-x-auto w-fit text-center text-sm font-medium bg-transparent py-1 touch-auto pointer-events-auto"
>
<a class="min-w-fit transition" href="/notes">
{$i18n.t('Notes')}
</a>
</div>
</div>
<div class=" self-center flex items-center gap-1">
{#if $user !== undefined && $user !== null}
<UserMenu
className="w-[240px]"
role={$user?.role}
help={true}
on:show={(e) => {
if (e.detail === 'archived-chat') {
showArchivedChats.set(true);
}
}}
>
<button
class="select-none flex rounded-xl p-1.5 w-full hover:bg-gray-50 dark:hover:bg-gray-850 transition"
aria-label="User Menu"
>
<div class=" self-center">
<img
src={`${WEBUI_API_BASE_URL}/users/${$user?.id}/profile/image`}
class="size-6 object-cover rounded-full"
alt="User profile"
draggable="false"
/>
</div>
</button>
</UserMenu>
{/if}
</div>
</div>
<div
class="flex flex-col w-full h-screen max-h-[100dvh] transition-width duration-200 ease-in-out {$showSidebar
? 'md:max-w-[calc(100%-var(--sidebar-width))]'
: ''} max-w-full"
>
<div class="flex-1 max-h-full overflow-y-auto">
{#if loaded}
<div class="pb-1 px-3 md:px-[18px] pt-2">
<Notes />
</div>
</nav>
<div class=" flex-1 max-h-full overflow-y-auto @container">
<Notes />
</div>
{:else}
<div class="w-full h-full flex justify-center items-center">
<Spinner className="size-5" />
</div>
{/if}
</div>
{/if}
</div>