forked from open-webui/open-webui
		
	Merge pull request #1025 from Carlos-err406/fix/dom-queries
rf: less dom queries using bind:this
This commit is contained in:
		
						commit
						2d59847fb9
					
				
					 15 changed files with 87 additions and 82 deletions
				
			
		|  | @ -19,7 +19,7 @@ | ||||||
| 
 | 
 | ||||||
| 	export let suggestionPrompts = []; | 	export let suggestionPrompts = []; | ||||||
| 	export let autoScroll = true; | 	export let autoScroll = true; | ||||||
| 
 | 	let chatTextAreaElement:HTMLTextAreaElement | ||||||
| 	let filesInputElement; | 	let filesInputElement; | ||||||
| 
 | 
 | ||||||
| 	let promptsElement; | 	let promptsElement; | ||||||
|  | @ -43,11 +43,9 @@ | ||||||
| 	let speechRecognition; | 	let speechRecognition; | ||||||
| 
 | 
 | ||||||
| 	$: if (prompt) { | 	$: if (prompt) { | ||||||
| 		const chatInput = document.getElementById('chat-textarea'); | 		if (chatTextAreaElement) { | ||||||
| 
 | 			chatTextAreaElement.style.height = ''; | ||||||
| 		if (chatInput) { | 			chatTextAreaElement.style.height = Math.min(chatTextAreaElement.scrollHeight, 200) + 'px'; | ||||||
| 			chatInput.style.height = ''; |  | ||||||
| 			chatInput.style.height = Math.min(chatInput.scrollHeight, 200) + 'px'; |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -86,9 +84,7 @@ | ||||||
| 			if (res) { | 			if (res) { | ||||||
| 				prompt = res.text; | 				prompt = res.text; | ||||||
| 				await tick(); | 				await tick(); | ||||||
| 
 | 				chatTextAreaElement?.focus(); | ||||||
| 				const inputElement = document.getElementById('chat-textarea'); |  | ||||||
| 				inputElement?.focus(); |  | ||||||
| 
 | 
 | ||||||
| 				if (prompt !== '' && $settings?.speechAutoSend === true) { | 				if (prompt !== '' && $settings?.speechAutoSend === true) { | ||||||
| 					submitPrompt(prompt, user); | 					submitPrompt(prompt, user); | ||||||
|  | @ -191,8 +187,7 @@ | ||||||
| 						prompt = `${prompt}${transcript}`; | 						prompt = `${prompt}${transcript}`; | ||||||
| 
 | 
 | ||||||
| 						await tick(); | 						await tick(); | ||||||
| 						const inputElement = document.getElementById('chat-textarea'); | 						chatTextAreaElement?.focus(); | ||||||
| 						inputElement?.focus(); |  | ||||||
| 
 | 
 | ||||||
| 						// Restart the inactivity timeout | 						// Restart the inactivity timeout | ||||||
| 						timeoutId = setTimeout(() => { | 						timeoutId = setTimeout(() => { | ||||||
|  | @ -294,8 +289,7 @@ | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	onMount(() => { | 	onMount(() => { | ||||||
| 		const chatInput = document.getElementById('chat-textarea'); | 		window.setTimeout(() => chatTextAreaElement?.focus(), 0); | ||||||
| 		window.setTimeout(() => chatInput?.focus(), 0); |  | ||||||
| 
 | 
 | ||||||
| 		const dropZone = document.querySelector('body'); | 		const dropZone = document.querySelector('body'); | ||||||
| 
 | 
 | ||||||
|  | @ -663,6 +657,7 @@ | ||||||
| 
 | 
 | ||||||
| 						<textarea | 						<textarea | ||||||
| 							id="chat-textarea" | 							id="chat-textarea" | ||||||
|  | 							bind:this={chatTextAreaElement} | ||||||
| 							class=" dark:bg-gray-900 dark:text-gray-100 outline-none w-full py-3 px-3 {fileUploadEnabled | 							class=" dark:bg-gray-900 dark:text-gray-100 outline-none w-full py-3 px-3 {fileUploadEnabled | ||||||
| 								? '' | 								? '' | ||||||
| 								: ' pl-4'} rounded-xl resize-none h-[48px]" | 								: ' pl-4'} rounded-xl resize-none h-[48px]" | ||||||
|  |  | ||||||
|  | @ -40,7 +40,7 @@ | ||||||
| 
 | 
 | ||||||
| 	let edit = false; | 	let edit = false; | ||||||
| 	let editedContent = ''; | 	let editedContent = ''; | ||||||
| 
 | 	let editTextAreaElement: HTMLTextAreaElement; | ||||||
| 	let tooltipInstance = null; | 	let tooltipInstance = null; | ||||||
| 
 | 
 | ||||||
| 	let sentencesAudio = {}; | 	let sentencesAudio = {}; | ||||||
|  | @ -247,10 +247,9 @@ | ||||||
| 		editedContent = message.content; | 		editedContent = message.content; | ||||||
| 
 | 
 | ||||||
| 		await tick(); | 		await tick(); | ||||||
| 		const editElement = document.getElementById(`message-edit-${message.id}`); |  | ||||||
| 
 | 
 | ||||||
| 		editElement.style.height = ''; | 		editTextAreaElement.style.height = ''; | ||||||
| 		editElement.style.height = `${editElement.scrollHeight}px`; | 		editTextAreaElement.style.height = `${editTextAreaElement.scrollHeight}px`; | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	const editMessageConfirmHandler = async () => { | 	const editMessageConfirmHandler = async () => { | ||||||
|  | @ -341,6 +340,7 @@ | ||||||
| 							<div class=" w-full"> | 							<div class=" w-full"> | ||||||
| 								<textarea | 								<textarea | ||||||
| 									id="message-edit-{message.id}" | 									id="message-edit-{message.id}" | ||||||
|  | 									bind:this={editTextAreaElement} | ||||||
| 									class=" bg-transparent outline-none w-full resize-none" | 									class=" bg-transparent outline-none w-full resize-none" | ||||||
| 									bind:value={editedContent} | 									bind:value={editedContent} | ||||||
| 									on:input={(e) => { | 									on:input={(e) => { | ||||||
|  |  | ||||||
|  | @ -20,18 +20,17 @@ | ||||||
| 
 | 
 | ||||||
| 	let edit = false; | 	let edit = false; | ||||||
| 	let editedContent = ''; | 	let editedContent = ''; | ||||||
| 
 | 	let messageEditTextAreaElement: HTMLTextAreaElement; | ||||||
| 	const editMessageHandler = async () => { | 	const editMessageHandler = async () => { | ||||||
| 		edit = true; | 		edit = true; | ||||||
| 		editedContent = message.content; | 		editedContent = message.content; | ||||||
| 
 | 
 | ||||||
| 		await tick(); | 		await tick(); | ||||||
| 		const editElement = document.getElementById(`message-edit-${message.id}`); |  | ||||||
| 
 | 
 | ||||||
| 		editElement.style.height = ''; | 		messageEditTextAreaElement.style.height = ''; | ||||||
| 		editElement.style.height = `${editElement.scrollHeight}px`; | 		messageEditTextAreaElement.style.height = `${messageEditTextAreaElement.scrollHeight}px`; | ||||||
| 
 | 
 | ||||||
| 		editElement?.focus(); | 		messageEditTextAreaElement?.focus(); | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	const editMessageConfirmHandler = async () => { | 	const editMessageConfirmHandler = async () => { | ||||||
|  | @ -165,10 +164,11 @@ | ||||||
| 				<div class=" w-full"> | 				<div class=" w-full"> | ||||||
| 					<textarea | 					<textarea | ||||||
| 						id="message-edit-{message.id}" | 						id="message-edit-{message.id}" | ||||||
|  | 						bind:this={messageEditTextAreaElement} | ||||||
| 						class=" bg-transparent outline-none w-full resize-none" | 						class=" bg-transparent outline-none w-full resize-none" | ||||||
| 						bind:value={editedContent} | 						bind:value={editedContent} | ||||||
| 						on:input={(e) => { | 						on:input={(e) => { | ||||||
| 							e.target.style.height = `${e.target.scrollHeight}px`; | 							messageEditTextAreaElement.style.height = `${messageEditTextAreaElement.scrollHeight}px`; | ||||||
| 						}} | 						}} | ||||||
| 					/> | 					/> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -15,6 +15,7 @@ | ||||||
| 	let name = ''; | 	let name = ''; | ||||||
| 	let showJWTToken = false; | 	let showJWTToken = false; | ||||||
| 	let JWTTokenCopied = false; | 	let JWTTokenCopied = false; | ||||||
|  | 	let profileImageInputElement: HTMLInputElement; | ||||||
| 
 | 
 | ||||||
| 	const submitHandler = async () => { | 	const submitHandler = async () => { | ||||||
| 		const updatedUser = await updateUserProfile(localStorage.token, name, profileImageUrl).catch( | 		const updatedUser = await updateUserProfile(localStorage.token, name, profileImageUrl).catch( | ||||||
|  | @ -40,11 +41,12 @@ | ||||||
| 	<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80"> | 	<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80"> | ||||||
| 		<input | 		<input | ||||||
| 			id="profile-image-input" | 			id="profile-image-input" | ||||||
|  | 			bind:this={profileImageInputElement} | ||||||
| 			type="file" | 			type="file" | ||||||
| 			hidden | 			hidden | ||||||
| 			accept="image/*" | 			accept="image/*" | ||||||
| 			on:change={(e) => { | 			on:change={(e) => { | ||||||
| 				const files = e?.target?.files ?? []; | 				const files = profileImageInputElement.files ?? []; | ||||||
| 				let reader = new FileReader(); | 				let reader = new FileReader(); | ||||||
| 				reader.onload = (event) => { | 				reader.onload = (event) => { | ||||||
| 					let originalImageUrl = `${event.target.result}`; | 					let originalImageUrl = `${event.target.result}`; | ||||||
|  | @ -86,7 +88,7 @@ | ||||||
| 						// Display the compressed image | 						// Display the compressed image | ||||||
| 						profileImageUrl = compressedSrc; | 						profileImageUrl = compressedSrc; | ||||||
| 
 | 
 | ||||||
| 						e.target.files = null; | 						profileImageInputElement.files = null; | ||||||
| 					}; | 					}; | ||||||
| 				}; | 				}; | ||||||
| 
 | 
 | ||||||
|  | @ -107,9 +109,7 @@ | ||||||
| 					<button | 					<button | ||||||
| 						class="relative rounded-full dark:bg-gray-700" | 						class="relative rounded-full dark:bg-gray-700" | ||||||
| 						type="button" | 						type="button" | ||||||
| 						on:click={() => { | 						on:click={profileImageInputElement.click} | ||||||
| 							document.getElementById('profile-image-input')?.click(); |  | ||||||
| 						}} |  | ||||||
| 					> | 					> | ||||||
| 						<img | 						<img | ||||||
| 							src={profileImageUrl !== '' ? profileImageUrl : '/user.png'} | 							src={profileImageUrl !== '' ? profileImageUrl : '/user.png'} | ||||||
|  |  | ||||||
|  | @ -22,6 +22,7 @@ | ||||||
| 	let saveChatHistory = true; | 	let saveChatHistory = true; | ||||||
| 	let importFiles; | 	let importFiles; | ||||||
| 	let showDeleteConfirm = false; | 	let showDeleteConfirm = false; | ||||||
|  | 	let chatImportInputElement: HTMLInputElement; | ||||||
| 
 | 
 | ||||||
| 	$: if (importFiles) { | 	$: if (importFiles) { | ||||||
| 		console.log(importFiles); | 		console.log(importFiles); | ||||||
|  | @ -159,12 +160,17 @@ | ||||||
| 		<hr class=" dark:border-gray-700" /> | 		<hr class=" dark:border-gray-700" /> | ||||||
| 
 | 
 | ||||||
| 		<div class="flex flex-col"> | 		<div class="flex flex-col"> | ||||||
| 			<input id="chat-import-input" bind:files={importFiles} type="file" accept=".json" hidden /> | 			<input | ||||||
|  | 				id="chat-import-input" | ||||||
|  | 				bind:this={chatImportInputElement} | ||||||
|  | 				bind:files={importFiles} | ||||||
|  | 				type="file" | ||||||
|  | 				accept=".json" | ||||||
|  | 				hidden | ||||||
|  | 			/> | ||||||
| 			<button | 			<button | ||||||
| 				class=" flex rounded-md py-2 px-3.5 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition" | 				class=" flex rounded-md py-2 px-3.5 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition" | ||||||
| 				on:click={() => { | 				on:click={chatImportInputElement.click} | ||||||
| 					document.getElementById('chat-import-input').click(); |  | ||||||
| 				}} |  | ||||||
| 			> | 			> | ||||||
| 				<div class=" self-center mr-3"> | 				<div class=" self-center mr-3"> | ||||||
| 					<svg | 					<svg | ||||||
|  |  | ||||||
|  | @ -13,7 +13,7 @@ | ||||||
| 
 | 
 | ||||||
| 	let showLiteLLM = false; | 	let showLiteLLM = false; | ||||||
| 	let showLiteLLMParams = false; | 	let showLiteLLMParams = false; | ||||||
| 
 | 	let modelUploadInputElement: HTMLInputElement; | ||||||
| 	let liteLLMModelInfo = []; | 	let liteLLMModelInfo = []; | ||||||
| 
 | 
 | ||||||
| 	let liteLLMModel = ''; | 	let liteLLMModel = ''; | ||||||
|  | @ -546,6 +546,7 @@ | ||||||
| 									<div class="flex-1 {modelInputFile && modelInputFile.length > 0 ? 'mr-2' : ''}"> | 									<div class="flex-1 {modelInputFile && modelInputFile.length > 0 ? 'mr-2' : ''}"> | ||||||
| 										<input | 										<input | ||||||
| 											id="model-upload-input" | 											id="model-upload-input" | ||||||
|  | 											bind:this={modelUploadInputElement} | ||||||
| 											type="file" | 											type="file" | ||||||
| 											bind:files={modelInputFile} | 											bind:files={modelInputFile} | ||||||
| 											on:change={() => { | 											on:change={() => { | ||||||
|  | @ -559,9 +560,7 @@ | ||||||
| 										<button | 										<button | ||||||
| 											type="button" | 											type="button" | ||||||
| 											class="w-full rounded text-left py-2 px-4 dark:text-gray-300 dark:bg-gray-850" | 											class="w-full rounded text-left py-2 px-4 dark:text-gray-300 dark:bg-gray-850" | ||||||
| 											on:click={() => { | 											on:click={modelUploadInputElement.click} | ||||||
| 												document.getElementById('model-upload-input').click(); |  | ||||||
| 											}} |  | ||||||
| 										> | 										> | ||||||
| 											{#if modelInputFile && modelInputFile.length > 0} | 											{#if modelInputFile && modelInputFile.length > 0} | ||||||
| 												{modelInputFile[0].name} | 												{modelInputFile[0].name} | ||||||
|  |  | ||||||
|  | @ -15,7 +15,7 @@ | ||||||
| 
 | 
 | ||||||
| 	export let show = false; | 	export let show = false; | ||||||
| 	export let selectedDoc; | 	export let selectedDoc; | ||||||
| 
 | 	let uploadDocInputElement: HTMLInputElement; | ||||||
| 	let inputFiles; | 	let inputFiles; | ||||||
| 	let tags = []; | 	let tags = []; | ||||||
| 
 | 
 | ||||||
|  | @ -69,7 +69,7 @@ | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			inputFiles = null; | 			inputFiles = null; | ||||||
| 			document.getElementById('upload-doc-input').value = ''; | 			uploadDocInputElement.value = ''; | ||||||
| 		} else { | 		} else { | ||||||
| 			toast.error(`File not found.`); | 			toast.error(`File not found.`); | ||||||
| 		} | 		} | ||||||
|  | @ -126,14 +126,19 @@ | ||||||
| 					}} | 					}} | ||||||
| 				> | 				> | ||||||
| 					<div class="mb-3 w-full"> | 					<div class="mb-3 w-full"> | ||||||
| 						<input id="upload-doc-input" hidden bind:files={inputFiles} type="file" multiple /> | 						<input | ||||||
|  | 							id="upload-doc-input" | ||||||
|  | 							bind:this={uploadDocInputElement} | ||||||
|  | 							hidden | ||||||
|  | 							bind:files={inputFiles} | ||||||
|  | 							type="file" | ||||||
|  | 							multiple | ||||||
|  | 						/> | ||||||
| 
 | 
 | ||||||
| 						<button | 						<button | ||||||
| 							class="w-full text-sm font-medium py-3 bg-gray-850 hover:bg-gray-800 text-center rounded-xl" | 							class="w-full text-sm font-medium py-3 bg-gray-850 hover:bg-gray-800 text-center rounded-xl" | ||||||
| 							type="button" | 							type="button" | ||||||
| 							on:click={() => { | 							on:click={uploadDocInputElement.click} | ||||||
| 								document.getElementById('upload-doc-input')?.click(); |  | ||||||
| 							}} |  | ||||||
| 						> | 						> | ||||||
| 							{#if inputFiles} | 							{#if inputFiles} | ||||||
| 								{inputFiles.length > 0 ? `${inputFiles.length}` : ''} document(s) selected. | 								{inputFiles.length > 0 ? `${inputFiles.length}` : ''} document(s) selected. | ||||||
|  |  | ||||||
|  | @ -2,12 +2,11 @@ | ||||||
| 	import { onMount } from 'svelte'; | 	import { onMount } from 'svelte'; | ||||||
| 
 | 
 | ||||||
| 	export let messages = []; | 	export let messages = []; | ||||||
| 
 | 	let textAreaElement: HTMLTextAreaElement; | ||||||
| 	onMount(() => { | 	onMount(() => { | ||||||
| 		messages.forEach((message, idx) => { | 		messages.forEach((message, idx) => { | ||||||
| 			let textareaElement = document.getElementById(`${message.role}-${idx}-textarea`); | 			textAreaElement.style.height = ''; | ||||||
| 			textareaElement.style.height = ''; | 			textAreaElement.style.height = textAreaElement.scrollHeight + 'px'; | ||||||
| 			textareaElement.style.height = textareaElement.scrollHeight + 'px'; |  | ||||||
| 		}); | 		}); | ||||||
| 	}); | 	}); | ||||||
| </script> | </script> | ||||||
|  | @ -27,16 +26,17 @@ | ||||||
| 			<div class="flex-1"> | 			<div class="flex-1"> | ||||||
| 				<textarea | 				<textarea | ||||||
| 					id="{message.role}-{idx}-textarea" | 					id="{message.role}-{idx}-textarea" | ||||||
|  | 					bind:this={textAreaElement} | ||||||
| 					class="w-full bg-transparent outline-none rounded-lg p-2 text-sm resize-none overflow-hidden" | 					class="w-full bg-transparent outline-none rounded-lg p-2 text-sm resize-none overflow-hidden" | ||||||
| 					placeholder="Enter {message.role === 'user' ? 'a user' : 'an assistant'} message here" | 					placeholder="Enter {message.role === 'user' ? 'a user' : 'an assistant'} message here" | ||||||
| 					rows="1" | 					rows="1" | ||||||
| 					on:input={(e) => { | 					on:input={(e) => { | ||||||
| 						e.target.style.height = ''; | 						textAreaElement.style.height = ''; | ||||||
| 						e.target.style.height = e.target.scrollHeight + 'px'; | 						textAreaElement.style.height = textAreaElement.scrollHeight + 'px'; | ||||||
| 					}} | 					}} | ||||||
| 					on:focus={(e) => { | 					on:focus={(e) => { | ||||||
| 						e.target.style.height = ''; | 						textAreaElement.style.height = ''; | ||||||
| 						e.target.style.height = e.target.scrollHeight + 'px'; | 						textAreaElement.style.height = textAreaElement.scrollHeight + 'px'; | ||||||
| 
 | 
 | ||||||
| 						// e.target.style.height = Math.min(e.target.scrollHeight, 200) + 'px'; | 						// e.target.style.height = Math.min(e.target.scrollHeight, 200) + 'px'; | ||||||
| 					}} | 					}} | ||||||
|  |  | ||||||
|  | @ -37,7 +37,7 @@ | ||||||
| 
 | 
 | ||||||
| 	let ollamaVersion = ''; | 	let ollamaVersion = ''; | ||||||
| 	let loaded = false; | 	let loaded = false; | ||||||
| 
 | 	let showShortcutsButtonElement:HTMLButtonElement | ||||||
| 	let DB = null; | 	let DB = null; | ||||||
| 	let localDBChats = []; | 	let localDBChats = []; | ||||||
| 
 | 
 | ||||||
|  | @ -184,7 +184,7 @@ | ||||||
| 				if (isCtrlPressed && event.key === '/') { | 				if (isCtrlPressed && event.key === '/') { | ||||||
| 					event.preventDefault(); | 					event.preventDefault(); | ||||||
| 					console.log('showShortcuts'); | 					console.log('showShortcuts'); | ||||||
| 					document.getElementById('show-shortcuts-button')?.click(); | 					showShortcutsButtonElement.click(); | ||||||
| 				} | 				} | ||||||
| 			}); | 			}); | ||||||
| 
 | 
 | ||||||
|  | @ -203,6 +203,7 @@ | ||||||
| 	<div class=" hidden lg:flex fixed bottom-0 right-0 px-3 py-3 z-10"> | 	<div class=" hidden lg:flex fixed bottom-0 right-0 px-3 py-3 z-10"> | ||||||
| 		<button | 		<button | ||||||
| 			id="show-shortcuts-button" | 			id="show-shortcuts-button" | ||||||
|  | 			bind:this={showShortcutsButtonElement} | ||||||
| 			class="text-gray-600 dark:text-gray-300 bg-gray-300/20 w-6 h-6 flex items-center justify-center text-xs rounded-full" | 			class="text-gray-600 dark:text-gray-300 bg-gray-300/20 w-6 h-6 flex items-center justify-center text-xs rounded-full" | ||||||
| 			on:click={() => { | 			on:click={() => { | ||||||
| 				showShortcuts = !showShortcuts; | 				showShortcuts = !showShortcuts; | ||||||
|  |  | ||||||
|  | @ -42,7 +42,7 @@ | ||||||
| 	let stopResponseFlag = false; | 	let stopResponseFlag = false; | ||||||
| 	let autoScroll = true; | 	let autoScroll = true; | ||||||
| 	let processing = ''; | 	let processing = ''; | ||||||
| 
 | 	let messagesContainerElement: HTMLDivElement; | ||||||
| 	let currentRequestId = null; | 	let currentRequestId = null; | ||||||
| 
 | 
 | ||||||
| 	let selectedModels = ['']; | 	let selectedModels = ['']; | ||||||
|  | @ -140,8 +140,7 @@ | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	const scrollToBottom = () => { | 	const scrollToBottom = () => { | ||||||
| 		const element = document.getElementById('messages-container'); | 		messagesContainerElement.scrollTop = messagesContainerElement.scrollHeight; | ||||||
| 		element.scrollTop = element.scrollHeight; |  | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	////////////////////////// | 	////////////////////////// | ||||||
|  | @ -821,8 +820,11 @@ | ||||||
| 		<div | 		<div | ||||||
| 			class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0" | 			class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0" | ||||||
| 			id="messages-container" | 			id="messages-container" | ||||||
|  | 			bind:this={messagesContainerElement} | ||||||
| 			on:scroll={(e) => { | 			on:scroll={(e) => { | ||||||
| 				autoScroll = e.target.scrollHeight - e.target.scrollTop <= e.target.clientHeight + 50; | 				autoScroll = | ||||||
|  | 					messagesContainerElement.scrollHeight - messagesContainerElement.scrollTop <= | ||||||
|  | 					messagesContainerElement.clientHeight + 50; | ||||||
| 			}} | 			}} | ||||||
| 		> | 		> | ||||||
| 			<div | 			<div | ||||||
|  |  | ||||||
|  | @ -45,7 +45,7 @@ | ||||||
| 	let stopResponseFlag = false; | 	let stopResponseFlag = false; | ||||||
| 	let autoScroll = true; | 	let autoScroll = true; | ||||||
| 	let processing = ''; | 	let processing = ''; | ||||||
| 
 | 	let messagesContainerElement: HTMLDivElement; | ||||||
| 	let currentRequestId = null; | 	let currentRequestId = null; | ||||||
| 
 | 
 | ||||||
| 	// let chatId = $page.params.id; | 	// let chatId = $page.params.id; | ||||||
|  | @ -160,8 +160,7 @@ | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	const scrollToBottom = () => { | 	const scrollToBottom = () => { | ||||||
| 		const element = document.getElementById('messages-container'); | 		messagesContainerElement.scrollTop = messagesContainerElement.scrollHeight; | ||||||
| 		element.scrollTop = element.scrollHeight; |  | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	////////////////////////// | 	////////////////////////// | ||||||
|  | @ -852,8 +851,11 @@ | ||||||
| 			<div | 			<div | ||||||
| 				class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0" | 				class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0" | ||||||
| 				id="messages-container" | 				id="messages-container" | ||||||
|  | 				bind:this={messagesContainerElement} | ||||||
| 				on:scroll={(e) => { | 				on:scroll={(e) => { | ||||||
| 					autoScroll = e.target.scrollHeight - e.target.scrollTop <= e.target.clientHeight + 50; | 					autoScroll = | ||||||
|  | 						messagesContainerElement.scrollHeight - messagesContainerElement.scrollTop <= | ||||||
|  | 						messagesContainerElement.clientHeight + 50; | ||||||
| 				}} | 				}} | ||||||
| 			> | 			> | ||||||
| 				<div | 				<div | ||||||
|  |  | ||||||
|  | @ -21,7 +21,7 @@ | ||||||
| 
 | 
 | ||||||
| 	let inputFiles = ''; | 	let inputFiles = ''; | ||||||
| 	let query = ''; | 	let query = ''; | ||||||
| 
 | 	let documentsImportInputElement: HTMLInputElement; | ||||||
| 	let tags = []; | 	let tags = []; | ||||||
| 
 | 
 | ||||||
| 	let showSettingsModal = false; | 	let showSettingsModal = false; | ||||||
|  | @ -524,6 +524,7 @@ | ||||||
| 				<div class="flex space-x-2"> | 				<div class="flex space-x-2"> | ||||||
| 					<input | 					<input | ||||||
| 						id="documents-import-input" | 						id="documents-import-input" | ||||||
|  | 						bind:this={documentsImportInputElement} | ||||||
| 						bind:files={importFiles} | 						bind:files={importFiles} | ||||||
| 						type="file" | 						type="file" | ||||||
| 						accept=".json" | 						accept=".json" | ||||||
|  | @ -558,9 +559,7 @@ | ||||||
| 
 | 
 | ||||||
| 					<button | 					<button | ||||||
| 						class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition" | 						class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition" | ||||||
| 						on:click={async () => { | 						on:click={documentsImportInputElement.click} | ||||||
| 							document.getElementById('documents-import-input')?.click(); |  | ||||||
| 						}} |  | ||||||
| 					> | 					> | ||||||
| 						<div class=" self-center mr-2 font-medium">Import Documents Mapping</div> | 						<div class=" self-center mr-2 font-medium">Import Documents Mapping</div> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -16,7 +16,7 @@ | ||||||
| 
 | 
 | ||||||
| 	let localModelfiles = []; | 	let localModelfiles = []; | ||||||
| 	let importFiles; | 	let importFiles; | ||||||
| 
 | 	let modelfilesImportInputElement: HTMLInputElement; | ||||||
| 	const deleteModelHandler = async (tagName) => { | 	const deleteModelHandler = async (tagName) => { | ||||||
| 		let success = null; | 		let success = null; | ||||||
| 
 | 
 | ||||||
|  | @ -235,6 +235,7 @@ | ||||||
| 				<div class="flex space-x-1"> | 				<div class="flex space-x-1"> | ||||||
| 					<input | 					<input | ||||||
| 						id="modelfiles-import-input" | 						id="modelfiles-import-input" | ||||||
|  | 						bind:this={modelfilesImportInputElement} | ||||||
| 						bind:files={importFiles} | 						bind:files={importFiles} | ||||||
| 						type="file" | 						type="file" | ||||||
| 						accept=".json" | 						accept=".json" | ||||||
|  | @ -262,9 +263,7 @@ | ||||||
| 
 | 
 | ||||||
| 					<button | 					<button | ||||||
| 						class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition" | 						class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition" | ||||||
| 						on:click={async () => { | 						on:click={modelfilesImportInputElement.click} | ||||||
| 							document.getElementById('modelfiles-import-input')?.click(); |  | ||||||
| 						}} |  | ||||||
| 					> | 					> | ||||||
| 						<div class=" self-center mr-2 font-medium">Import Modelfiles</div> | 						<div class=" self-center mr-2 font-medium">Import Modelfiles</div> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| <script> | <script lang="ts"> | ||||||
| 	import { goto } from '$app/navigation'; | 	import { goto } from '$app/navigation'; | ||||||
| 
 | 
 | ||||||
| 	import { onMount, tick } from 'svelte'; | 	import { onMount, tick } from 'svelte'; | ||||||
|  | @ -21,7 +21,6 @@ | ||||||
| 
 | 
 | ||||||
| 	let mode = 'chat'; | 	let mode = 'chat'; | ||||||
| 	let loaded = false; | 	let loaded = false; | ||||||
| 
 |  | ||||||
| 	let text = ''; | 	let text = ''; | ||||||
| 
 | 
 | ||||||
| 	let selectedModelId = ''; | 	let selectedModelId = ''; | ||||||
|  | @ -30,6 +29,9 @@ | ||||||
| 	let currentRequestId; | 	let currentRequestId; | ||||||
| 	let stopResponseFlag = false; | 	let stopResponseFlag = false; | ||||||
| 
 | 
 | ||||||
|  | 	let messagesContainerElement: HTMLDivElement; | ||||||
|  | 	let textCompletionAreaElement: HTMLTextAreaElement; | ||||||
|  | 
 | ||||||
| 	let system = ''; | 	let system = ''; | ||||||
| 	let messages = [ | 	let messages = [ | ||||||
| 		{ | 		{ | ||||||
|  | @ -39,13 +41,7 @@ | ||||||
| 	]; | 	]; | ||||||
| 
 | 
 | ||||||
| 	const scrollToBottom = () => { | 	const scrollToBottom = () => { | ||||||
| 		let element; | 		const element = mode === 'chat' ? messagesContainerElement : textCompletionAreaElement; | ||||||
| 
 |  | ||||||
| 		if (mode === 'chat') { |  | ||||||
| 			element = document.getElementById('messages-container'); |  | ||||||
| 		} else { |  | ||||||
| 			element = document.getElementById('text-completion-textarea'); |  | ||||||
| 		} |  | ||||||
| 
 | 
 | ||||||
| 		if (element) { | 		if (element) { | ||||||
| 			element.scrollTop = element?.scrollHeight; | 			element.scrollTop = element?.scrollHeight; | ||||||
|  | @ -417,12 +413,14 @@ | ||||||
| 				<div | 				<div | ||||||
| 					class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0" | 					class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0" | ||||||
| 					id="messages-container" | 					id="messages-container" | ||||||
|  | 					bind:this={messagesContainerElement} | ||||||
| 				> | 				> | ||||||
| 					<div class=" h-full w-full flex flex-col"> | 					<div class=" h-full w-full flex flex-col"> | ||||||
| 						<div class="flex-1 p-1"> | 						<div class="flex-1 p-1"> | ||||||
| 							{#if mode === 'complete'} | 							{#if mode === 'complete'} | ||||||
| 								<textarea | 								<textarea | ||||||
| 									id="text-completion-textarea" | 									id="text-completion-textarea" | ||||||
|  | 									bind:this={textCompletionAreaElement} | ||||||
| 									class="w-full h-full p-3 bg-transparent outline outline-1 outline-gray-200 dark:outline-gray-800 resize-none rounded-lg text-sm" | 									class="w-full h-full p-3 bg-transparent outline outline-1 outline-gray-200 dark:outline-gray-800 resize-none rounded-lg text-sm" | ||||||
| 									bind:value={text} | 									bind:value={text} | ||||||
| 									placeholder="You're a helpful assistant." | 									placeholder="You're a helpful assistant." | ||||||
|  |  | ||||||
|  | @ -11,7 +11,7 @@ | ||||||
| 
 | 
 | ||||||
| 	let importFiles = ''; | 	let importFiles = ''; | ||||||
| 	let query = ''; | 	let query = ''; | ||||||
| 
 | 	let promptsImportInputElement: HTMLInputElement; | ||||||
| 	const sharePrompt = async (prompt) => { | 	const sharePrompt = async (prompt) => { | ||||||
| 		toast.success('Redirecting you to OpenWebUI Community'); | 		toast.success('Redirecting you to OpenWebUI Community'); | ||||||
| 
 | 
 | ||||||
|  | @ -208,6 +208,7 @@ | ||||||
| 				<div class="flex space-x-2"> | 				<div class="flex space-x-2"> | ||||||
| 					<input | 					<input | ||||||
| 						id="prompts-import-input" | 						id="prompts-import-input" | ||||||
|  | 						bind:this={promptsImportInputElement} | ||||||
| 						bind:files={importFiles} | 						bind:files={importFiles} | ||||||
| 						type="file" | 						type="file" | ||||||
| 						accept=".json" | 						accept=".json" | ||||||
|  | @ -241,9 +242,7 @@ | ||||||
| 
 | 
 | ||||||
| 					<button | 					<button | ||||||
| 						class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition" | 						class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition" | ||||||
| 						on:click={async () => { | 						on:click={promptsImportInputElement.click} | ||||||
| 							document.getElementById('prompts-import-input')?.click(); |  | ||||||
| 						}} |  | ||||||
| 					> | 					> | ||||||
| 						<div class=" self-center mr-2 font-medium">Import Prompts</div> | 						<div class=" self-center mr-2 font-medium">Import Prompts</div> | ||||||
| 
 | 
 | ||||||
|  | @ -266,7 +265,7 @@ | ||||||
| 					<button | 					<button | ||||||
| 						class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition" | 						class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition" | ||||||
| 						on:click={async () => { | 						on:click={async () => { | ||||||
| 							// document.getElementById('modelfiles-import-input')?.click(); | 							// promptsImportInputElement.click(); | ||||||
| 							let blob = new Blob([JSON.stringify($prompts)], { | 							let blob = new Blob([JSON.stringify($prompts)], { | ||||||
| 								type: 'application/json' | 								type: 'application/json' | ||||||
| 							}); | 							}); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Timothy Jaeryang Baek
						Timothy Jaeryang Baek