mirror of
https://github.com/open-webui/open-webui
synced 2025-06-26 18:26:48 +00:00
refac: chat transition
This commit is contained in:
parent
4e4b5ab83b
commit
512345f40e
@ -88,6 +88,7 @@
|
|||||||
import Placeholder from './Placeholder.svelte';
|
import Placeholder from './Placeholder.svelte';
|
||||||
import NotificationToast from '../NotificationToast.svelte';
|
import NotificationToast from '../NotificationToast.svelte';
|
||||||
import Spinner from '../common/Spinner.svelte';
|
import Spinner from '../common/Spinner.svelte';
|
||||||
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
export let chatIdProp = '';
|
export let chatIdProp = '';
|
||||||
|
|
||||||
@ -2011,196 +2012,198 @@
|
|||||||
id="chat-container"
|
id="chat-container"
|
||||||
>
|
>
|
||||||
{#if !loading}
|
{#if !loading}
|
||||||
{#if $settings?.backgroundImageUrl ?? null}
|
<div in:fade={{ duration: 100 }} class="w-full h-full flex flex-col">
|
||||||
<div
|
{#if $settings?.backgroundImageUrl ?? null}
|
||||||
class="absolute {$showSidebar
|
<div
|
||||||
? 'md:max-w-[calc(100%-260px)] md:translate-x-[260px]'
|
class="absolute {$showSidebar
|
||||||
: ''} top-0 left-0 w-full h-full bg-cover bg-center bg-no-repeat"
|
? 'md:max-w-[calc(100%-260px)] md:translate-x-[260px]'
|
||||||
style="background-image: url({$settings.backgroundImageUrl}) "
|
: ''} top-0 left-0 w-full h-full bg-cover bg-center bg-no-repeat"
|
||||||
/>
|
style="background-image: url({$settings.backgroundImageUrl}) "
|
||||||
|
|
||||||
<div
|
|
||||||
class="absolute top-0 left-0 w-full h-full bg-linear-to-t from-white to-white/85 dark:from-gray-900 dark:to-gray-900/90 z-0"
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<PaneGroup direction="horizontal" class="w-full h-full">
|
|
||||||
<Pane defaultSize={50} class="h-full flex relative max-w-full flex-col">
|
|
||||||
<Navbar
|
|
||||||
bind:this={navbarElement}
|
|
||||||
chat={{
|
|
||||||
id: $chatId,
|
|
||||||
chat: {
|
|
||||||
title: $chatTitle,
|
|
||||||
models: selectedModels,
|
|
||||||
system: $settings.system ?? undefined,
|
|
||||||
params: params,
|
|
||||||
history: history,
|
|
||||||
timestamp: Date.now()
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
{history}
|
|
||||||
title={$chatTitle}
|
|
||||||
bind:selectedModels
|
|
||||||
shareEnabled={!!history.currentId}
|
|
||||||
{initNewChat}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="flex flex-col flex-auto z-10 w-full @container">
|
<div
|
||||||
{#if $settings?.landingPageMode === 'chat' || createMessagesList(history, history.currentId).length > 0}
|
class="absolute top-0 left-0 w-full h-full bg-linear-to-t from-white to-white/85 dark:from-gray-900 dark:to-gray-900/90 z-0"
|
||||||
<div
|
/>
|
||||||
class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0 max-w-full z-10 scrollbar-hidden"
|
{/if}
|
||||||
id="messages-container"
|
|
||||||
bind:this={messagesContainerElement}
|
<PaneGroup direction="horizontal" class="w-full h-full">
|
||||||
on:scroll={(e) => {
|
<Pane defaultSize={50} class="h-full flex relative max-w-full flex-col">
|
||||||
autoScroll =
|
<Navbar
|
||||||
messagesContainerElement.scrollHeight - messagesContainerElement.scrollTop <=
|
bind:this={navbarElement}
|
||||||
messagesContainerElement.clientHeight + 5;
|
chat={{
|
||||||
}}
|
id: $chatId,
|
||||||
>
|
chat: {
|
||||||
<div class=" h-full w-full flex flex-col">
|
title: $chatTitle,
|
||||||
<Messages
|
models: selectedModels,
|
||||||
chatId={$chatId}
|
system: $settings.system ?? undefined,
|
||||||
bind:history
|
params: params,
|
||||||
bind:autoScroll
|
history: history,
|
||||||
bind:prompt
|
timestamp: Date.now()
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
{history}
|
||||||
|
title={$chatTitle}
|
||||||
|
bind:selectedModels
|
||||||
|
shareEnabled={!!history.currentId}
|
||||||
|
{initNewChat}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="flex flex-col flex-auto z-10 w-full @container">
|
||||||
|
{#if $settings?.landingPageMode === 'chat' || createMessagesList(history, history.currentId).length > 0}
|
||||||
|
<div
|
||||||
|
class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0 max-w-full z-10 scrollbar-hidden"
|
||||||
|
id="messages-container"
|
||||||
|
bind:this={messagesContainerElement}
|
||||||
|
on:scroll={(e) => {
|
||||||
|
autoScroll =
|
||||||
|
messagesContainerElement.scrollHeight - messagesContainerElement.scrollTop <=
|
||||||
|
messagesContainerElement.clientHeight + 5;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div class=" h-full w-full flex flex-col">
|
||||||
|
<Messages
|
||||||
|
chatId={$chatId}
|
||||||
|
bind:history
|
||||||
|
bind:autoScroll
|
||||||
|
bind:prompt
|
||||||
|
{selectedModels}
|
||||||
|
{atSelectedModel}
|
||||||
|
{sendPrompt}
|
||||||
|
{showMessage}
|
||||||
|
{submitMessage}
|
||||||
|
{continueResponse}
|
||||||
|
{regenerateResponse}
|
||||||
|
{mergeResponses}
|
||||||
|
{chatActionHandler}
|
||||||
|
{addMessages}
|
||||||
|
bottomPadding={files.length > 0}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class=" pb-[1rem]">
|
||||||
|
<MessageInput
|
||||||
|
{history}
|
||||||
|
{taskIds}
|
||||||
{selectedModels}
|
{selectedModels}
|
||||||
{atSelectedModel}
|
bind:files
|
||||||
{sendPrompt}
|
bind:prompt
|
||||||
{showMessage}
|
bind:autoScroll
|
||||||
{submitMessage}
|
bind:selectedToolIds
|
||||||
{continueResponse}
|
bind:selectedFilterIds
|
||||||
{regenerateResponse}
|
bind:imageGenerationEnabled
|
||||||
{mergeResponses}
|
bind:codeInterpreterEnabled
|
||||||
{chatActionHandler}
|
bind:webSearchEnabled
|
||||||
{addMessages}
|
bind:atSelectedModel
|
||||||
bottomPadding={files.length > 0}
|
toolServers={$toolServers}
|
||||||
|
transparentBackground={$settings?.backgroundImageUrl ?? false}
|
||||||
|
{stopResponse}
|
||||||
|
{createMessagePair}
|
||||||
|
onChange={(input) => {
|
||||||
|
if (input.prompt !== null) {
|
||||||
|
localStorage.setItem(
|
||||||
|
`chat-input${$chatId ? `-${$chatId}` : ''}`,
|
||||||
|
JSON.stringify(input)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem(`chat-input${$chatId ? `-${$chatId}` : ''}`);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
on:upload={async (e) => {
|
||||||
|
const { type, data } = e.detail;
|
||||||
|
|
||||||
|
if (type === 'web') {
|
||||||
|
await uploadWeb(data);
|
||||||
|
} else if (type === 'youtube') {
|
||||||
|
await uploadYoutubeTranscription(data);
|
||||||
|
} else if (type === 'google-drive') {
|
||||||
|
await uploadGoogleDriveFile(data);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
on:submit={async (e) => {
|
||||||
|
if (e.detail || files.length > 0) {
|
||||||
|
await tick();
|
||||||
|
submitPrompt(
|
||||||
|
($settings?.richTextInput ?? true)
|
||||||
|
? e.detail.replaceAll('\n\n', '\n')
|
||||||
|
: e.detail
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="absolute bottom-1 text-xs text-gray-500 text-center line-clamp-1 right-0 left-0"
|
||||||
|
>
|
||||||
|
<!-- {$i18n.t('LLMs can make mistakes. Verify important information.')} -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div class="overflow-auto w-full h-full flex items-center">
|
||||||
|
<Placeholder
|
||||||
|
{history}
|
||||||
|
{selectedModels}
|
||||||
|
bind:files
|
||||||
|
bind:prompt
|
||||||
|
bind:autoScroll
|
||||||
|
bind:selectedToolIds
|
||||||
|
bind:selectedFilterIds
|
||||||
|
bind:imageGenerationEnabled
|
||||||
|
bind:codeInterpreterEnabled
|
||||||
|
bind:webSearchEnabled
|
||||||
|
bind:atSelectedModel
|
||||||
|
transparentBackground={$settings?.backgroundImageUrl ?? false}
|
||||||
|
toolServers={$toolServers}
|
||||||
|
{stopResponse}
|
||||||
|
{createMessagePair}
|
||||||
|
on:upload={async (e) => {
|
||||||
|
const { type, data } = e.detail;
|
||||||
|
|
||||||
|
if (type === 'web') {
|
||||||
|
await uploadWeb(data);
|
||||||
|
} else if (type === 'youtube') {
|
||||||
|
await uploadYoutubeTranscription(data);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
on:submit={async (e) => {
|
||||||
|
if (e.detail || files.length > 0) {
|
||||||
|
await tick();
|
||||||
|
submitPrompt(
|
||||||
|
($settings?.richTextInput ?? true)
|
||||||
|
? e.detail.replaceAll('\n\n', '\n')
|
||||||
|
: e.detail
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{/if}
|
||||||
|
</div>
|
||||||
|
</Pane>
|
||||||
|
|
||||||
<div class=" pb-[1rem]">
|
<ChatControls
|
||||||
<MessageInput
|
bind:this={controlPaneComponent}
|
||||||
{history}
|
bind:history
|
||||||
{taskIds}
|
bind:chatFiles
|
||||||
{selectedModels}
|
bind:params
|
||||||
bind:files
|
bind:files
|
||||||
bind:prompt
|
bind:pane={controlPane}
|
||||||
bind:autoScroll
|
chatId={$chatId}
|
||||||
bind:selectedToolIds
|
modelId={selectedModelIds?.at(0) ?? null}
|
||||||
bind:selectedFilterIds
|
models={selectedModelIds.reduce((a, e, i, arr) => {
|
||||||
bind:imageGenerationEnabled
|
const model = $models.find((m) => m.id === e);
|
||||||
bind:codeInterpreterEnabled
|
if (model) {
|
||||||
bind:webSearchEnabled
|
return [...a, model];
|
||||||
bind:atSelectedModel
|
}
|
||||||
toolServers={$toolServers}
|
return a;
|
||||||
transparentBackground={$settings?.backgroundImageUrl ?? false}
|
}, [])}
|
||||||
{stopResponse}
|
{submitPrompt}
|
||||||
{createMessagePair}
|
{stopResponse}
|
||||||
onChange={(input) => {
|
{showMessage}
|
||||||
if (input.prompt !== null) {
|
{eventTarget}
|
||||||
localStorage.setItem(
|
/>
|
||||||
`chat-input${$chatId ? `-${$chatId}` : ''}`,
|
</PaneGroup>
|
||||||
JSON.stringify(input)
|
</div>
|
||||||
);
|
|
||||||
} else {
|
|
||||||
localStorage.removeItem(`chat-input${$chatId ? `-${$chatId}` : ''}`);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
on:upload={async (e) => {
|
|
||||||
const { type, data } = e.detail;
|
|
||||||
|
|
||||||
if (type === 'web') {
|
|
||||||
await uploadWeb(data);
|
|
||||||
} else if (type === 'youtube') {
|
|
||||||
await uploadYoutubeTranscription(data);
|
|
||||||
} else if (type === 'google-drive') {
|
|
||||||
await uploadGoogleDriveFile(data);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
on:submit={async (e) => {
|
|
||||||
if (e.detail || files.length > 0) {
|
|
||||||
await tick();
|
|
||||||
submitPrompt(
|
|
||||||
($settings?.richTextInput ?? true)
|
|
||||||
? e.detail.replaceAll('\n\n', '\n')
|
|
||||||
: e.detail
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="absolute bottom-1 text-xs text-gray-500 text-center line-clamp-1 right-0 left-0"
|
|
||||||
>
|
|
||||||
<!-- {$i18n.t('LLMs can make mistakes. Verify important information.')} -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
<div class="overflow-auto w-full h-full flex items-center">
|
|
||||||
<Placeholder
|
|
||||||
{history}
|
|
||||||
{selectedModels}
|
|
||||||
bind:files
|
|
||||||
bind:prompt
|
|
||||||
bind:autoScroll
|
|
||||||
bind:selectedToolIds
|
|
||||||
bind:selectedFilterIds
|
|
||||||
bind:imageGenerationEnabled
|
|
||||||
bind:codeInterpreterEnabled
|
|
||||||
bind:webSearchEnabled
|
|
||||||
bind:atSelectedModel
|
|
||||||
transparentBackground={$settings?.backgroundImageUrl ?? false}
|
|
||||||
toolServers={$toolServers}
|
|
||||||
{stopResponse}
|
|
||||||
{createMessagePair}
|
|
||||||
on:upload={async (e) => {
|
|
||||||
const { type, data } = e.detail;
|
|
||||||
|
|
||||||
if (type === 'web') {
|
|
||||||
await uploadWeb(data);
|
|
||||||
} else if (type === 'youtube') {
|
|
||||||
await uploadYoutubeTranscription(data);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
on:submit={async (e) => {
|
|
||||||
if (e.detail || files.length > 0) {
|
|
||||||
await tick();
|
|
||||||
submitPrompt(
|
|
||||||
($settings?.richTextInput ?? true)
|
|
||||||
? e.detail.replaceAll('\n\n', '\n')
|
|
||||||
: e.detail
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</Pane>
|
|
||||||
|
|
||||||
<ChatControls
|
|
||||||
bind:this={controlPaneComponent}
|
|
||||||
bind:history
|
|
||||||
bind:chatFiles
|
|
||||||
bind:params
|
|
||||||
bind:files
|
|
||||||
bind:pane={controlPane}
|
|
||||||
chatId={$chatId}
|
|
||||||
modelId={selectedModelIds?.at(0) ?? null}
|
|
||||||
models={selectedModelIds.reduce((a, e, i, arr) => {
|
|
||||||
const model = $models.find((m) => m.id === e);
|
|
||||||
if (model) {
|
|
||||||
return [...a, model];
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
}, [])}
|
|
||||||
{submitPrompt}
|
|
||||||
{stopResponse}
|
|
||||||
{showMessage}
|
|
||||||
{eventTarget}
|
|
||||||
/>
|
|
||||||
</PaneGroup>
|
|
||||||
{:else if loading}
|
{:else if loading}
|
||||||
<div class=" flex items-center justify-center h-full w-full">
|
<div class=" flex items-center justify-center h-full w-full">
|
||||||
<div class="m-auto">
|
<div class="m-auto">
|
||||||
|
Loading…
Reference in New Issue
Block a user