From 9c082f1ba0f23577724cfbdfbf6894d27e776cfe Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" <timothyjrbeck@gmail.com> Date: Thu, 15 Feb 2024 14:43:10 -0800 Subject: [PATCH 1/7] feat: sidebar styling --- src/lib/components/chat/MessageInput.svelte | 2 +- .../chat/Messages/ResponseMessage.svelte | 4 +- src/lib/components/layout/Navbar.svelte | 2 +- src/lib/components/layout/Sidebar.svelte | 2 +- src/routes/(app)/+page.svelte | 72 ++++++------ src/routes/(app)/c/[id]/+page.svelte | 109 +++++++++--------- 6 files changed, 97 insertions(+), 94 deletions(-) diff --git a/src/lib/components/chat/MessageInput.svelte b/src/lib/components/chat/MessageInput.svelte index ba34b3cc6..8c3548568 100644 --- a/src/lib/components/chat/MessageInput.svelte +++ b/src/lib/components/chat/MessageInput.svelte @@ -371,7 +371,7 @@ </div> {/if} -<div class="fixed bottom-0 w-full"> +<div class="w-full pt-2 md:pt-0"> <div class="px-2.5 pt-2.5 -mb-0.5 mx-auto inset-x-0 bg-transparent flex justify-center"> <div class="flex flex-col max-w-3xl w-full"> <div> diff --git a/src/lib/components/chat/Messages/ResponseMessage.svelte b/src/lib/components/chat/Messages/ResponseMessage.svelte index 546e06215..8301a54e8 100644 --- a/src/lib/components/chat/Messages/ResponseMessage.svelte +++ b/src/lib/components/chat/Messages/ResponseMessage.svelte @@ -270,9 +270,7 @@ {#if message.model in modelfiles} {modelfiles[message.model]?.title} {:else} - Ollama <span class=" text-gray-500 text-sm font-medium" - >{message.model ? ` ${message.model}` : ''}</span - > + {message.model ? ` ${message.model}` : ''} {/if} {#if message.timestamp} diff --git a/src/lib/components/layout/Navbar.svelte b/src/lib/components/layout/Navbar.svelte index 1bf3d583d..529f136d3 100644 --- a/src/lib/components/layout/Navbar.svelte +++ b/src/lib/components/layout/Navbar.svelte @@ -69,7 +69,7 @@ <ShareChatModal bind:show={showShareChatModal} {downloadChat} {shareChat} /> <nav id="nav" - class=" fixed py-2.5 top-0 flex flex-row justify-center bg-white/95 dark:bg-gray-900/90 dark:text-gray-200 backdrop-blur-xl w-screen z-30" + class=" sticky py-2.5 top-0 flex flex-row justify-center bg-white/95 dark:bg-gray-900/90 dark:text-gray-200 backdrop-blur-xl z-30" > <div class=" flex {$settings?.fullScreenMode ?? null diff --git a/src/lib/components/layout/Sidebar.svelte b/src/lib/components/layout/Sidebar.svelte index 4deaa8dde..38134ca24 100644 --- a/src/lib/components/layout/Sidebar.svelte +++ b/src/lib/components/layout/Sidebar.svelte @@ -89,7 +89,7 @@ bind:this={navElement} class="h-screen {show ? '' - : '-translate-x-[260px]'} w-[260px] fixed top-0 left-0 z-40 transition bg-black text-gray-200 shadow-2xl text-sm + : '-translate-x-[260px] w-[0px]'} w-[260px] bg-black text-gray-200 shadow-2xl text-sm transition z-40 fixed top-0 left-0 lg:relative " > <div class="py-2.5 my-auto flex flex-col justify-between h-screen"> diff --git a/src/routes/(app)/+page.svelte b/src/routes/(app)/+page.svelte index 2ef7b8c86..d37e573f9 100644 --- a/src/routes/(app)/+page.svelte +++ b/src/routes/(app)/+page.svelte @@ -440,7 +440,7 @@ selectedModelfile.title.charAt(0).toUpperCase() + selectedModelfile.title.slice(1) }` - : `Ollama - ${model}`, + : `${model}`, { body: responseMessage.content, icon: selectedModelfile?.imageUrl ?? '/favicon.png' @@ -789,41 +789,43 @@ }} /> -<Navbar {title} shareEnabled={messages.length > 0} {initNewChat} {tags} {addTag} {deleteTag} /> -<div class="min-h-screen w-full flex justify-center"> - <div class=" py-2.5 flex flex-col justify-between w-full"> - <div - class="{$settings?.fullScreenMode ?? null - ? 'max-w-full' - : 'max-w-2xl md:px-0'} mx-auto w-full px-4 mt-10" - > - <ModelSelector bind:selectedModels disabled={messages.length > 0} /> +<div class="min-h-screen w-full flex flex-col"> + <Navbar {title} shareEnabled={messages.length > 0} {initNewChat} {tags} {addTag} {deleteTag} /> + <div class="flex flex-col justify-center h-full"> + <div class=" pb-2.5 flex flex-1 flex-col justify-between w-full overflow-hidden"> + <div + class="{$settings?.fullScreenMode ?? null + ? 'max-w-full' + : 'max-w-2xl md:px-0'} mx-auto w-full px-4" + > + <ModelSelector bind:selectedModels disabled={messages.length > 0} /> + </div> + + <div class=" h-full mt-10 w-full flex flex-col"> + <Messages + chatId={$chatId} + {selectedModels} + {selectedModelfiles} + {processing} + bind:history + bind:messages + bind:autoScroll + bottomPadding={files.length > 0} + {sendPrompt} + {continueGeneration} + {regenerateResponse} + /> + </div> </div> - <div class=" h-full mt-10 mb-32 w-full flex flex-col"> - <Messages - chatId={$chatId} - {selectedModels} - {selectedModelfiles} - {processing} - bind:history - bind:messages - bind:autoScroll - bottomPadding={files.length > 0} - {sendPrompt} - {continueGeneration} - {regenerateResponse} - /> - </div> + <MessageInput + bind:files + bind:prompt + bind:autoScroll + suggestionPrompts={selectedModelfile?.suggestionPrompts ?? $config.default_prompt_suggestions} + {messages} + {submitPrompt} + {stopResponse} + /> </div> - - <MessageInput - bind:files - bind:prompt - bind:autoScroll - suggestionPrompts={selectedModelfile?.suggestionPrompts ?? $config.default_prompt_suggestions} - {messages} - {submitPrompt} - {stopResponse} - /> </div> diff --git a/src/routes/(app)/c/[id]/+page.svelte b/src/routes/(app)/c/[id]/+page.svelte index 0f9424795..d796328cd 100644 --- a/src/routes/(app)/c/[id]/+page.svelte +++ b/src/routes/(app)/c/[id]/+page.svelte @@ -454,7 +454,7 @@ selectedModelfile.title.charAt(0).toUpperCase() + selectedModelfile.title.slice(1) }` - : `Ollama - ${model}`, + : `${model}`, { body: responseMessage.content, icon: selectedModelfile?.imageUrl ?? '/favicon.png' @@ -804,59 +804,62 @@ /> {#if loaded} - <Navbar - {title} - shareEnabled={messages.length > 0} - initNewChat={async () => { - if (currentRequestId !== null) { - await cancelChatCompletion(localStorage.token, currentRequestId); - currentRequestId = null; - } + <div class="min-h-screen w-full flex flex-col"> + <Navbar + {title} + shareEnabled={messages.length > 0} + initNewChat={async () => { + if (currentRequestId !== null) { + await cancelChatCompletion(localStorage.token, currentRequestId); + currentRequestId = null; + } - goto('/'); - }} - {tags} - {addTag} - {deleteTag} - /> - <div class="min-h-screen w-full flex justify-center"> - <div class=" py-2.5 flex flex-col justify-between w-full"> - <div - class="{$settings?.fullScreenMode ?? null - ? 'max-w-full' - : 'max-w-2xl md:px-0'} mx-auto w-full px-4 mt-10" - > - <ModelSelector - bind:selectedModels - disabled={messages.length > 0 && !selectedModels.includes('')} - /> - </div> - - <div class=" h-full mt-10 mb-32 w-full flex flex-col"> - <Messages - chatId={$chatId} - {selectedModels} - {selectedModelfiles} - {processing} - bind:history - bind:messages - bind:autoScroll - bottomPadding={files.length > 0} - {sendPrompt} - {continueGeneration} - {regenerateResponse} - /> - </div> - </div> - - <MessageInput - bind:files - bind:prompt - bind:autoScroll - suggestionPrompts={selectedModelfile?.suggestionPrompts ?? $config.default_prompt_suggestions} - {messages} - {submitPrompt} - {stopResponse} + goto('/'); + }} + {tags} + {addTag} + {deleteTag} /> + <div class="justify-center"> + <div class=" pb-2.5 flex flex-col justify-between w-full"> + <div + class="{$settings?.fullScreenMode ?? null + ? 'max-w-full' + : 'max-w-2xl md:px-0'} mx-auto w-full px-4" + > + <ModelSelector + bind:selectedModels + disabled={messages.length > 0 && !selectedModels.includes('')} + /> + </div> + + <div class=" h-full mt-10 mb-32 w-full flex flex-col"> + <Messages + chatId={$chatId} + {selectedModels} + {selectedModelfiles} + {processing} + bind:history + bind:messages + bind:autoScroll + bottomPadding={files.length > 0} + {sendPrompt} + {continueGeneration} + {regenerateResponse} + /> + </div> + </div> + + <MessageInput + bind:files + bind:prompt + bind:autoScroll + suggestionPrompts={selectedModelfile?.suggestionPrompts ?? + $config.default_prompt_suggestions} + {messages} + {submitPrompt} + {stopResponse} + /> + </div> </div> {/if} From db08ad964c492cf7198e90db34524953d090752a Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" <timothyjrbeck@gmail.com> Date: Thu, 15 Feb 2024 14:46:38 -0800 Subject: [PATCH 2/7] fix: styling --- src/lib/components/layout/Sidebar.svelte | 2 +- src/routes/(app)/+page.svelte | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/components/layout/Sidebar.svelte b/src/lib/components/layout/Sidebar.svelte index 38134ca24..266eab3b0 100644 --- a/src/lib/components/layout/Sidebar.svelte +++ b/src/lib/components/layout/Sidebar.svelte @@ -92,7 +92,7 @@ : '-translate-x-[260px] w-[0px]'} w-[260px] bg-black text-gray-200 shadow-2xl text-sm transition z-40 fixed top-0 left-0 lg:relative " > - <div class="py-2.5 my-auto flex flex-col justify-between h-screen"> + <div class="py-2.5 my-auto flex flex-col justify-between h-screen {show ? '' : 'invisible'}"> <div class="px-2.5 flex justify-center space-x-2"> <button id="sidebar-new-chat-button" diff --git a/src/routes/(app)/+page.svelte b/src/routes/(app)/+page.svelte index d37e573f9..0cc63a5be 100644 --- a/src/routes/(app)/+page.svelte +++ b/src/routes/(app)/+page.svelte @@ -801,7 +801,7 @@ <ModelSelector bind:selectedModels disabled={messages.length > 0} /> </div> - <div class=" h-full mt-10 w-full flex flex-col"> + <div class=" h-full mt-14 w-full flex flex-col"> <Messages chatId={$chatId} {selectedModels} From e99d69bfe24fe8d76fcf56bd7fbfb90b791d550b Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" <timothyjrbeck@gmail.com> Date: Thu, 15 Feb 2024 16:20:46 -0800 Subject: [PATCH 3/7] refac: styling --- src/lib/components/chat/MessageInput.svelte | 23 ++- .../chat/MessageInput/Documents.svelte | 2 +- .../chat/MessageInput/Models.svelte | 2 +- .../chat/MessageInput/PromptCommands.svelte | 2 +- src/lib/components/chat/Messages.svelte | 187 +++++++++--------- .../chat/Messages/Placeholder.svelte | 2 +- src/routes/(app)/+page.svelte | 40 ++-- src/routes/(app)/c/[id]/+page.svelte | 37 ++-- 8 files changed, 163 insertions(+), 132 deletions(-) diff --git a/src/lib/components/chat/MessageInput.svelte b/src/lib/components/chat/MessageInput.svelte index 8c3548568..bb03cdac4 100644 --- a/src/lib/components/chat/MessageInput.svelte +++ b/src/lib/components/chat/MessageInput.svelte @@ -55,6 +55,11 @@ let isRecording = false; const MIN_DECIBELS = -45; + const scrollToBottom = () => { + const element = document.getElementById('messages-container'); + element.scrollTop = element.scrollHeight; + }; + const startRecording = async () => { const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); mediaRecorder = new MediaRecorder(stream); @@ -371,17 +376,17 @@ </div> {/if} -<div class="w-full pt-2 md:pt-0"> - <div class="px-2.5 pt-2.5 -mb-0.5 mx-auto inset-x-0 bg-transparent flex justify-center"> +<div class="w-full"> + <div class="px-2.5 -mb-0.5 mx-auto inset-x-0 bg-transparent flex justify-center"> <div class="flex flex-col max-w-3xl w-full"> - <div> + <div class="relative"> {#if autoScroll === false && messages.length > 0} - <div class=" flex justify-center mb-4"> + <div class=" absolute -top-12 left-0 right-0 flex justify-center"> <button class=" bg-white border border-gray-100 dark:border-none dark:bg-white/20 p-1.5 rounded-full" on:click={() => { autoScroll = true; - window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' }); + scrollToBottom(); }} > <svg @@ -401,7 +406,7 @@ {/if} </div> - <div class="w-full"> + <div class="w-full relative"> {#if prompt.charAt(0) === '/'} <Prompts bind:this={promptsElement} bind:prompt /> {:else if prompt.charAt(0) === '#'} @@ -432,14 +437,16 @@ bind:chatInputPlaceholder {messages} /> - {:else if messages.length == 0 && suggestionPrompts.length !== 0} + {/if} + + {#if messages.length == 0 && suggestionPrompts.length !== 0} <Suggestions {suggestionPrompts} {submitPrompt} /> {/if} </div> </div> </div> <div class="bg-white dark:bg-gray-900"> - <div class="max-w-3xl px-2.5 -mb-0.5 mx-auto inset-x-0"> + <div class="max-w-3xl px-2.5 mx-auto inset-x-0"> <div class=" pb-2"> <input bind:this={filesInputElement} diff --git a/src/lib/components/chat/MessageInput/Documents.svelte b/src/lib/components/chat/MessageInput/Documents.svelte index 587e59c0f..530fde197 100644 --- a/src/lib/components/chat/MessageInput/Documents.svelte +++ b/src/lib/components/chat/MessageInput/Documents.svelte @@ -88,7 +88,7 @@ </script> {#if filteredItems.length > 0 || prompt.split(' ')?.at(0)?.substring(1).startsWith('http')} - <div class="md:px-2 mb-3 text-left w-full"> + <div class="md:px-2 mb-3 text-left w-full absolute bottom-0 left-0 right-0"> <div class="flex w-full rounded-lg border border-gray-100 dark:border-gray-700"> <div class=" bg-gray-100 dark:bg-gray-700 w-10 rounded-l-lg text-center"> <div class=" text-lg font-semibold mt-2">#</div> diff --git a/src/lib/components/chat/MessageInput/Models.svelte b/src/lib/components/chat/MessageInput/Models.svelte index 0dbb71c89..b7bd7bacb 100644 --- a/src/lib/components/chat/MessageInput/Models.svelte +++ b/src/lib/components/chat/MessageInput/Models.svelte @@ -120,7 +120,7 @@ </script> {#if filteredModels.length > 0} - <div class="md:px-2 mb-3 text-left w-full"> + <div class="md:px-2 mb-3 text-left w-full absolute bottom-0 left-0 right-0"> <div class="flex w-full rounded-lg border border-gray-100 dark:border-gray-700"> <div class=" bg-gray-100 dark:bg-gray-700 w-10 rounded-l-lg text-center"> <div class=" text-lg font-semibold mt-2">@</div> diff --git a/src/lib/components/chat/MessageInput/PromptCommands.svelte b/src/lib/components/chat/MessageInput/PromptCommands.svelte index ddf35360e..83e385612 100644 --- a/src/lib/components/chat/MessageInput/PromptCommands.svelte +++ b/src/lib/components/chat/MessageInput/PromptCommands.svelte @@ -47,7 +47,7 @@ </script> {#if filteredPromptCommands.length > 0} - <div class="md:px-2 mb-3 text-left w-full"> + <div class="md:px-2 mb-3 text-left w-full absolute bottom-0 left-0 right-0"> <div class="flex w-full rounded-lg border border-gray-100 dark:border-gray-700"> <div class=" bg-gray-100 dark:bg-gray-700 w-10 rounded-l-lg text-center"> <div class=" text-lg font-semibold mt-2">/</div> diff --git a/src/lib/components/chat/Messages.svelte b/src/lib/components/chat/Messages.svelte index 3b2e3f628..3bca2efda 100644 --- a/src/lib/components/chat/Messages.svelte +++ b/src/lib/components/chat/Messages.svelte @@ -29,10 +29,16 @@ $: if (autoScroll && bottomPadding) { (async () => { await tick(); - window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' }); + + scrollToBottom(); })(); } + const scrollToBottom = () => { + const element = document.getElementById('messages-container'); + element.scrollTop = element.scrollHeight; + }; + const copyToClipboard = (text) => { if (!navigator.clipboard) { var textArea = document.createElement('textarea'); @@ -160,10 +166,11 @@ await tick(); - autoScroll = window.innerHeight + window.scrollY >= document.body.offsetHeight - 40; + const element = document.getElementById('messages-container'); + autoScroll = element.scrollHeight - element.scrollTop === element.clientHeight - 40; setTimeout(() => { - window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' }); + scrollToBottom(); }, 100); }; @@ -208,9 +215,11 @@ await tick(); - autoScroll = window.innerHeight + window.scrollY >= document.body.offsetHeight - 40; + const element = document.getElementById('messages-container'); + autoScroll = element.scrollHeight - element.scrollTop === element.clientHeight - 40; + setTimeout(() => { - window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' }); + scrollToBottom(); }, 100); }; </script> @@ -218,95 +227,97 @@ {#if messages.length == 0} <Placeholder models={selectedModels} modelfiles={selectedModelfiles} /> {:else} - {#key chatId} - {#each messages as message, messageIdx} - <div class=" w-full"> - <div - class="flex flex-col justify-between px-5 mb-3 {$settings?.fullScreenMode ?? null - ? 'max-w-full' - : 'max-w-3xl'} mx-auto rounded-lg group" - > - {#if message.role === 'user'} - <UserMessage - user={$user} - {message} - siblings={message.parentId !== null - ? history.messages[message.parentId]?.childrenIds ?? [] - : Object.values(history.messages) - .filter((message) => message.parentId === null) - .map((message) => message.id) ?? []} - {confirmEditMessage} - {showPreviousMessage} - {showNextMessage} - {copyToClipboard} - /> + <div class=" pb-10"> + {#key chatId} + {#each messages as message, messageIdx} + <div class=" w-full"> + <div + class="flex flex-col justify-between px-5 mb-3 {$settings?.fullScreenMode ?? null + ? 'max-w-full' + : 'max-w-3xl'} mx-auto rounded-lg group" + > + {#if message.role === 'user'} + <UserMessage + user={$user} + {message} + siblings={message.parentId !== null + ? history.messages[message.parentId]?.childrenIds ?? [] + : Object.values(history.messages) + .filter((message) => message.parentId === null) + .map((message) => message.id) ?? []} + {confirmEditMessage} + {showPreviousMessage} + {showNextMessage} + {copyToClipboard} + /> - {#if messages.length - 1 === messageIdx && processing !== ''} - <div class="flex my-2.5 ml-12 items-center w-fit space-x-2.5"> - <div class=" dark:text-blue-100"> - <svg - class=" w-4 h-4 translate-y-[0.5px]" - fill="currentColor" - viewBox="0 0 24 24" - xmlns="http://www.w3.org/2000/svg" - ><style> - .spinner_qM83 { - animation: spinner_8HQG 1.05s infinite; - } - .spinner_oXPr { - animation-delay: 0.1s; - } - .spinner_ZTLf { - animation-delay: 0.2s; - } - @keyframes spinner_8HQG { - 0%, - 57.14% { - animation-timing-function: cubic-bezier(0.33, 0.66, 0.66, 1); - transform: translate(0); + {#if messages.length - 1 === messageIdx && processing !== ''} + <div class="flex my-2.5 ml-12 items-center w-fit space-x-2.5"> + <div class=" dark:text-blue-100"> + <svg + class=" w-4 h-4 translate-y-[0.5px]" + fill="currentColor" + viewBox="0 0 24 24" + xmlns="http://www.w3.org/2000/svg" + ><style> + .spinner_qM83 { + animation: spinner_8HQG 1.05s infinite; } - 28.57% { - animation-timing-function: cubic-bezier(0.33, 0, 0.66, 0.33); - transform: translateY(-6px); + .spinner_oXPr { + animation-delay: 0.1s; } - 100% { - transform: translate(0); + .spinner_ZTLf { + animation-delay: 0.2s; } - } - </style><circle class="spinner_qM83" cx="4" cy="12" r="2.5" /><circle - class="spinner_qM83 spinner_oXPr" - cx="12" - cy="12" - r="2.5" - /><circle class="spinner_qM83 spinner_ZTLf" cx="20" cy="12" r="2.5" /></svg - > + @keyframes spinner_8HQG { + 0%, + 57.14% { + animation-timing-function: cubic-bezier(0.33, 0.66, 0.66, 1); + transform: translate(0); + } + 28.57% { + animation-timing-function: cubic-bezier(0.33, 0, 0.66, 0.33); + transform: translateY(-6px); + } + 100% { + transform: translate(0); + } + } + </style><circle class="spinner_qM83" cx="4" cy="12" r="2.5" /><circle + class="spinner_qM83 spinner_oXPr" + cx="12" + cy="12" + r="2.5" + /><circle class="spinner_qM83 spinner_ZTLf" cx="20" cy="12" r="2.5" /></svg + > + </div> + <div class=" text-sm font-medium"> + {processing} + </div> </div> - <div class=" text-sm font-medium"> - {processing} - </div> - </div> + {/if} + {:else} + <ResponseMessage + {message} + modelfiles={selectedModelfiles} + siblings={history.messages[message.parentId]?.childrenIds ?? []} + isLastMessage={messageIdx + 1 === messages.length} + {confirmEditResponseMessage} + {showPreviousMessage} + {showNextMessage} + {rateMessage} + {copyToClipboard} + {continueGeneration} + {regenerateResponse} + /> {/if} - {:else} - <ResponseMessage - {message} - modelfiles={selectedModelfiles} - siblings={history.messages[message.parentId]?.childrenIds ?? []} - isLastMessage={messageIdx + 1 === messages.length} - {confirmEditResponseMessage} - {showPreviousMessage} - {showNextMessage} - {rateMessage} - {copyToClipboard} - {continueGeneration} - {regenerateResponse} - /> - {/if} + </div> </div> - </div> - {/each} + {/each} - {#if bottomPadding} - <div class=" mb-10" /> - {/if} - {/key} + {#if bottomPadding} + <div class=" mb-10" /> + {/if} + {/key} + </div> {/if} diff --git a/src/lib/components/chat/Messages/Placeholder.svelte b/src/lib/components/chat/Messages/Placeholder.svelte index e62fb41a7..39abf6c50 100644 --- a/src/lib/components/chat/Messages/Placeholder.svelte +++ b/src/lib/components/chat/Messages/Placeholder.svelte @@ -16,7 +16,7 @@ </script> {#if models.length > 0} - <div class="m-auto text-center max-w-md pb-56 px-2"> + <div class="m-auto text-center max-w-md px-2"> <div class="flex justify-center mt-8"> <div class="flex -space-x-4 mb-1"> {#each models as model, modelIdx} diff --git a/src/routes/(app)/+page.svelte b/src/routes/(app)/+page.svelte index 0cc63a5be..8de0c66ad 100644 --- a/src/routes/(app)/+page.svelte +++ b/src/routes/(app)/+page.svelte @@ -137,6 +137,11 @@ }); }; + const scrollToBottom = () => { + const element = document.getElementById('messages-container'); + element.scrollTop = element.scrollHeight; + }; + ////////////////////////// // Ollama functions ////////////////////////// @@ -316,7 +321,7 @@ await tick(); // Scroll down - window.scrollTo({ top: document.body.scrollHeight }); + scrollToBottom(); const messagesBody = [ $settings.system @@ -469,7 +474,7 @@ } if (autoScroll) { - window.scrollTo({ top: document.body.scrollHeight }); + scrollToBottom(); } } @@ -508,7 +513,7 @@ await tick(); if (autoScroll) { - window.scrollTo({ top: document.body.scrollHeight }); + scrollToBottom(); } if (messages.length == 2 && messages.at(1).content !== '') { @@ -519,8 +524,7 @@ const sendPromptOpenAI = async (model, userPrompt, responseMessageId, _chatId) => { const responseMessage = history.messages[responseMessageId]; - - window.scrollTo({ top: document.body.scrollHeight }); + scrollToBottom(); const res = await generateOpenAIChatCompletion(localStorage.token, { model: model, @@ -628,7 +632,7 @@ } if (autoScroll) { - window.scrollTo({ top: document.body.scrollHeight }); + scrollToBottom(); } } @@ -672,7 +676,7 @@ await tick(); if (autoScroll) { - window.scrollTo({ top: document.body.scrollHeight }); + scrollToBottom(); } if (messages.length == 2) { @@ -783,16 +787,18 @@ }; </script> -<svelte:window - on:scroll={(e) => { - autoScroll = window.innerHeight + window.scrollY >= document.body.offsetHeight - 40; - }} -/> - -<div class="min-h-screen w-full flex flex-col"> +<div class="min-h-screen max-h-screen w-full flex flex-col"> <Navbar {title} shareEnabled={messages.length > 0} {initNewChat} {tags} {addTag} {deleteTag} /> - <div class="flex flex-col justify-center h-full"> - <div class=" pb-2.5 flex flex-1 flex-col justify-between w-full overflow-hidden"> + <div class="flex flex-col flex-auto"> + <div + class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-80" + id="messages-container" + on:scroll={(e) => { + console.log(e.target.scrollHeight, e.target.scrollTop, e.target.clientHeight); + console.log(e.target.scrollHeight - e.target.scrollTop, e.target.clientHeight); + autoScroll = e.target.scrollHeight - e.target.scrollTop <= e.target.clientHeight + 50; + }} + > <div class="{$settings?.fullScreenMode ?? null ? 'max-w-full' @@ -801,7 +807,7 @@ <ModelSelector bind:selectedModels disabled={messages.length > 0} /> </div> - <div class=" h-full mt-14 w-full flex flex-col"> + <div class=" h-full w-full flex flex-col py-8"> <Messages chatId={$chatId} {selectedModels} diff --git a/src/routes/(app)/c/[id]/+page.svelte b/src/routes/(app)/c/[id]/+page.svelte index d796328cd..905a8135f 100644 --- a/src/routes/(app)/c/[id]/+page.svelte +++ b/src/routes/(app)/c/[id]/+page.svelte @@ -153,6 +153,11 @@ } }; + const scrollToBottom = () => { + const element = document.getElementById('messages-container'); + element.scrollTop = element.scrollHeight; + }; + ////////////////////////// // Ollama functions ////////////////////////// @@ -330,7 +335,7 @@ await tick(); // Scroll down - window.scrollTo({ top: document.body.scrollHeight }); + scrollToBottom(); const messagesBody = [ $settings.system @@ -483,7 +488,7 @@ } if (autoScroll) { - window.scrollTo({ top: document.body.scrollHeight }); + scrollToBottom(); } } @@ -522,7 +527,7 @@ await tick(); if (autoScroll) { - window.scrollTo({ top: document.body.scrollHeight }); + scrollToBottom(); } if (messages.length == 2 && messages.at(1).content !== '') { @@ -534,7 +539,7 @@ const sendPromptOpenAI = async (model, userPrompt, responseMessageId, _chatId) => { const responseMessage = history.messages[responseMessageId]; - window.scrollTo({ top: document.body.scrollHeight }); + scrollToBottom(); const res = await generateOpenAIChatCompletion(localStorage.token, { model: model, @@ -642,7 +647,7 @@ } if (autoScroll) { - window.scrollTo({ top: document.body.scrollHeight }); + scrollToBottom(); } } @@ -686,7 +691,7 @@ await tick(); if (autoScroll) { - window.scrollTo({ top: document.body.scrollHeight }); + scrollToBottom(); } if (messages.length == 2) { @@ -797,14 +802,8 @@ }); </script> -<svelte:window - on:scroll={(e) => { - autoScroll = window.innerHeight + window.scrollY >= document.body.offsetHeight - 40; - }} -/> - {#if loaded} - <div class="min-h-screen w-full flex flex-col"> + <div class="min-h-screen max-h-screen w-full flex flex-col"> <Navbar {title} shareEnabled={messages.length > 0} @@ -820,8 +819,16 @@ {addTag} {deleteTag} /> - <div class="justify-center"> - <div class=" pb-2.5 flex flex-col justify-between w-full"> + <div class="flex flex-col flex-auto"> + <div + class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0" + id="messages-container" + on:scroll={(e) => { + console.log(e.target.scrollHeight, e.target.scrollTop, e.target.clientHeight); + console.log(e.target.scrollHeight - e.target.scrollTop, e.target.clientHeight); + autoScroll = e.target.scrollHeight - e.target.scrollTop <= e.target.clientHeight + 50; + }} + > <div class="{$settings?.fullScreenMode ?? null ? 'max-w-full' From 0d39a9fe0e9001fb4099c65db8eac842e37df694 Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" <timothyjrbeck@gmail.com> Date: Thu, 15 Feb 2024 16:36:35 -0800 Subject: [PATCH 4/7] refac: styling --- src/lib/components/chat/Messages/ResponseMessage.svelte | 4 ++-- src/routes/(app)/c/[id]/+page.svelte | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/components/chat/Messages/ResponseMessage.svelte b/src/lib/components/chat/Messages/ResponseMessage.svelte index 8301a54e8..b743d06f2 100644 --- a/src/lib/components/chat/Messages/ResponseMessage.svelte +++ b/src/lib/components/chat/Messages/ResponseMessage.svelte @@ -363,7 +363,7 @@ {#if message.done} <div class=" flex justify-start space-x-1 -mt-2 overflow-x-auto buttons"> {#if siblings.length > 1} - <div class="flex self-center"> + <div class="flex self-center min-w-fit"> <button class="self-center" on:click={() => { @@ -384,7 +384,7 @@ </svg> </button> - <div class="text-xs font-bold self-center"> + <div class="text-xs font-bold self-center min-w-fit"> {siblings.indexOf(message.id) + 1} / {siblings.length} </div> diff --git a/src/routes/(app)/c/[id]/+page.svelte b/src/routes/(app)/c/[id]/+page.svelte index 905a8135f..7af734c22 100644 --- a/src/routes/(app)/c/[id]/+page.svelte +++ b/src/routes/(app)/c/[id]/+page.svelte @@ -840,7 +840,7 @@ /> </div> - <div class=" h-full mt-10 mb-32 w-full flex flex-col"> + <div class=" h-full w-full flex flex-col py-8"> <Messages chatId={$chatId} {selectedModels} From 2c2c2fd889b45e732438c7d4ef33efac479703d7 Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" <timothyjrbeck@gmail.com> Date: Thu, 15 Feb 2024 20:10:48 -0800 Subject: [PATCH 5/7] refac --- src/lib/components/chat/Messages.svelte | 5 ++--- src/lib/components/layout/Sidebar.svelte | 8 ++++++-- src/routes/(app)/+page.svelte | 2 -- src/routes/(app)/c/[id]/+page.svelte | 2 -- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/lib/components/chat/Messages.svelte b/src/lib/components/chat/Messages.svelte index 3bca2efda..b02ba1166 100644 --- a/src/lib/components/chat/Messages.svelte +++ b/src/lib/components/chat/Messages.svelte @@ -29,7 +29,6 @@ $: if (autoScroll && bottomPadding) { (async () => { await tick(); - scrollToBottom(); })(); } @@ -167,7 +166,7 @@ await tick(); const element = document.getElementById('messages-container'); - autoScroll = element.scrollHeight - element.scrollTop === element.clientHeight - 40; + autoScroll = element.scrollHeight - element.scrollTop <= element.clientHeight + 50; setTimeout(() => { scrollToBottom(); @@ -216,7 +215,7 @@ await tick(); const element = document.getElementById('messages-container'); - autoScroll = element.scrollHeight - element.scrollTop === element.clientHeight - 40; + autoScroll = element.scrollHeight - element.scrollTop <= element.clientHeight + 50; setTimeout(() => { scrollToBottom(); diff --git a/src/lib/components/layout/Sidebar.svelte b/src/lib/components/layout/Sidebar.svelte index 266eab3b0..6136b19e4 100644 --- a/src/lib/components/layout/Sidebar.svelte +++ b/src/lib/components/layout/Sidebar.svelte @@ -89,10 +89,14 @@ bind:this={navElement} class="h-screen {show ? '' - : '-translate-x-[260px] w-[0px]'} w-[260px] bg-black text-gray-200 shadow-2xl text-sm transition z-40 fixed top-0 left-0 lg:relative + : '-translate-x-[260px] w-[0px]'} w-[260px] min-w[260px] bg-black text-gray-200 shadow-2xl text-sm transition z-40 fixed top-0 left-0 lg:relative " > - <div class="py-2.5 my-auto flex flex-col justify-between h-screen {show ? '' : 'invisible'}"> + <div + class="py-2.5 my-auto flex flex-col justify-between h-screen w-[260px] {show + ? '' + : 'invisible'}" + > <div class="px-2.5 flex justify-center space-x-2"> <button id="sidebar-new-chat-button" diff --git a/src/routes/(app)/+page.svelte b/src/routes/(app)/+page.svelte index 8de0c66ad..f902f4992 100644 --- a/src/routes/(app)/+page.svelte +++ b/src/routes/(app)/+page.svelte @@ -794,8 +794,6 @@ class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-80" id="messages-container" on:scroll={(e) => { - console.log(e.target.scrollHeight, e.target.scrollTop, e.target.clientHeight); - console.log(e.target.scrollHeight - e.target.scrollTop, e.target.clientHeight); autoScroll = e.target.scrollHeight - e.target.scrollTop <= e.target.clientHeight + 50; }} > diff --git a/src/routes/(app)/c/[id]/+page.svelte b/src/routes/(app)/c/[id]/+page.svelte index 7af734c22..a358e14cb 100644 --- a/src/routes/(app)/c/[id]/+page.svelte +++ b/src/routes/(app)/c/[id]/+page.svelte @@ -824,8 +824,6 @@ class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0" id="messages-container" on:scroll={(e) => { - console.log(e.target.scrollHeight, e.target.scrollTop, e.target.clientHeight); - console.log(e.target.scrollHeight - e.target.scrollTop, e.target.clientHeight); autoScroll = e.target.scrollHeight - e.target.scrollTop <= e.target.clientHeight + 50; }} > From 74a3900ad0816a0f033bd2e70c3e385d296abc5f Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" <timothyjrbeck@gmail.com> Date: Thu, 15 Feb 2024 20:18:05 -0800 Subject: [PATCH 6/7] refac --- src/routes/(app)/+page.svelte | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/routes/(app)/+page.svelte b/src/routes/(app)/+page.svelte index f902f4992..1dd6319bc 100644 --- a/src/routes/(app)/+page.svelte +++ b/src/routes/(app)/+page.svelte @@ -791,7 +791,7 @@ <Navbar {title} shareEnabled={messages.length > 0} {initNewChat} {tags} {addTag} {deleteTag} /> <div class="flex flex-col flex-auto"> <div - class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-80" + class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0" id="messages-container" on:scroll={(e) => { autoScroll = e.target.scrollHeight - e.target.scrollTop <= e.target.clientHeight + 50; @@ -802,7 +802,10 @@ ? 'max-w-full' : 'max-w-2xl md:px-0'} mx-auto w-full px-4" > - <ModelSelector bind:selectedModels disabled={messages.length > 0} /> + <ModelSelector + bind:selectedModels + disabled={messages.length > 0 && !selectedModels.includes('')} + /> </div> <div class=" h-full w-full flex flex-col py-8"> From dc322084bb8cbee02400c5913a9f749534d2e702 Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" <timothyjrbeck@gmail.com> Date: Thu, 15 Feb 2024 20:26:24 -0800 Subject: [PATCH 7/7] refac: styling --- src/lib/components/chat/Settings/Models.svelte | 4 ++-- src/routes/error/+page.svelte | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lib/components/chat/Settings/Models.svelte b/src/lib/components/chat/Settings/Models.svelte index 529b778ff..aafa333f3 100644 --- a/src/lib/components/chat/Settings/Models.svelte +++ b/src/lib/components/chat/Settings/Models.svelte @@ -3,7 +3,7 @@ import toast from 'svelte-french-toast'; import { createModel, deleteModel, pullModel } from '$lib/apis/ollama'; - import { WEBUI_API_BASE_URL } from '$lib/constants'; + import { WEBUI_API_BASE_URL, WEBUI_NAME } from '$lib/constants'; import { models, user } from '$lib/stores'; import { splitStream } from '$lib/utils'; @@ -59,7 +59,7 @@ } else { toast.success(`Model '${modelName}' has been successfully downloaded.`); - const notification = new Notification(`Ollama`, { + const notification = new Notification(WEBUI_NAME, { body: `Model '${modelName}' has been successfully downloaded.`, icon: '/favicon.png' }); diff --git a/src/routes/error/+page.svelte b/src/routes/error/+page.svelte index 2ceea878c..f7ba2f4f5 100644 --- a/src/routes/error/+page.svelte +++ b/src/routes/error/+page.svelte @@ -1,5 +1,6 @@ <script> import { goto } from '$app/navigation'; + import { WEBUI_NAME } from '$lib/constants'; import { config } from '$lib/stores'; import { onMount } from 'svelte'; @@ -19,7 +20,7 @@ <div class="absolute rounded-xl w-full h-full backdrop-blur flex justify-center"> <div class="m-auto pb-44 flex flex-col justify-center"> <div class="max-w-md"> - <div class="text-center text-2xl font-medium z-50">Ollama WebUI Backend Required</div> + <div class="text-center text-2xl font-medium z-50">{WEBUI_NAME} Backend Required</div> <div class=" mt-4 text-center text-sm w-full"> Oops! You're using an unsupported method (frontend only). Please serve the WebUI from