forked from open-webui/open-webui
		
	Merge pull request #751 from ollama-webui/styling
feat: fullscreen mode
This commit is contained in:
		
						commit
						a7bb692a54
					
				
					 13 changed files with 259 additions and 222 deletions
				
			
		|  | @ -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="fixed bottom-0 w-full"> | ||||
| 	<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} | ||||
|  |  | |||
|  | @ -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> | ||||
|  |  | |||
|  | @ -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> | ||||
|  |  | |||
|  | @ -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> | ||||
|  |  | |||
|  | @ -29,10 +29,15 @@ | |||
| 	$: 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 +165,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 + 50; | ||||
| 
 | ||||
| 		setTimeout(() => { | ||||
| 			window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' }); | ||||
| 			scrollToBottom(); | ||||
| 		}, 100); | ||||
| 	}; | ||||
| 
 | ||||
|  | @ -208,9 +214,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 + 50; | ||||
| 
 | ||||
| 		setTimeout(() => { | ||||
| 			window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' }); | ||||
| 			scrollToBottom(); | ||||
| 		}, 100); | ||||
| 	}; | ||||
| </script> | ||||
|  | @ -218,6 +226,7 @@ | |||
| {#if messages.length == 0} | ||||
| 	<Placeholder models={selectedModels} modelfiles={selectedModelfiles} /> | ||||
| {:else} | ||||
| 	<div class=" pb-10"> | ||||
| 		{#key chatId} | ||||
| 			{#each messages as message, messageIdx} | ||||
| 				<div class=" w-full"> | ||||
|  | @ -309,4 +318,5 @@ | |||
| 				<div class=" mb-10" /> | ||||
| 			{/if} | ||||
| 		{/key} | ||||
| 	</div> | ||||
| {/if} | ||||
|  |  | |||
|  | @ -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} | ||||
|  |  | |||
|  | @ -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} | ||||
|  | @ -365,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={() => { | ||||
|  | @ -386,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> | ||||
| 
 | ||||
|  |  | |||
|  | @ -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' | ||||
| 					}); | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -89,10 +89,14 @@ | |||
| 	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] 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"> | ||||
| 	<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" | ||||
|  |  | |||
|  | @ -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 | ||||
|  | @ -440,7 +445,7 @@ | |||
| 														selectedModelfile.title.charAt(0).toUpperCase() + | ||||
| 														selectedModelfile.title.slice(1) | ||||
| 												  }` | ||||
| 												: `Ollama - ${model}`, | ||||
| 												: `${model}`, | ||||
| 											{ | ||||
| 												body: responseMessage.content, | ||||
| 												icon: selectedModelfile?.imageUrl ?? '/favicon.png' | ||||
|  | @ -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,24 +787,28 @@ | |||
| 	}; | ||||
| </script> | ||||
| 
 | ||||
| <svelte:window | ||||
| 	on:scroll={(e) => { | ||||
| 		autoScroll = window.innerHeight + window.scrollY >= document.body.offsetHeight - 40; | ||||
| 	}} | ||||
| /> | ||||
| 
 | ||||
| <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="min-h-screen w-full flex justify-center"> | ||||
| 	<div class=" py-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) => { | ||||
| 				autoScroll = e.target.scrollHeight - e.target.scrollTop <= e.target.clientHeight + 50; | ||||
| 			}} | ||||
| 		> | ||||
| 			<div | ||||
| 				class="{$settings?.fullScreenMode ?? null | ||||
| 					? 'max-w-full' | ||||
| 				: 'max-w-2xl md:px-0'} mx-auto w-full px-4 mt-10" | ||||
| 					: '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 mt-10 mb-32 w-full flex flex-col"> | ||||
| 			<div class=" h-full w-full flex flex-col py-8"> | ||||
| 				<Messages | ||||
| 					chatId={$chatId} | ||||
| 					{selectedModels} | ||||
|  | @ -827,3 +835,4 @@ | |||
| 			{stopResponse} | ||||
| 		/> | ||||
| 	</div> | ||||
| </div> | ||||
|  |  | |||
|  | @ -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 | ||||
|  | @ -454,7 +459,7 @@ | |||
| 														selectedModelfile.title.charAt(0).toUpperCase() + | ||||
| 														selectedModelfile.title.slice(1) | ||||
| 												  }` | ||||
| 												: `Ollama - ${model}`, | ||||
| 												: `${model}`, | ||||
| 											{ | ||||
| 												body: responseMessage.content, | ||||
| 												icon: selectedModelfile?.imageUrl ?? '/favicon.png' | ||||
|  | @ -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,13 +802,8 @@ | |||
| 	}); | ||||
| </script> | ||||
| 
 | ||||
| <svelte:window | ||||
| 	on:scroll={(e) => { | ||||
| 		autoScroll = window.innerHeight + window.scrollY >= document.body.offsetHeight - 40; | ||||
| 	}} | ||||
| /> | ||||
| 
 | ||||
| {#if loaded} | ||||
| 	<div class="min-h-screen max-h-screen w-full flex flex-col"> | ||||
| 		<Navbar | ||||
| 			{title} | ||||
| 			shareEnabled={messages.length > 0} | ||||
|  | @ -819,12 +819,18 @@ | |||
| 			{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="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) => { | ||||
| 					autoScroll = e.target.scrollHeight - e.target.scrollTop <= e.target.clientHeight + 50; | ||||
| 				}} | ||||
| 			> | ||||
| 				<div | ||||
| 					class="{$settings?.fullScreenMode ?? null | ||||
| 						? 'max-w-full' | ||||
| 					: 'max-w-2xl md:px-0'} mx-auto w-full px-4 mt-10" | ||||
| 						: 'max-w-2xl md:px-0'} mx-auto w-full px-4" | ||||
| 				> | ||||
| 					<ModelSelector | ||||
| 						bind:selectedModels | ||||
|  | @ -832,7 +838,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} | ||||
|  | @ -853,10 +859,12 @@ | |||
| 				bind:files | ||||
| 				bind:prompt | ||||
| 				bind:autoScroll | ||||
| 			suggestionPrompts={selectedModelfile?.suggestionPrompts ?? $config.default_prompt_suggestions} | ||||
| 				suggestionPrompts={selectedModelfile?.suggestionPrompts ?? | ||||
| 					$config.default_prompt_suggestions} | ||||
| 				{messages} | ||||
| 				{submitPrompt} | ||||
| 				{stopResponse} | ||||
| 			/> | ||||
| 		</div> | ||||
| 	</div> | ||||
| {/if} | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Timothy Jaeryang Baek
						Timothy Jaeryang Baek