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