From 7978adbf457be13290548b28e0169e67c227d08e Mon Sep 17 00:00:00 2001
From: Jun Siang Cheah <git@jscheah.me>
Date: Sun, 31 Mar 2024 22:03:28 +0100
Subject: [PATCH] feat: add frontend support for locally sharing chats

---
 src/lib/apis/chats/index.ts                   |  96 +++++++++
 src/lib/components/chat/Messages.svelte       |   3 +
 .../chat/Messages/ResponseMessage.svelte      | 158 ++++++++-------
 .../chat/Messages/UserMessage.svelte          |  49 ++---
 src/lib/components/chat/ShareChatModal.svelte |  12 ++
 src/lib/components/layout/Navbar.svelte       |  30 ++-
 src/routes/(app)/+page.svelte                 |   2 +-
 src/routes/(app)/c/[id]/+page.svelte          |   2 +-
 src/routes/(app)/s/[id]/+page.svelte          | 187 ++++++++++++++++++
 9 files changed, 433 insertions(+), 106 deletions(-)
 create mode 100644 src/routes/(app)/s/[id]/+page.svelte

diff --git a/src/lib/apis/chats/index.ts b/src/lib/apis/chats/index.ts
index 35b259d56..4fcd1b639 100644
--- a/src/lib/apis/chats/index.ts
+++ b/src/lib/apis/chats/index.ts
@@ -218,6 +218,102 @@ export const getChatById = async (token: string, id: string) => {
 	return res;
 };
 
+export const getChatByShareId = async (token: string, share_id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/share/${share_id}`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const shareChatById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/${id}/share`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const deleteSharedChatById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/${id}`, {
+		method: 'DELETE',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
 export const updateChatById = async (token: string, id: string, chat: object) => {
 	let error = null;
 
diff --git a/src/lib/components/chat/Messages.svelte b/src/lib/components/chat/Messages.svelte
index 7afb5c376..4cd97ca80 100644
--- a/src/lib/components/chat/Messages.svelte
+++ b/src/lib/components/chat/Messages.svelte
@@ -16,6 +16,7 @@
 	const i18n = getContext('i18n');
 
 	export let chatId = '';
+	export let readOnly = false;
 	export let sendPrompt: Function;
 	export let continueGeneration: Function;
 	export let regenerateResponse: Function;
@@ -317,6 +318,7 @@
 							<UserMessage
 								on:delete={() => messageDeleteHandler(message.id)}
 								user={$user}
+								{readOnly}
 								{message}
 								isFirstMessage={messageIdx === 0}
 								siblings={message.parentId !== null
@@ -335,6 +337,7 @@
 								modelfiles={selectedModelfiles}
 								siblings={history.messages[message.parentId]?.childrenIds ?? []}
 								isLastMessage={messageIdx + 1 === messages.length}
+								{readOnly}
 								{confirmEditResponseMessage}
 								{showPreviousMessage}
 								{showNextMessage}
diff --git a/src/lib/components/chat/Messages/ResponseMessage.svelte b/src/lib/components/chat/Messages/ResponseMessage.svelte
index 50e6e0c03..3888d764e 100644
--- a/src/lib/components/chat/Messages/ResponseMessage.svelte
+++ b/src/lib/components/chat/Messages/ResponseMessage.svelte
@@ -33,6 +33,8 @@
 
 	export let isLastMessage = true;
 
+	export let readOnly = false;
+
 	export let confirmEditResponseMessage: Function;
 	export let showPreviousMessage: Function;
 	export let showNextMessage: Function;
@@ -469,31 +471,33 @@
 											</div>
 										{/if}
 
-										<Tooltip content="Edit" placement="bottom">
-											<button
-												class="{isLastMessage
-													? 'visible'
-													: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition"
-												on:click={() => {
-													editMessageHandler();
-												}}
-											>
-												<svg
-													xmlns="http://www.w3.org/2000/svg"
-													fill="none"
-													viewBox="0 0 24 24"
-													stroke-width="2"
-													stroke="currentColor"
-													class="w-4 h-4"
+										{#if !readOnly}
+											<Tooltip content="Edit" placement="bottom">
+												<button
+													class="{isLastMessage
+														? 'visible'
+														: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition"
+													on:click={() => {
+														editMessageHandler();
+													}}
 												>
-													<path
-														stroke-linecap="round"
-														stroke-linejoin="round"
-														d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
-													/>
-												</svg>
-											</button>
-										</Tooltip>
+													<svg
+														xmlns="http://www.w3.org/2000/svg"
+														fill="none"
+														viewBox="0 0 24 24"
+														stroke-width="2"
+														stroke="currentColor"
+														class="w-4 h-4"
+													>
+														<path
+															stroke-linecap="round"
+															stroke-linejoin="round"
+															d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
+														/>
+													</svg>
+												</button>
+											</Tooltip>
+										{/if}
 
 										<Tooltip content="Copy" placement="bottom">
 											<button
@@ -521,59 +525,61 @@
 											</button>
 										</Tooltip>
 
-										<Tooltip content="Good Response" placement="bottom">
-											<button
-												class="{isLastMessage
-													? 'visible'
-													: 'invisible group-hover:visible'} p-1 rounded {message.rating === 1
-													? 'bg-gray-100 dark:bg-gray-800'
-													: ''} dark:hover:text-white hover:text-black transition"
-												on:click={() => {
-													rateMessage(message.id, 1);
-												}}
-											>
-												<svg
-													stroke="currentColor"
-													fill="none"
-													stroke-width="2"
-													viewBox="0 0 24 24"
-													stroke-linecap="round"
-													stroke-linejoin="round"
-													class="w-4 h-4"
-													xmlns="http://www.w3.org/2000/svg"
-													><path
-														d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"
-													/></svg
+										{#if !readOnly}
+											<Tooltip content="Good Response" placement="bottom">
+												<button
+													class="{isLastMessage
+														? 'visible'
+														: 'invisible group-hover:visible'} p-1 rounded {message.rating === 1
+														? 'bg-gray-100 dark:bg-gray-800'
+														: ''} dark:hover:text-white hover:text-black transition"
+													on:click={() => {
+														rateMessage(message.id, 1);
+													}}
 												>
-											</button>
-										</Tooltip>
+													<svg
+														stroke="currentColor"
+														fill="none"
+														stroke-width="2"
+														viewBox="0 0 24 24"
+														stroke-linecap="round"
+														stroke-linejoin="round"
+														class="w-4 h-4"
+														xmlns="http://www.w3.org/2000/svg"
+														><path
+															d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"
+														/></svg
+													>
+												</button>
+											</Tooltip>
 
-										<Tooltip content="Bad Response" placement="bottom">
-											<button
-												class="{isLastMessage
-													? 'visible'
-													: 'invisible group-hover:visible'} p-1 rounded {message.rating === -1
-													? 'bg-gray-100 dark:bg-gray-800'
-													: ''} dark:hover:text-white hover:text-black transition"
-												on:click={() => {
-													rateMessage(message.id, -1);
-												}}
-											>
-												<svg
-													stroke="currentColor"
-													fill="none"
-													stroke-width="2"
-													viewBox="0 0 24 24"
-													stroke-linecap="round"
-													stroke-linejoin="round"
-													class="w-4 h-4"
-													xmlns="http://www.w3.org/2000/svg"
-													><path
-														d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17"
-													/></svg
+											<Tooltip content="Bad Response" placement="bottom">
+												<button
+													class="{isLastMessage
+														? 'visible'
+														: 'invisible group-hover:visible'} p-1 rounded {message.rating === -1
+														? 'bg-gray-100 dark:bg-gray-800'
+														: ''} dark:hover:text-white hover:text-black transition"
+													on:click={() => {
+														rateMessage(message.id, -1);
+													}}
 												>
-											</button>
-										</Tooltip>
+													<svg
+														stroke="currentColor"
+														fill="none"
+														stroke-width="2"
+														viewBox="0 0 24 24"
+														stroke-linecap="round"
+														stroke-linejoin="round"
+														class="w-4 h-4"
+														xmlns="http://www.w3.org/2000/svg"
+														><path
+															d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17"
+														/></svg
+													>
+												</button>
+											</Tooltip>
+										{/if}
 
 										<Tooltip content="Read Aloud" placement="bottom">
 											<button
@@ -656,7 +662,7 @@
 											</button>
 										</Tooltip>
 
-										{#if $config.images}
+										{#if $config.images && !readOnly}
 											<Tooltip content="Generate Image" placement="bottom">
 												<button
 													class="{isLastMessage
@@ -752,7 +758,7 @@
 											</Tooltip>
 										{/if}
 
-										{#if isLastMessage}
+										{#if isLastMessage && !readOnly}
 											<Tooltip content="Continue Response" placement="bottom">
 												<button
 													type="button"
diff --git a/src/lib/components/chat/Messages/UserMessage.svelte b/src/lib/components/chat/Messages/UserMessage.svelte
index 4e0088d78..99ce6b8f7 100644
--- a/src/lib/components/chat/Messages/UserMessage.svelte
+++ b/src/lib/components/chat/Messages/UserMessage.svelte
@@ -15,6 +15,7 @@
 	export let message;
 	export let siblings;
 	export let isFirstMessage: boolean;
+	export let readOnly: boolean;
 
 	export let confirmEditMessage: Function;
 	export let showPreviousMessage: Function;
@@ -250,29 +251,31 @@
 							</div>
 						{/if}
 
-						<Tooltip content="Edit" placement="bottom">
-							<button
-								class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition edit-user-message-button"
-								on:click={() => {
-									editMessageHandler();
-								}}
-							>
-								<svg
-									xmlns="http://www.w3.org/2000/svg"
-									fill="none"
-									viewBox="0 0 24 24"
-									stroke-width="2"
-									stroke="currentColor"
-									class="w-4 h-4"
+						{#if !readOnly}
+							<Tooltip content="Edit" placement="bottom">
+								<button
+									class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition edit-user-message-button"
+									on:click={() => {
+										editMessageHandler();
+									}}
 								>
-									<path
-										stroke-linecap="round"
-										stroke-linejoin="round"
-										d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
-									/>
-								</svg>
-							</button>
-						</Tooltip>
+									<svg
+										xmlns="http://www.w3.org/2000/svg"
+										fill="none"
+										viewBox="0 0 24 24"
+										stroke-width="2"
+										stroke="currentColor"
+										class="w-4 h-4"
+									>
+										<path
+											stroke-linecap="round"
+											stroke-linejoin="round"
+											d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
+										/>
+									</svg>
+								</button>
+							</Tooltip>
+						{/if}
 
 						<Tooltip content="Copy" placement="bottom">
 							<button
@@ -298,7 +301,7 @@
 							</button>
 						</Tooltip>
 
-						{#if !isFirstMessage}
+						{#if !isFirstMessage && !readOnly}
 							<Tooltip content="Delete" placement="bottom">
 								<button
 									class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition"
diff --git a/src/lib/components/chat/ShareChatModal.svelte b/src/lib/components/chat/ShareChatModal.svelte
index aef098f04..ac1bfef9f 100644
--- a/src/lib/components/chat/ShareChatModal.svelte
+++ b/src/lib/components/chat/ShareChatModal.svelte
@@ -6,6 +6,7 @@
 
 	export let downloadChat: Function;
 	export let shareChat: Function;
+	export let shareLocalChat: Function;
 
 	export let show = false;
 </script>
@@ -23,6 +24,17 @@
 			{$i18n.t('Share to OpenWebUI Community')}
 		</button>
 
+		<button
+			class=" self-center px-8 py-1.5 w-full rounded-full text-sm font-medium bg-blue-600 hover:bg-blue-500 text-white mt-1.5"
+			type="button"
+			on:click={() => {
+				shareLocalChat();
+				show = false;
+			}}
+		>
+			{$i18n.t('Create local share link')}
+		</button>
+
 		<div class="flex justify-center space-x-1 mt-1.5">
 			<div class=" self-center text-gray-400 text-xs font-medium">{$i18n.t('or')}</div>
 
diff --git a/src/lib/components/layout/Navbar.svelte b/src/lib/components/layout/Navbar.svelte
index 2680de8dd..7be5fbd8c 100644
--- a/src/lib/components/layout/Navbar.svelte
+++ b/src/lib/components/layout/Navbar.svelte
@@ -5,7 +5,7 @@
 	const { saveAs } = fileSaver;
 
 	import { Separator } from 'bits-ui';
-	import { getChatById } from '$lib/apis/chats';
+	import { getChatById, shareChatById } from '$lib/apis/chats';
 	import { WEBUI_NAME, chatId, modelfiles, settings, showSettings } from '$lib/stores';
 
 	import { slide } from 'svelte/transition';
@@ -19,6 +19,7 @@
 	import ChevronUpDown from '../icons/ChevronUpDown.svelte';
 	import Menu from './Navbar/Menu.svelte';
 	import TagChatModal from '../chat/TagChatModal.svelte';
+	import { copyToClipboard } from '$lib/utils';
 
 	const i18n = getContext('i18n');
 
@@ -32,7 +33,7 @@
 	export let addTag: Function;
 	export let deleteTag: Function;
 
-	export let showModelSelector = false;
+	export let showModelSelector = true;
 
 	let showShareChatModal = false;
 	let showTagChatModal = false;
@@ -64,6 +65,23 @@
 		);
 	};
 
+	const shareLocalChat = async () => {
+		const chat = await getChatById(localStorage.token, $chatId);
+		console.log('shareLocal', chat);
+		if (chat.share_id) {
+			const shareUrl = `${window.location.origin}/s/${chat.share_id}`;
+			toast.info(
+				$i18n.t('Chat is already shared at {{shareUrl}}, copied to clipboard', { shareUrl })
+			);
+			copyToClipboard(shareUrl);
+		} else {
+			const sharedChat = await shareChatById(localStorage.token, $chatId);
+			const shareUrl = `${window.location.origin}/s/${sharedChat.id}`;
+			toast.info($i18n.t('Chat is now shared at {{shareUrl}}, copied to clipboard', { shareUrl }));
+			copyToClipboard(shareUrl);
+		}
+	};
+
 	const downloadChat = async () => {
 		const chat = (await getChatById(localStorage.token, $chatId)).chat;
 		console.log('download', chat);
@@ -80,7 +98,7 @@
 	};
 </script>
 
-<ShareChatModal bind:show={showShareChatModal} {downloadChat} {shareChat} />
+<ShareChatModal bind:show={showShareChatModal} {downloadChat} {shareChat} {shareLocalChat} />
 <!-- <TagChatModal bind:show={showTagChatModal} {tags} {deleteTag} {addTag} /> -->
 <nav id="nav" class=" sticky py-2.5 top-0 flex flex-row justify-center z-30">
 	<div
@@ -135,8 +153,10 @@
 		</div> -->
 
 		<div class="flex items-center w-full max-w-full">
-			<div class="w-full flex-1 overflow-hidden max-w-full">
-				<ModelSelector bind:selectedModels />
+			<div class="flex-1 overflow-hidden max-w-full">
+				{#if showModelSelector}
+					<ModelSelector bind:selectedModels />
+				{/if}
 			</div>
 
 			<div class="self-start flex flex-none items-center">
diff --git a/src/routes/(app)/+page.svelte b/src/routes/(app)/+page.svelte
index 1580e365e..4e7ebfa30 100644
--- a/src/routes/(app)/+page.svelte
+++ b/src/routes/(app)/+page.svelte
@@ -48,7 +48,7 @@
 	let messagesContainerElement: HTMLDivElement;
 	let currentRequestId = null;
 
-	let showModelSelector = false;
+	let showModelSelector = true;
 	let selectedModels = [''];
 
 	let selectedModelfile = null;
diff --git a/src/routes/(app)/c/[id]/+page.svelte b/src/routes/(app)/c/[id]/+page.svelte
index 139fcd401..74cf9df10 100644
--- a/src/routes/(app)/c/[id]/+page.svelte
+++ b/src/routes/(app)/c/[id]/+page.svelte
@@ -56,7 +56,7 @@
 	let currentRequestId = null;
 
 	// let chatId = $page.params.id;
-	let showModelSelector = false;
+	let showModelSelector = true;
 	let selectedModels = [''];
 	let selectedModelfile = null;
 
diff --git a/src/routes/(app)/s/[id]/+page.svelte b/src/routes/(app)/s/[id]/+page.svelte
new file mode 100644
index 000000000..2b5226205
--- /dev/null
+++ b/src/routes/(app)/s/[id]/+page.svelte
@@ -0,0 +1,187 @@
+<script lang="ts">
+	import { onMount, tick, getContext } from 'svelte';
+	import { goto } from '$app/navigation';
+	import { page } from '$app/stores';
+
+	import { modelfiles, settings, chatId, WEBUI_NAME } from '$lib/stores';
+	import { convertMessagesToHistory } from '$lib/utils';
+
+	import { getChatByShareId } from '$lib/apis/chats';
+
+	import Messages from '$lib/components/chat/Messages.svelte';
+	import Navbar from '$lib/components/layout/Navbar.svelte';
+
+	const i18n = getContext('i18n');
+
+	let loaded = false;
+
+	let autoScroll = true;
+	let processing = '';
+	let messagesContainerElement: HTMLDivElement;
+
+	// let chatId = $page.params.id;
+	let showModelSelector = false;
+	let selectedModels = [''];
+
+	let selectedModelfiles = {};
+	$: selectedModelfiles = selectedModels.reduce((a, tagName, i, arr) => {
+		const modelfile =
+			$modelfiles.filter((modelfile) => modelfile.tagName === tagName)?.at(0) ?? undefined;
+
+		return {
+			...a,
+			...(modelfile && { [tagName]: modelfile })
+		};
+	}, {});
+
+	let chat = null;
+
+	let title = '';
+	let files = [];
+
+	let messages = [];
+	let history = {
+		messages: {},
+		currentId: null
+	};
+
+	$: if (history.currentId !== null) {
+		let _messages = [];
+
+		let currentMessage = history.messages[history.currentId];
+		while (currentMessage !== null) {
+			_messages.unshift({ ...currentMessage });
+			currentMessage =
+				currentMessage.parentId !== null ? history.messages[currentMessage.parentId] : null;
+		}
+		messages = _messages;
+	} else {
+		messages = [];
+	}
+
+	$: if ($page.params.id) {
+		(async () => {
+			if (await loadSharedChat()) {
+				await tick();
+				loaded = true;
+
+				window.setTimeout(() => scrollToBottom(), 0);
+				const chatInput = document.getElementById('chat-textarea');
+				chatInput?.focus();
+			} else {
+				await goto('/');
+			}
+		})();
+	}
+
+	//////////////////////////
+	// Web functions
+	//////////////////////////
+
+	const loadSharedChat = async () => {
+		await chatId.set($page.params.id);
+		chat = await getChatByShareId(localStorage.token, $chatId).catch(async (error) => {
+			await goto('/');
+			return null;
+		});
+
+		if (chat) {
+			const chatContent = chat.chat;
+
+			if (chatContent) {
+				console.log(chatContent);
+
+				selectedModels =
+					(chatContent?.models ?? undefined) !== undefined
+						? chatContent.models
+						: [chatContent.models ?? ''];
+				history =
+					(chatContent?.history ?? undefined) !== undefined
+						? chatContent.history
+						: convertMessagesToHistory(chatContent.messages);
+				title = chatContent.title;
+
+				let _settings = JSON.parse(localStorage.getItem('settings') ?? '{}');
+				await settings.set({
+					..._settings,
+					system: chatContent.system ?? _settings.system,
+					options: chatContent.options ?? _settings.options
+				});
+				autoScroll = true;
+				await tick();
+
+				if (messages.length > 0) {
+					history.messages[messages.at(-1).id].done = true;
+				}
+				await tick();
+
+				return true;
+			} else {
+				return null;
+			}
+		}
+	};
+
+	const scrollToBottom = () => {
+		if (messagesContainerElement) {
+			messagesContainerElement.scrollTop = messagesContainerElement.scrollHeight;
+		}
+	};
+
+	onMount(async () => {
+		if (!($settings.saveChatHistory ?? true)) {
+			await goto('/');
+		}
+	});
+</script>
+
+<svelte:head>
+	<title>
+		{title
+			? `${title.length > 30 ? `${title.slice(0, 30)}...` : title} | ${$WEBUI_NAME}`
+			: `${$WEBUI_NAME}`}
+	</title>
+</svelte:head>
+
+{#if loaded}
+	<div class="min-h-screen max-h-screen w-full flex flex-col">
+		<Navbar
+			{title}
+			bind:selectedModels
+			bind:showModelSelector
+			shareEnabled={false}
+			initNewChat={async () => {
+				goto('/');
+			}}
+		/>
+		<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"
+				bind:this={messagesContainerElement}
+				on:scroll={(e) => {
+					autoScroll =
+						messagesContainerElement.scrollHeight - messagesContainerElement.scrollTop <=
+						messagesContainerElement.clientHeight + 5;
+				}}
+			>
+				<div class=" h-full w-full flex flex-col py-4">
+					<Messages
+						chatId={$chatId}
+						readOnly={true}
+						{selectedModels}
+						{selectedModelfiles}
+						{processing}
+						bind:history
+						bind:messages
+						bind:autoScroll
+						bottomPadding={files.length > 0}
+						sendPrompt={() => {}}
+						continueGeneration={() => {}}
+						regenerateResponse={() => {}}
+					/>
+				</div>
+			</div>
+		</div>
+	</div>
+{/if}