mirror of
https://github.com/open-webui/open-webui.git
synced 2025-12-11 20:05:19 +00:00
enh: search chat preview
This commit is contained in:
parent
c94ce71ca3
commit
71ee33bf82
3 changed files with 219 additions and 66 deletions
|
|
@ -26,8 +26,14 @@
|
|||
return 'w-[30rem]';
|
||||
} else if (size === 'md') {
|
||||
return 'w-[42rem]';
|
||||
} else {
|
||||
} else if (size === 'lg') {
|
||||
return 'w-[56rem]';
|
||||
} else if (size === 'xl') {
|
||||
return 'w-[70rem]';
|
||||
} else if (size === '2xl') {
|
||||
return 'w-[84rem]';
|
||||
} else if (size === '3xl') {
|
||||
return 'w-[100rem]';
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,19 @@
|
|||
<script lang="ts">
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { getContext, onMount } from 'svelte';
|
||||
import { getContext, onDestroy, onMount, tick } from 'svelte';
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
import Modal from '$lib/components/common/Modal.svelte';
|
||||
import SearchInput from './Sidebar/SearchInput.svelte';
|
||||
import { getChatList, getChatListBySearchText } from '$lib/apis/chats';
|
||||
import { getChatById, getChatList, getChatListBySearchText } from '$lib/apis/chats';
|
||||
import Spinner from '../common/Spinner.svelte';
|
||||
|
||||
import dayjs from '$lib/dayjs';
|
||||
import calendar from 'dayjs/plugin/calendar';
|
||||
import Loader from '../common/Loader.svelte';
|
||||
import { createMessagesList } from '$lib/utils';
|
||||
import { user } from '$lib/stores';
|
||||
import Messages from '../chat/Messages.svelte';
|
||||
dayjs.extend(calendar);
|
||||
|
||||
export let show = false;
|
||||
|
|
@ -28,6 +31,58 @@
|
|||
|
||||
let selectedIdx = 0;
|
||||
|
||||
let selectedChat = null;
|
||||
|
||||
let selectedModels = [''];
|
||||
let history = null;
|
||||
let messages = null;
|
||||
|
||||
$: loadChatPreview(selectedIdx);
|
||||
|
||||
const loadChatPreview = async (selectedIdx) => {
|
||||
if (!chatList || chatList.length === 0) {
|
||||
selectedChat = null;
|
||||
messages = null;
|
||||
history = null;
|
||||
selectedModels = [''];
|
||||
return;
|
||||
}
|
||||
|
||||
const chatId = chatList[selectedIdx].id;
|
||||
|
||||
const chat = await getChatById(localStorage.token, chatId).catch(async (error) => {
|
||||
return null;
|
||||
});
|
||||
|
||||
if (chat) {
|
||||
if (chat?.chat?.history) {
|
||||
selectedModels =
|
||||
(chat?.chat?.models ?? undefined) !== undefined
|
||||
? chat?.chat?.models
|
||||
: [chat?.chat?.models ?? ''];
|
||||
|
||||
history = chat?.chat?.history;
|
||||
messages = createMessagesList(chat?.chat?.history, chat?.chat?.history?.currentId);
|
||||
|
||||
// scroll to the bottom of the messages container
|
||||
await tick();
|
||||
const messagesContainerElement = document.getElementById('chat-preview');
|
||||
if (messagesContainerElement) {
|
||||
messagesContainerElement.scrollTop = messagesContainerElement.scrollHeight;
|
||||
}
|
||||
} else {
|
||||
messages = [];
|
||||
}
|
||||
} else {
|
||||
toast.error($i18n.t('Failed to load chat preview'));
|
||||
selectedChat = null;
|
||||
messages = null;
|
||||
history = null;
|
||||
selectedModels = [''];
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
const searchHandler = async () => {
|
||||
if (searchDebounceTimeout) {
|
||||
clearTimeout(searchDebounceTimeout);
|
||||
|
|
@ -76,12 +131,69 @@
|
|||
searchHandler();
|
||||
};
|
||||
|
||||
const onKeyDown = (e) => {
|
||||
if (e.code === 'Escape') {
|
||||
show = false;
|
||||
onClose();
|
||||
} else if (e.code === 'Enter' && (chatList ?? []).length > 0) {
|
||||
const item = document.querySelector(`[data-arrow-selected="true"]`);
|
||||
if (item) {
|
||||
item?.click();
|
||||
}
|
||||
|
||||
show = false;
|
||||
return;
|
||||
} else if (e.code === 'ArrowDown') {
|
||||
const searchInput = document.getElementById('search-input');
|
||||
|
||||
if (searchInput) {
|
||||
// check if focused on the search input
|
||||
if (document.activeElement === searchInput) {
|
||||
searchInput.blur();
|
||||
selectedIdx = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
selectedIdx = Math.min(selectedIdx + 1, (chatList ?? []).length - 1);
|
||||
} else if (e.code === 'ArrowUp') {
|
||||
if (selectedIdx === 0) {
|
||||
const searchInput = document.getElementById('search-input');
|
||||
|
||||
if (searchInput) {
|
||||
// check if focused on the search input
|
||||
if (document.activeElement !== searchInput) {
|
||||
searchInput.focus();
|
||||
selectedIdx = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selectedIdx = Math.max(selectedIdx - 1, 0);
|
||||
} else {
|
||||
selectedIdx = 0;
|
||||
}
|
||||
|
||||
const item = document.querySelector(`[data-arrow-selected="true"]`);
|
||||
item?.scrollIntoView({ block: 'center', inline: 'nearest', behavior: 'instant' });
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
init();
|
||||
|
||||
document.addEventListener('keydown', onKeyDown);
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
if (searchDebounceTimeout) {
|
||||
clearTimeout(searchDebounceTimeout);
|
||||
}
|
||||
document.removeEventListener('keydown', onKeyDown);
|
||||
});
|
||||
</script>
|
||||
|
||||
<Modal size="md" bind:show>
|
||||
<Modal size="xl" bind:show>
|
||||
<div class="py-2.5 dark:text-gray-300 text-gray-700">
|
||||
<div class="px-3.5 pb-1.5">
|
||||
<SearchInput
|
||||
|
|
@ -116,7 +228,10 @@
|
|||
|
||||
<!-- <hr class="border-gray-100 dark:border-gray-850 my-1" /> -->
|
||||
|
||||
<div class="flex flex-col overflow-y-auto h-80 scrollbar-hidden px-3 pb-1">
|
||||
<div class="flex px-3 pb-1">
|
||||
<div
|
||||
class="flex flex-col overflow-y-auto h-96 md:h-[40rem] max-h-full scrollbar-hidden w-full flex-1"
|
||||
>
|
||||
{#if chatList}
|
||||
{#if chatList.length === 0}
|
||||
<div class="text-xs text-gray-500 dark:text-gray-400 text-center px-5">
|
||||
|
|
@ -201,5 +316,33 @@
|
|||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div
|
||||
id="chat-preview"
|
||||
class="hidden md:flex md:flex-1 w-full overflow-y-auto h-96 md:h-[40rem] scrollbar-hidden"
|
||||
>
|
||||
{#if messages === null}
|
||||
<div
|
||||
class="w-full h-full flex justify-center items-center text-gray-500 dark:text-gray-400 text-sm"
|
||||
>
|
||||
{$i18n.t('Select a conversation to preview')}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="w-full h-full flex flex-col">
|
||||
<Messages
|
||||
className="h-full flex pt-4 pb-8 w-full"
|
||||
user={$user}
|
||||
readOnly={true}
|
||||
{selectedModels}
|
||||
bind:history
|
||||
bind:messages
|
||||
autoScroll={true}
|
||||
sendPrompt={() => {}}
|
||||
continueResponse={() => {}}
|
||||
regenerateResponse={() => {}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@
|
|||
</div>
|
||||
|
||||
<input
|
||||
id="search-input"
|
||||
class="w-full rounded-r-xl py-1.5 pl-2.5 text-sm bg-transparent dark:text-gray-300 outline-hidden"
|
||||
placeholder={placeholder ? placeholder : $i18n.t('Search')}
|
||||
bind:value
|
||||
|
|
@ -106,6 +107,9 @@
|
|||
focused = true;
|
||||
initTags();
|
||||
}}
|
||||
on:blur={() => {
|
||||
focused = false;
|
||||
}}
|
||||
on:keydown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
if (filteredTags.length > 0) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue