Browse Source

refac: styling

Timothy J. Baek 1 year ago
parent
commit
e99d69bfe2

+ 15 - 8
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}

+ 1 - 1
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>

+ 1 - 1
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>

+ 1 - 1
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>

+ 100 - 89
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}
-						/>
-
-						{#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);
+	<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;
 												}
-												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
-									>
-								</div>
-								<div class=" text-sm font-medium">
-									{processing}
+												@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>
+							{/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}

+ 1 - 1
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}

+ 23 - 17
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}

+ 22 - 15
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'