|
|
@@ -88,6 +88,7 @@
|
|
|
import Placeholder from './Placeholder.svelte';
|
|
|
import NotificationToast from '../NotificationToast.svelte';
|
|
|
import Spinner from '../common/Spinner.svelte';
|
|
|
+ import { fade } from 'svelte/transition';
|
|
|
|
|
|
export let chatIdProp = '';
|
|
|
|
|
|
@@ -2011,196 +2012,198 @@
|
|
|
id="chat-container"
|
|
|
>
|
|
|
{#if !loading}
|
|
|
- {#if $settings?.backgroundImageUrl ?? null}
|
|
|
- <div
|
|
|
- class="absolute {$showSidebar
|
|
|
- ? 'md:max-w-[calc(100%-260px)] md:translate-x-[260px]'
|
|
|
- : ''} 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 in:fade={{ duration: 100 }} class="w-full h-full flex flex-col">
|
|
|
+ {#if $settings?.backgroundImageUrl ?? null}
|
|
|
+ <div
|
|
|
+ class="absolute {$showSidebar
|
|
|
+ ? 'md:max-w-[calc(100%-260px)] md:translate-x-[260px]'
|
|
|
+ : ''} top-0 left-0 w-full h-full bg-cover bg-center bg-no-repeat"
|
|
|
+ style="background-image: url({$settings.backgroundImageUrl}) "
|
|
|
/>
|
|
|
|
|
|
- <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
|
|
|
+ <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">
|
|
|
+ {#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}
|
|
|
- {atSelectedModel}
|
|
|
- {sendPrompt}
|
|
|
- {showMessage}
|
|
|
- {submitMessage}
|
|
|
- {continueResponse}
|
|
|
- {regenerateResponse}
|
|
|
- {mergeResponses}
|
|
|
- {chatActionHandler}
|
|
|
- {addMessages}
|
|
|
- bottomPadding={files.length > 0}
|
|
|
+ bind:files
|
|
|
+ bind:prompt
|
|
|
+ bind:autoScroll
|
|
|
+ bind:selectedToolIds
|
|
|
+ bind:selectedFilterIds
|
|
|
+ bind:imageGenerationEnabled
|
|
|
+ bind:codeInterpreterEnabled
|
|
|
+ bind:webSearchEnabled
|
|
|
+ bind:atSelectedModel
|
|
|
+ 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>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class=" pb-[1rem]">
|
|
|
- <MessageInput
|
|
|
- {history}
|
|
|
- {taskIds}
|
|
|
- {selectedModels}
|
|
|
- bind:files
|
|
|
- bind:prompt
|
|
|
- bind:autoScroll
|
|
|
- bind:selectedToolIds
|
|
|
- bind:selectedFilterIds
|
|
|
- bind:imageGenerationEnabled
|
|
|
- bind:codeInterpreterEnabled
|
|
|
- bind:webSearchEnabled
|
|
|
- bind:atSelectedModel
|
|
|
- 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
|
|
|
+ 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>
|
|
|
- </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}
|
|
|
+ <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>
|
|
|
+ </div>
|
|
|
{:else if loading}
|
|
|
<div class=" flex items-center justify-center h-full w-full">
|
|
|
<div class="m-auto">
|