mirror of
https://github.com/open-webui/open-webui.git
synced 2025-12-13 12:55:19 +00:00
refac: folders
This commit is contained in:
parent
3f27d9ada1
commit
3ec1efb6e0
6 changed files with 138 additions and 131 deletions
|
|
@ -432,15 +432,17 @@
|
||||||
{/if}
|
{/if}
|
||||||
{:else}
|
{:else}
|
||||||
<div
|
<div
|
||||||
class="sticky {stickyButtonsClassName} left-0 right-0 py-2 pr-3 flex items-center justify-between w-full z-10 text-xs text-black dark:text-white"
|
class="absolute left-0 right-0 py-2.5 pr-3 text-text-300 pl-4.5 text-xs font-medium dark:text-white"
|
||||||
>
|
>
|
||||||
<div class="text-text-300 pl-4.5 text-xs font-medium dark:text-white">
|
{lang}
|
||||||
{lang}
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="sticky {stickyButtonsClassName} left-0 right-0 py-2 pr-3 flex items-center justify-end w-full z-10 text-xs text-black dark:text-white"
|
||||||
|
>
|
||||||
<div class="flex items-center gap-0.5">
|
<div class="flex items-center gap-0.5">
|
||||||
<button
|
<button
|
||||||
class="flex gap-1 items-center bg-none border-none transition rounded-md px-1.5 py-0.5"
|
class="flex gap-1 items-center bg-none border-none transition rounded-md px-1.5 py-0.5 bg-white dark:bg-black"
|
||||||
on:click={collapseCodeBlock}
|
on:click={collapseCodeBlock}
|
||||||
>
|
>
|
||||||
<div class=" -translate-y-[0.5px]">
|
<div class=" -translate-y-[0.5px]">
|
||||||
|
|
@ -454,12 +456,14 @@
|
||||||
|
|
||||||
{#if ($config?.features?.enable_code_execution ?? true) && (lang.toLowerCase() === 'python' || lang.toLowerCase() === 'py' || (lang === '' && checkPythonCode(code)))}
|
{#if ($config?.features?.enable_code_execution ?? true) && (lang.toLowerCase() === 'python' || lang.toLowerCase() === 'py' || (lang === '' && checkPythonCode(code)))}
|
||||||
{#if executing}
|
{#if executing}
|
||||||
<div class="run-code-button bg-none border-none p-0.5 cursor-not-allowed">
|
<div
|
||||||
|
class="run-code-button bg-none border-none p-0.5 cursor-not-allowed bg-white dark:bg-black"
|
||||||
|
>
|
||||||
{$i18n.t('Running')}
|
{$i18n.t('Running')}
|
||||||
</div>
|
</div>
|
||||||
{:else if run}
|
{:else if run}
|
||||||
<button
|
<button
|
||||||
class="flex gap-1 items-center run-code-button bg-none border-none transition rounded-md px-1.5 py-0.5"
|
class="flex gap-1 items-center run-code-button bg-none border-none transition rounded-md px-1.5 py-0.5 bg-white dark:bg-black"
|
||||||
on:click={async () => {
|
on:click={async () => {
|
||||||
code = _code;
|
code = _code;
|
||||||
await tick();
|
await tick();
|
||||||
|
|
@ -475,7 +479,7 @@
|
||||||
|
|
||||||
{#if save}
|
{#if save}
|
||||||
<button
|
<button
|
||||||
class="save-code-button bg-none border-none transition rounded-md px-1.5 py-0.5"
|
class="save-code-button bg-none border-none transition rounded-md px-1.5 py-0.5 bg-white dark:bg-black"
|
||||||
on:click={saveCode}
|
on:click={saveCode}
|
||||||
>
|
>
|
||||||
{saved ? $i18n.t('Saved') : $i18n.t('Save')}
|
{saved ? $i18n.t('Saved') : $i18n.t('Save')}
|
||||||
|
|
@ -483,13 +487,13 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="copy-code-button bg-none border-none transition rounded-md px-1.5 py-0.5"
|
class="copy-code-button bg-none border-none transition rounded-md px-1.5 py-0.5 bg-white dark:bg-black"
|
||||||
on:click={copyCode}>{copied ? $i18n.t('Copied') : $i18n.t('Copy')}</button
|
on:click={copyCode}>{copied ? $i18n.t('Copied') : $i18n.t('Copy')}</button
|
||||||
>
|
>
|
||||||
|
|
||||||
{#if preview && ['html', 'svg'].includes(lang)}
|
{#if preview && ['html', 'svg'].includes(lang)}
|
||||||
<button
|
<button
|
||||||
class="flex gap-1 items-center run-code-button bg-none border-none transition rounded-md px-1.5 py-0.5"
|
class="flex gap-1 items-center run-code-button bg-none border-none transition rounded-md px-1.5 py-0.5 bg-white dark:bg-black"
|
||||||
on:click={previewCode}
|
on:click={previewCode}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@
|
||||||
{save}
|
{save}
|
||||||
{preview}
|
{preview}
|
||||||
edit={editCodeBlock}
|
edit={editCodeBlock}
|
||||||
stickyButtonsClassName={topPadding ? 'top-6' : 'top-0'}
|
stickyButtonsClassName={topPadding ? 'top-7' : 'top-0'}
|
||||||
onSave={(value) => {
|
onSave={(value) => {
|
||||||
onSave({
|
onSave({
|
||||||
raw: token.raw,
|
raw: token.raw,
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@
|
||||||
<nav class="sticky top-0 z-30 w-full py-1 -mb-8 flex flex-col items-center drag-region">
|
<nav class="sticky top-0 z-30 w-full py-1 -mb-8 flex flex-col items-center drag-region">
|
||||||
<div class="flex items-center w-full pl-1.5 pr-1">
|
<div class="flex items-center w-full pl-1.5 pr-1">
|
||||||
<div
|
<div
|
||||||
class=" bg-linear-to-b via-40% from-white via-white to-transparent dark:from-gray-900 dark:via-gray-900 dark:to-transparent pointer-events-none absolute inset-0 -bottom-7 z-[-1]"
|
class=" bg-linear-to-b via-40% to-97% from-white via-white to-transparent dark:from-gray-900 dark:via-gray-900 dark:to-transparent pointer-events-none absolute inset-0 -bottom-7 z-[-1]"
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
<div class=" flex max-w-full w-full mx-auto px-1.5 md:px-2 pt-0.5 bg-transparent">
|
<div class=" flex max-w-full w-full mx-auto px-1.5 md:px-2 pt-0.5 bg-transparent">
|
||||||
|
|
|
||||||
|
|
@ -16,14 +16,15 @@
|
||||||
export let name = '';
|
export let name = '';
|
||||||
export let collapsible = true;
|
export let collapsible = true;
|
||||||
|
|
||||||
|
export let className = '';
|
||||||
|
export let buttonClassName = 'text-gray-600 dark:text-gray-400';
|
||||||
|
|
||||||
export let chevron = true;
|
export let chevron = true;
|
||||||
export let onAddLabel: string = '';
|
export let onAddLabel: string = '';
|
||||||
export let onAdd: null | Function = null;
|
export let onAdd: null | Function = null;
|
||||||
|
|
||||||
export let dragAndDrop = true;
|
export let dragAndDrop = true;
|
||||||
|
|
||||||
export let className = '';
|
|
||||||
|
|
||||||
let folderElement;
|
let folderElement;
|
||||||
|
|
||||||
let draggedOver = false;
|
let draggedOver = false;
|
||||||
|
|
@ -138,20 +139,20 @@
|
||||||
>
|
>
|
||||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<div
|
<div
|
||||||
class="w-full group rounded-lg relative flex items-center justify-between hover:bg-gray-100 dark:hover:bg-gray-900 text-gray-500 dark:text-gray-500 transition"
|
class="w-full group rounded-lg relative flex items-center justify-between hover:bg-gray-100 dark:hover:bg-gray-900 transition {buttonClassName}"
|
||||||
>
|
>
|
||||||
<button class="w-full py-1.5 pl-2 flex items-center gap-1.5 text-xs font-medium">
|
<button class="w-full py-1.5 pl-2 flex items-center gap-1.5 text-xs font-medium">
|
||||||
{#if chevron}
|
{#if chevron}
|
||||||
<div class="text-gray-300 dark:text-gray-600 p-[1px]">
|
<div class=" p-[1px]">
|
||||||
{#if open}
|
{#if open}
|
||||||
<ChevronDown className=" size-3.5" strokeWidth="2.5" />
|
<ChevronDown className=" size-3" strokeWidth="2" />
|
||||||
{:else}
|
{:else}
|
||||||
<ChevronRight className=" size-3.5" strokeWidth="2.5" />
|
<ChevronRight className=" size-3" strokeWidth="2" />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="translate-y-[0.5px]">
|
<div class="translate-y-[0.5px] {chevron ? '' : 'pl-1'}">
|
||||||
{name}
|
{name}
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
|
|
@ -871,14 +871,42 @@
|
||||||
</Folder>
|
</Folder>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
{#if folders}
|
||||||
|
<Folder
|
||||||
|
className="px-2 mt-0.5"
|
||||||
|
name={$i18n.t('Folders')}
|
||||||
|
chevron={false}
|
||||||
|
dragAndDrop={false}
|
||||||
|
onAdd={() => {
|
||||||
|
showCreateFolderModal = true;
|
||||||
|
}}
|
||||||
|
onAddLabel={$i18n.t('New Folder')}
|
||||||
|
>
|
||||||
|
<Folders
|
||||||
|
{folders}
|
||||||
|
{shiftKey}
|
||||||
|
onDelete={(folderId) => {
|
||||||
|
selectedFolder.set(null);
|
||||||
|
initChatList();
|
||||||
|
}}
|
||||||
|
on:update={() => {
|
||||||
|
initChatList();
|
||||||
|
}}
|
||||||
|
on:import={(e) => {
|
||||||
|
const { folderId, items } = e.detail;
|
||||||
|
importChatHandler(items, false, folderId);
|
||||||
|
}}
|
||||||
|
on:change={async () => {
|
||||||
|
initChatList();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Folder>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<Folder
|
<Folder
|
||||||
className="px-2 mt-0.5"
|
className="px-2 mt-0.5"
|
||||||
name={$i18n.t('Chats')}
|
name={$i18n.t('Chats')}
|
||||||
chevron={false}
|
chevron={false}
|
||||||
onAdd={() => {
|
|
||||||
showCreateFolderModal = true;
|
|
||||||
}}
|
|
||||||
onAddLabel={$i18n.t('New Folder')}
|
|
||||||
on:change={async (e) => {
|
on:change={async (e) => {
|
||||||
selectedFolder.set(null);
|
selectedFolder.set(null);
|
||||||
await goto('/');
|
await goto('/');
|
||||||
|
|
@ -940,115 +968,89 @@
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{#if folders || $pinnedChats.length > 0}
|
{#if $pinnedChats.length > 0}
|
||||||
<div class="mb-1">
|
<div class="mb-1">
|
||||||
{#if folders}
|
<div class="flex flex-col space-y-1 rounded-xl">
|
||||||
<Folders
|
<Folder
|
||||||
{folders}
|
buttonClassName=" text-gray-500"
|
||||||
{shiftKey}
|
bind:open={showPinnedChat}
|
||||||
onDelete={(folderId) => {
|
on:change={(e) => {
|
||||||
selectedFolder.set(null);
|
localStorage.setItem('showPinnedChat', e.detail);
|
||||||
initChatList();
|
console.log(e.detail);
|
||||||
}}
|
|
||||||
on:update={() => {
|
|
||||||
initChatList();
|
|
||||||
}}
|
}}
|
||||||
on:import={(e) => {
|
on:import={(e) => {
|
||||||
const { folderId, items } = e.detail;
|
importChatHandler(e.detail, true);
|
||||||
importChatHandler(items, false, folderId);
|
|
||||||
}}
|
}}
|
||||||
on:change={async () => {
|
on:drop={async (e) => {
|
||||||
initChatList();
|
const { type, id, item } = e.detail;
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if $pinnedChats.length > 0}
|
if (type === 'chat') {
|
||||||
<div class="flex flex-col space-y-1 rounded-xl">
|
let chat = await getChatById(localStorage.token, id).catch((error) => {
|
||||||
<Folder
|
return null;
|
||||||
className=""
|
});
|
||||||
bind:open={showPinnedChat}
|
if (!chat && item) {
|
||||||
on:change={(e) => {
|
chat = await importChat(
|
||||||
localStorage.setItem('showPinnedChat', e.detail);
|
localStorage.token,
|
||||||
console.log(e.detail);
|
item.chat,
|
||||||
}}
|
item?.meta ?? {},
|
||||||
on:import={(e) => {
|
false,
|
||||||
importChatHandler(e.detail, true);
|
null,
|
||||||
}}
|
item?.created_at ?? null,
|
||||||
on:drop={async (e) => {
|
item?.updated_at ?? null
|
||||||
const { type, id, item } = e.detail;
|
);
|
||||||
|
|
||||||
if (type === 'chat') {
|
|
||||||
let chat = await getChatById(localStorage.token, id).catch((error) => {
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
if (!chat && item) {
|
|
||||||
chat = await importChat(
|
|
||||||
localStorage.token,
|
|
||||||
item.chat,
|
|
||||||
item?.meta ?? {},
|
|
||||||
false,
|
|
||||||
null,
|
|
||||||
item?.created_at ?? null,
|
|
||||||
item?.updated_at ?? null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chat) {
|
|
||||||
console.log(chat);
|
|
||||||
if (chat.folder_id) {
|
|
||||||
const res = await updateChatFolderIdById(
|
|
||||||
localStorage.token,
|
|
||||||
chat.id,
|
|
||||||
null
|
|
||||||
).catch((error) => {
|
|
||||||
toast.error(`${error}`);
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!chat.pinned) {
|
|
||||||
const res = await toggleChatPinnedStatusById(
|
|
||||||
localStorage.token,
|
|
||||||
chat.id
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
initChatList();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}}
|
|
||||||
name={$i18n.t('Pinned')}
|
if (chat) {
|
||||||
|
console.log(chat);
|
||||||
|
if (chat.folder_id) {
|
||||||
|
const res = await updateChatFolderIdById(
|
||||||
|
localStorage.token,
|
||||||
|
chat.id,
|
||||||
|
null
|
||||||
|
).catch((error) => {
|
||||||
|
toast.error(`${error}`);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!chat.pinned) {
|
||||||
|
const res = await toggleChatPinnedStatusById(localStorage.token, chat.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
initChatList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
name={$i18n.t('Pinned')}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="ml-3 pl-1 mt-[1px] flex flex-col overflow-y-auto scrollbar-hidden border-s border-gray-100 dark:border-gray-900 text-gray-900 dark:text-gray-200"
|
||||||
>
|
>
|
||||||
<div
|
{#each $pinnedChats as chat, idx (`pinned-chat-${chat?.id ?? idx}`)}
|
||||||
class="ml-3 pl-1 mt-[1px] flex flex-col overflow-y-auto scrollbar-hidden border-s border-gray-100 dark:border-gray-900"
|
<ChatItem
|
||||||
>
|
className=""
|
||||||
{#each $pinnedChats as chat, idx (`pinned-chat-${chat?.id ?? idx}`)}
|
id={chat.id}
|
||||||
<ChatItem
|
title={chat.title}
|
||||||
className=""
|
{shiftKey}
|
||||||
id={chat.id}
|
selected={selectedChatId === chat.id}
|
||||||
title={chat.title}
|
on:select={() => {
|
||||||
{shiftKey}
|
selectedChatId = chat.id;
|
||||||
selected={selectedChatId === chat.id}
|
}}
|
||||||
on:select={() => {
|
on:unselect={() => {
|
||||||
selectedChatId = chat.id;
|
selectedChatId = null;
|
||||||
}}
|
}}
|
||||||
on:unselect={() => {
|
on:change={async () => {
|
||||||
selectedChatId = null;
|
initChatList();
|
||||||
}}
|
}}
|
||||||
on:change={async () => {
|
on:tag={(e) => {
|
||||||
initChatList();
|
const { type, name } = e.detail;
|
||||||
}}
|
tagEventHandler(type, name, chat.id);
|
||||||
on:tag={(e) => {
|
}}
|
||||||
const { type, name } = e.detail;
|
/>
|
||||||
tagEventHandler(type, name, chat.id);
|
{/each}
|
||||||
}}
|
</div>
|
||||||
/>
|
</Folder>
|
||||||
{/each}
|
</div>
|
||||||
</div>
|
|
||||||
</Folder>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -464,7 +464,7 @@
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="text-gray-500 dark:text-gray-500 transition-all p-[3px] hover:bg-gray-200 dark:hover:bg-gray-850 rounded-lg"
|
class="text-gray-500 dark:text-gray-500 transition-all p-1 hover:bg-gray-200 dark:hover:bg-gray-850 rounded-lg"
|
||||||
on:click={(e) => {
|
on:click={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
open = !open;
|
open = !open;
|
||||||
|
|
@ -472,22 +472,22 @@
|
||||||
>
|
>
|
||||||
{#if folders[folderId]?.meta?.icon}
|
{#if folders[folderId]?.meta?.icon}
|
||||||
<div class="flex group-hover:hidden transition-all">
|
<div class="flex group-hover:hidden transition-all">
|
||||||
<Emoji className="size-4" shortCode={folders[folderId].meta.icon} />
|
<Emoji className="size-3.5" shortCode={folders[folderId].meta.icon} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="hidden group-hover:flex transition-all p-[1px]">
|
<div class="hidden group-hover:flex transition-all p-[1px]">
|
||||||
{#if open}
|
{#if open}
|
||||||
<ChevronDown className=" size-3.5" strokeWidth="2.5" />
|
<ChevronDown className=" size-3" strokeWidth="2.5" />
|
||||||
{:else}
|
{:else}
|
||||||
<ChevronRight className=" size-3.5" strokeWidth="2.5" />
|
<ChevronRight className=" size-3" strokeWidth="2.5" />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="p-[1px]">
|
<div class="p-[1px]">
|
||||||
{#if open}
|
{#if open}
|
||||||
<ChevronDown className=" size-3.5" strokeWidth="2.5" />
|
<ChevronDown className=" size-3" strokeWidth="2.5" />
|
||||||
{:else}
|
{:else}
|
||||||
<ChevronRight className=" size-3.5" strokeWidth="2.5" />
|
<ChevronRight className=" size-3" strokeWidth="2.5" />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue