| 
									
										
										
										
											2023-11-20 09:47:07 +08:00
										 |  |  | <script lang="ts"> | 
					
						
							| 
									
										
										
										
											2024-03-01 17:18:07 +08:00
										 |  |  | 	import { toast } from 'svelte-sonner'; | 
					
						
							| 
									
										
										
										
											2024-10-27 03:56:37 +08:00
										 |  |  | 	import { v4 as uuidv4 } from 'uuid'; | 
					
						
							| 
									
										
										
										
											2024-12-16 07:43:06 +08:00
										 |  |  | 	import { createPicker, getAuthToken } from '$lib/utils/google-drive-picker'; | 
					
						
							| 
									
										
										
										
											2024-10-27 03:56:37 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-09 14:57:36 +08:00
										 |  |  | 	import { onMount, tick, getContext, createEventDispatcher, onDestroy } from 'svelte'; | 
					
						
							| 
									
										
										
										
											2024-08-23 22:42:36 +08:00
										 |  |  | 	const dispatch = createEventDispatcher(); | 
					
						
							| 
									
										
										
										
											2024-08-23 20:31:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-07 13:30:19 +08:00
										 |  |  | 	import { | 
					
						
							|  |  |  | 		type Model, | 
					
						
							|  |  |  | 		mobile, | 
					
						
							|  |  |  | 		settings, | 
					
						
							|  |  |  | 		showSidebar, | 
					
						
							|  |  |  | 		models, | 
					
						
							|  |  |  | 		config, | 
					
						
							| 
									
										
										
										
											2024-06-11 15:18:45 +08:00
										 |  |  | 		showCallOverlay, | 
					
						
							| 
									
										
										
										
											2024-06-12 06:29:46 +08:00
										 |  |  | 		tools, | 
					
						
							| 
									
										
										
										
											2024-10-05 18:07:56 +08:00
										 |  |  | 		user as _user, | 
					
						
							|  |  |  | 		showControls | 
					
						
							| 
									
										
										
										
											2024-06-07 13:30:19 +08:00
										 |  |  | 	} from '$lib/stores'; | 
					
						
							| 
									
										
										
										
											2024-11-17 08:51:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-25 14:28:14 +08:00
										 |  |  | 	import { blobToFile, compressImage, createMessagesList, findWordIndices } from '$lib/utils'; | 
					
						
							| 
									
										
										
										
											2024-08-27 21:58:02 +08:00
										 |  |  | 	import { transcribeAudio } from '$lib/apis/audio'; | 
					
						
							| 
									
										
										
										
											2024-06-19 04:50:18 +08:00
										 |  |  | 	import { uploadFile } from '$lib/apis/files'; | 
					
						
							| 
									
										
										
										
											2024-11-17 08:51:55 +08:00
										 |  |  | 	import { getTools } from '$lib/apis/tools'; | 
					
						
							| 
									
										
										
										
											2024-08-27 21:58:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-19 12:50:12 +08:00
										 |  |  | 	import { WEBUI_BASE_URL, WEBUI_API_BASE_URL, PASTED_TEXT_CHARACTER_LIMIT } from '$lib/constants'; | 
					
						
							| 
									
										
										
										
											2024-05-02 17:20:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-02 17:00:20 +08:00
										 |  |  | 	import Tooltip from '../common/Tooltip.svelte'; | 
					
						
							| 
									
										
										
										
											2024-05-28 04:22:24 +08:00
										 |  |  | 	import InputMenu from './MessageInput/InputMenu.svelte'; | 
					
						
							| 
									
										
										
										
											2024-06-07 11:33:23 +08:00
										 |  |  | 	import Headphone from '../icons/Headphone.svelte'; | 
					
						
							|  |  |  | 	import VoiceRecording from './MessageInput/VoiceRecording.svelte'; | 
					
						
							| 
									
										
										
										
											2024-07-17 17:39:37 +08:00
										 |  |  | 	import FileItem from '../common/FileItem.svelte'; | 
					
						
							| 
									
										
										
										
											2024-07-17 18:02:54 +08:00
										 |  |  | 	import FilesOverlay from './MessageInput/FilesOverlay.svelte'; | 
					
						
							| 
									
										
										
										
											2024-08-23 20:31:39 +08:00
										 |  |  | 	import Commands from './MessageInput/Commands.svelte'; | 
					
						
							|  |  |  | 	import XMark from '../icons/XMark.svelte'; | 
					
						
							| 
									
										
										
										
											2024-10-19 14:54:35 +08:00
										 |  |  | 	import RichTextInput from '../common/RichTextInput.svelte'; | 
					
						
							| 
									
										
										
										
											2024-11-29 16:16:49 +08:00
										 |  |  | 	import { generateAutoCompletion } from '$lib/apis'; | 
					
						
							|  |  |  | 	import { error, text } from '@sveltejs/kit'; | 
					
						
							| 
									
										
										
										
											2024-12-18 17:27:32 +08:00
										 |  |  | 	import Image from '../common/Image.svelte'; | 
					
						
							| 
									
										
										
										
											2025-01-14 01:21:00 +08:00
										 |  |  | 	import { deleteFileById } from '$lib/apis/files'; | 
					
						
							| 
									
										
										
										
											2024-01-02 17:18:14 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-01 12:40:36 +08:00
										 |  |  | 	const i18n = getContext('i18n'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-17 16:42:04 +08:00
										 |  |  | 	export let transparentBackground = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-22 00:16:29 +08:00
										 |  |  | 	export let onChange: Function = () => {}; | 
					
						
							| 
									
										
										
										
											2024-09-24 06:57:28 +08:00
										 |  |  | 	export let createMessagePair: Function; | 
					
						
							| 
									
										
										
										
											2023-11-20 09:47:07 +08:00
										 |  |  | 	export let stopResponse: Function; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-22 23:37:47 +08:00
										 |  |  | 	export let autoScroll = false; | 
					
						
							| 
									
										
										
										
											2024-05-25 13:21:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-23 10:40:01 +08:00
										 |  |  | 	export let atSelectedModel: Model | undefined = undefined; | 
					
						
							| 
									
										
										
										
											2024-05-19 18:46:24 +08:00
										 |  |  | 	export let selectedModels: ['']; | 
					
						
							| 
									
										
										
										
											2024-05-02 17:20:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-29 16:16:49 +08:00
										 |  |  | 	let selectedModelIds = []; | 
					
						
							|  |  |  | 	$: selectedModelIds = atSelectedModel !== undefined ? [atSelectedModel.id] : selectedModels; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-05 18:07:56 +08:00
										 |  |  | 	export let history; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	export let prompt = ''; | 
					
						
							|  |  |  | 	export let files = []; | 
					
						
							| 
									
										
										
										
											2024-11-17 09:49:13 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-05 18:07:56 +08:00
										 |  |  | 	export let selectedToolIds = []; | 
					
						
							| 
									
										
										
										
											2025-01-16 15:32:13 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	export let imageGenerationEnabled = false; | 
					
						
							| 
									
										
										
										
											2024-10-05 18:07:56 +08:00
										 |  |  | 	export let webSearchEnabled = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-22 00:16:29 +08:00
										 |  |  | 	$: onChange({ | 
					
						
							|  |  |  | 		prompt, | 
					
						
							|  |  |  | 		files, | 
					
						
							|  |  |  | 		selectedToolIds, | 
					
						
							| 
									
										
										
										
											2025-01-16 15:32:13 +08:00
										 |  |  | 		imageGenerationEnabled, | 
					
						
							| 
									
										
										
										
											2024-12-22 00:16:29 +08:00
										 |  |  | 		webSearchEnabled | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 	let loaded = false; | 
					
						
							| 
									
										
										
										
											2024-06-07 11:33:23 +08:00
										 |  |  | 	let recording = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-19 14:54:35 +08:00
										 |  |  | 	let chatInputContainerElement; | 
					
						
							| 
									
										
										
										
											2024-10-19 15:23:59 +08:00
										 |  |  | 	let chatInputElement; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-25 16:21:07 +08:00
										 |  |  | 	let filesInputElement; | 
					
						
							| 
									
										
										
										
											2024-08-23 20:31:39 +08:00
										 |  |  | 	let commandsElement; | 
					
						
							| 
									
										
										
										
											2024-01-02 16:55:28 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-25 16:21:07 +08:00
										 |  |  | 	let inputFiles; | 
					
						
							| 
									
										
										
										
											2023-12-20 06:50:43 +08:00
										 |  |  | 	let dragged = false; | 
					
						
							| 
									
										
										
										
											2023-11-25 16:21:07 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-10 14:47:31 +08:00
										 |  |  | 	let user = null; | 
					
						
							| 
									
										
										
										
											2024-10-05 18:07:56 +08:00
										 |  |  | 	export let placeholder = ''; | 
					
						
							| 
									
										
										
										
											2024-05-11 19:35:48 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-25 14:34:58 +08:00
										 |  |  | 	let visionCapableModels = []; | 
					
						
							|  |  |  | 	$: visionCapableModels = [...(atSelectedModel ? [atSelectedModel] : selectedModels)].filter( | 
					
						
							|  |  |  | 		(model) => $models.find((m) => m.id === model)?.info?.meta?.capabilities?.vision ?? true | 
					
						
							|  |  |  | 	); | 
					
						
							| 
									
										
										
										
											2024-05-19 18:46:24 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-16 08:20:46 +08:00
										 |  |  | 	const scrollToBottom = () => { | 
					
						
							|  |  |  | 		const element = document.getElementById('messages-container'); | 
					
						
							| 
									
										
										
										
											2024-08-22 23:37:47 +08:00
										 |  |  | 		element.scrollTo({ | 
					
						
							|  |  |  | 			top: element.scrollHeight, | 
					
						
							|  |  |  | 			behavior: 'smooth' | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2024-02-16 08:20:46 +08:00
										 |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-19 01:15:23 +08:00
										 |  |  | 	const screenCaptureHandler = async () => { | 
					
						
							|  |  |  | 		try { | 
					
						
							|  |  |  | 			// Request screen media | 
					
						
							|  |  |  | 			const mediaStream = await navigator.mediaDevices.getDisplayMedia({ | 
					
						
							|  |  |  | 				video: { cursor: 'never' }, | 
					
						
							|  |  |  | 				audio: false | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 			// Once the user selects a screen, temporarily create a video element | 
					
						
							|  |  |  | 			const video = document.createElement('video'); | 
					
						
							|  |  |  | 			video.srcObject = mediaStream; | 
					
						
							|  |  |  | 			// Ensure the video loads without affecting user experience or tab switching | 
					
						
							|  |  |  | 			await video.play(); | 
					
						
							|  |  |  | 			// Set up the canvas to match the video dimensions | 
					
						
							|  |  |  | 			const canvas = document.createElement('canvas'); | 
					
						
							|  |  |  | 			canvas.width = video.videoWidth; | 
					
						
							|  |  |  | 			canvas.height = video.videoHeight; | 
					
						
							|  |  |  | 			// Grab a single frame from the video stream using the canvas | 
					
						
							|  |  |  | 			const context = canvas.getContext('2d'); | 
					
						
							|  |  |  | 			context.drawImage(video, 0, 0, canvas.width, canvas.height); | 
					
						
							|  |  |  | 			// Stop all video tracks (stop screen sharing) after capturing the image | 
					
						
							|  |  |  | 			mediaStream.getTracks().forEach((track) => track.stop()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// bring back focus to this current tab, so that the user can see the screen capture | 
					
						
							|  |  |  | 			window.focus(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// Convert the canvas to a Base64 image URL | 
					
						
							|  |  |  | 			const imageUrl = canvas.toDataURL('image/png'); | 
					
						
							|  |  |  | 			// Add the captured image to the files array to render it | 
					
						
							|  |  |  | 			files = [...files, { type: 'image', url: imageUrl }]; | 
					
						
							|  |  |  | 			// Clean memory: Clear video srcObject | 
					
						
							|  |  |  | 			video.srcObject = null; | 
					
						
							|  |  |  | 		} catch (error) { | 
					
						
							|  |  |  | 			// Handle any errors (e.g., user cancels screen sharing) | 
					
						
							|  |  |  | 			console.error('Error capturing screen:', error); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-19 12:58:18 +08:00
										 |  |  | 	const uploadFileHandler = async (file, fullContext: boolean = false) => { | 
					
						
							| 
									
										
										
										
											2024-11-17 13:43:57 +08:00
										 |  |  | 		if ($_user?.role !== 'admin' && !($_user?.permissions?.chat?.file_upload ?? true)) { | 
					
						
							| 
									
										
										
										
											2024-11-17 13:31:57 +08:00
										 |  |  | 			toast.error($i18n.t('You do not have permission to upload files.')); | 
					
						
							|  |  |  | 			return null; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-27 03:56:37 +08:00
										 |  |  | 		const tempItemId = uuidv4(); | 
					
						
							| 
									
										
										
										
											2024-07-31 07:25:53 +08:00
										 |  |  | 		const fileItem = { | 
					
						
							|  |  |  | 			type: 'file', | 
					
						
							|  |  |  | 			file: '', | 
					
						
							| 
									
										
										
										
											2024-08-02 04:09:29 +08:00
										 |  |  | 			id: null, | 
					
						
							| 
									
										
										
										
											2024-07-31 07:25:53 +08:00
										 |  |  | 			url: '', | 
					
						
							|  |  |  | 			name: file.name, | 
					
						
							|  |  |  | 			collection_name: '', | 
					
						
							| 
									
										
										
										
											2024-10-06 01:18:43 +08:00
										 |  |  | 			status: 'uploading', | 
					
						
							| 
									
										
										
										
											2024-07-31 07:25:53 +08:00
										 |  |  | 			size: file.size, | 
					
						
							| 
									
										
										
										
											2024-10-27 03:56:37 +08:00
										 |  |  | 			error: '', | 
					
						
							| 
									
										
										
										
											2024-11-19 12:58:18 +08:00
										 |  |  | 			itemId: tempItemId, | 
					
						
							|  |  |  | 			...(fullContext ? { context: 'full' } : {}) | 
					
						
							| 
									
										
										
										
											2024-07-31 07:25:53 +08:00
										 |  |  | 		}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-27 03:56:37 +08:00
										 |  |  | 		if (fileItem.size == 0) { | 
					
						
							|  |  |  | 			toast.error($i18n.t('You cannot upload an empty file.')); | 
					
						
							|  |  |  | 			return null; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		files = [...files, fileItem]; | 
					
						
							| 
									
										
										
										
											2024-09-12 21:18:20 +08:00
										 |  |  | 		// Check if the file is an audio file and transcribe/convert it to text file | 
					
						
							| 
									
										
										
										
											2024-09-24 17:00:47 +08:00
										 |  |  | 		if (['audio/mpeg', 'audio/wav', 'audio/ogg', 'audio/x-m4a'].includes(file['type'])) { | 
					
						
							| 
									
										
										
										
											2024-09-12 21:18:20 +08:00
										 |  |  | 			const res = await transcribeAudio(localStorage.token, file).catch((error) => { | 
					
						
							| 
									
										
										
										
											2025-01-21 14:41:32 +08:00
										 |  |  | 				toast.error(`${error}`); | 
					
						
							| 
									
										
										
										
											2024-09-12 21:18:20 +08:00
										 |  |  | 				return null; | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (res) { | 
					
						
							|  |  |  | 				console.log(res); | 
					
						
							|  |  |  | 				const blob = new Blob([res.text], { type: 'text/plain' }); | 
					
						
							|  |  |  | 				file = blobToFile(blob, `${file.name}.txt`); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				fileItem.name = file.name; | 
					
						
							|  |  |  | 				fileItem.size = file.size; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-31 07:25:53 +08:00
										 |  |  | 		try { | 
					
						
							| 
									
										
										
										
											2024-10-04 13:22:22 +08:00
										 |  |  | 			// During the file upload, file content is automatically extracted. | 
					
						
							| 
									
										
										
										
											2024-07-31 07:25:53 +08:00
										 |  |  | 			const uploadedFile = await uploadFile(localStorage.token, file); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (uploadedFile) { | 
					
						
							| 
									
										
										
										
											2024-12-16 07:48:43 +08:00
										 |  |  | 				console.log('File upload completed:', { | 
					
						
							|  |  |  | 					id: uploadedFile.id, | 
					
						
							|  |  |  | 					name: fileItem.name, | 
					
						
							|  |  |  | 					collection: uploadedFile?.meta?.collection_name | 
					
						
							|  |  |  | 				}); | 
					
						
							| 
									
										
										
										
											2024-12-17 02:36:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-27 04:05:54 +08:00
										 |  |  | 				if (uploadedFile.error) { | 
					
						
							| 
									
										
										
										
											2024-12-16 07:48:43 +08:00
										 |  |  | 					console.warn('File upload warning:', uploadedFile.error); | 
					
						
							| 
									
										
										
										
											2024-10-27 04:05:54 +08:00
										 |  |  | 					toast.warning(uploadedFile.error); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-06 01:18:43 +08:00
										 |  |  | 				fileItem.status = 'uploaded'; | 
					
						
							| 
									
										
										
										
											2024-07-31 07:25:53 +08:00
										 |  |  | 				fileItem.file = uploadedFile; | 
					
						
							|  |  |  | 				fileItem.id = uploadedFile.id; | 
					
						
							| 
									
										
										
										
											2024-12-17 02:36:25 +08:00
										 |  |  | 				fileItem.collection_name = | 
					
						
							|  |  |  | 					uploadedFile?.meta?.collection_name || uploadedFile?.collection_name; | 
					
						
							| 
									
										
										
										
											2024-07-31 07:25:53 +08:00
										 |  |  | 				fileItem.url = `${WEBUI_API_BASE_URL}/files/${uploadedFile.id}`; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-04 13:22:22 +08:00
										 |  |  | 				files = files; | 
					
						
							| 
									
										
										
										
											2024-06-19 04:50:18 +08:00
										 |  |  | 			} else { | 
					
						
							| 
									
										
										
										
											2024-10-27 03:56:37 +08:00
										 |  |  | 				files = files.filter((item) => item?.itemId !== tempItemId); | 
					
						
							| 
									
										
										
										
											2024-02-11 17:06:25 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2024-08-02 04:09:29 +08:00
										 |  |  | 		} catch (e) { | 
					
						
							| 
									
										
										
										
											2025-01-30 13:56:28 +08:00
										 |  |  | 			toast.error(`${e}`); | 
					
						
							| 
									
										
										
										
											2024-10-27 03:56:37 +08:00
										 |  |  | 			files = files.filter((item) => item?.itemId !== tempItemId); | 
					
						
							| 
									
										
										
										
											2024-06-19 04:50:18 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	}; | 
					
						
							| 
									
										
										
										
											2024-10-04 14:41:17 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-27 21:51:40 +08:00
										 |  |  | 	const inputFilesHandler = async (inputFiles) => { | 
					
						
							| 
									
										
										
										
											2024-12-16 07:48:35 +08:00
										 |  |  | 		console.log('Input files handler called with:', inputFiles); | 
					
						
							| 
									
										
										
										
											2024-08-27 21:51:40 +08:00
										 |  |  | 		inputFiles.forEach((file) => { | 
					
						
							| 
									
										
										
										
											2024-12-16 07:48:35 +08:00
										 |  |  | 			console.log('Processing file:', { | 
					
						
							|  |  |  | 				name: file.name, | 
					
						
							|  |  |  | 				type: file.type, | 
					
						
							|  |  |  | 				size: file.size, | 
					
						
							|  |  |  | 				extension: file.name.split('.').at(-1) | 
					
						
							|  |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2024-08-27 23:05:24 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			if ( | 
					
						
							|  |  |  | 				($config?.file?.max_size ?? null) !== null && | 
					
						
							|  |  |  | 				file.size > ($config?.file?.max_size ?? 0) * 1024 * 1024 | 
					
						
							|  |  |  | 			) { | 
					
						
							| 
									
										
										
										
											2024-12-16 07:48:35 +08:00
										 |  |  | 				console.log('File exceeds max size limit:', { | 
					
						
							|  |  |  | 					fileSize: file.size, | 
					
						
							|  |  |  | 					maxSize: ($config?.file?.max_size ?? 0) * 1024 * 1024 | 
					
						
							|  |  |  | 				}); | 
					
						
							| 
									
										
										
										
											2024-08-27 23:05:24 +08:00
										 |  |  | 				toast.error( | 
					
						
							|  |  |  | 					$i18n.t(`File size should not exceed {{maxSize}} MB.`, { | 
					
						
							|  |  |  | 						maxSize: $config?.file?.max_size | 
					
						
							|  |  |  | 					}) | 
					
						
							|  |  |  | 				); | 
					
						
							|  |  |  | 				return; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-02 21:36:17 +08:00
										 |  |  | 			if (['image/gif', 'image/webp', 'image/jpeg', 'image/png'].includes(file['type'])) { | 
					
						
							|  |  |  | 				if (visionCapableModels.length === 0) { | 
					
						
							|  |  |  | 					toast.error($i18n.t('Selected model(s) do not support image inputs')); | 
					
						
							|  |  |  | 					return; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				let reader = new FileReader(); | 
					
						
							| 
									
										
										
										
											2024-12-25 14:28:14 +08:00
										 |  |  | 				reader.onload = async (event) => { | 
					
						
							|  |  |  | 					let imageUrl = event.target.result; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					if ($settings?.imageCompression ?? false) { | 
					
						
							|  |  |  | 						const width = $settings?.imageCompressionSize?.width ?? null; | 
					
						
							|  |  |  | 						const height = $settings?.imageCompressionSize?.height ?? null; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 						if (width || height) { | 
					
						
							|  |  |  | 							imageUrl = await compressImage(imageUrl, width, height); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-02 21:36:17 +08:00
										 |  |  | 					files = [ | 
					
						
							|  |  |  | 						...files, | 
					
						
							|  |  |  | 						{ | 
					
						
							|  |  |  | 							type: 'image', | 
					
						
							| 
									
										
										
										
											2024-12-25 14:28:14 +08:00
										 |  |  | 							url: `${imageUrl}` | 
					
						
							| 
									
										
										
										
											2024-08-02 21:36:17 +08:00
										 |  |  | 						} | 
					
						
							|  |  |  | 					]; | 
					
						
							|  |  |  | 				}; | 
					
						
							|  |  |  | 				reader.readAsDataURL(file); | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				uploadFileHandler(file); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2024-08-27 21:51:40 +08:00
										 |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2024-08-02 21:36:17 +08:00
										 |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-09 14:57:36 +08:00
										 |  |  | 	const handleKeyDown = (event: KeyboardEvent) => { | 
					
						
							|  |  |  | 		if (event.key === 'Escape') { | 
					
						
							|  |  |  | 			console.log('Escape'); | 
					
						
							|  |  |  | 			dragged = false; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}; | 
					
						
							| 
									
										
										
										
											2024-02-28 11:56:52 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-09 14:57:36 +08:00
										 |  |  | 	const onDragOver = (e) => { | 
					
						
							|  |  |  | 		e.preventDefault(); | 
					
						
							| 
									
										
										
										
											2024-10-15 12:03:55 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// Check if a file is being dragged. | 
					
						
							|  |  |  | 		if (e.dataTransfer?.types?.includes('Files')) { | 
					
						
							|  |  |  | 			dragged = true; | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			dragged = false; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-10-09 14:57:36 +08:00
										 |  |  | 	}; | 
					
						
							| 
									
										
										
										
											2024-04-07 16:03:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-09 14:57:36 +08:00
										 |  |  | 	const onDragLeave = () => { | 
					
						
							|  |  |  | 		dragged = false; | 
					
						
							|  |  |  | 	}; | 
					
						
							| 
									
										
										
										
											2024-01-08 17:12:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-09 14:57:36 +08:00
										 |  |  | 	const onDrop = async (e) => { | 
					
						
							|  |  |  | 		e.preventDefault(); | 
					
						
							|  |  |  | 		console.log(e); | 
					
						
							| 
									
										
										
										
											2023-12-20 06:50:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-09 14:57:36 +08:00
										 |  |  | 		if (e.dataTransfer?.files) { | 
					
						
							|  |  |  | 			const inputFiles = Array.from(e.dataTransfer?.files); | 
					
						
							|  |  |  | 			if (inputFiles && inputFiles.length > 0) { | 
					
						
							|  |  |  | 				console.log(inputFiles); | 
					
						
							|  |  |  | 				inputFilesHandler(inputFiles); | 
					
						
							| 
									
										
										
										
											2023-12-20 06:50:43 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2024-10-09 14:57:36 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-12-20 06:50:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-09 14:57:36 +08:00
										 |  |  | 		dragged = false; | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 	onMount(async () => { | 
					
						
							|  |  |  | 		loaded = true; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-19 14:54:35 +08:00
										 |  |  | 		window.setTimeout(() => { | 
					
						
							|  |  |  | 			const chatInput = document.getElementById('chat-input'); | 
					
						
							|  |  |  | 			chatInput?.focus(); | 
					
						
							|  |  |  | 		}, 0); | 
					
						
							| 
									
										
										
										
											2023-12-20 06:53:14 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-07 16:03:16 +08:00
										 |  |  | 		window.addEventListener('keydown', handleKeyDown); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-18 14:06:58 +08:00
										 |  |  | 		await tick(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		const dropzoneElement = document.getElementById('chat-container'); | 
					
						
							| 
									
										
										
										
											2024-10-09 14:57:36 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-18 14:06:58 +08:00
										 |  |  | 		dropzoneElement?.addEventListener('dragover', onDragOver); | 
					
						
							|  |  |  | 		dropzoneElement?.addEventListener('drop', onDrop); | 
					
						
							|  |  |  | 		dropzoneElement?.addEventListener('dragleave', onDragLeave); | 
					
						
							| 
									
										
										
										
											2024-10-09 14:57:36 +08:00
										 |  |  | 	}); | 
					
						
							| 
									
										
										
										
											2024-01-08 17:12:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-09 14:57:36 +08:00
										 |  |  | 	onDestroy(() => { | 
					
						
							| 
									
										
										
										
											2024-11-17 13:50:31 +08:00
										 |  |  | 		console.log('destroy'); | 
					
						
							| 
									
										
										
										
											2024-10-09 14:57:36 +08:00
										 |  |  | 		window.removeEventListener('keydown', handleKeyDown); | 
					
						
							| 
									
										
										
										
											2024-04-07 16:03:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-18 14:06:58 +08:00
										 |  |  | 		const dropzoneElement = document.getElementById('chat-container'); | 
					
						
							| 
									
										
										
										
											2024-10-09 14:57:36 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-17 13:50:31 +08:00
										 |  |  | 		if (dropzoneElement) { | 
					
						
							|  |  |  | 			dropzoneElement?.removeEventListener('dragover', onDragOver); | 
					
						
							|  |  |  | 			dropzoneElement?.removeEventListener('drop', onDrop); | 
					
						
							|  |  |  | 			dropzoneElement?.removeEventListener('dragleave', onDragLeave); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-12-20 06:50:43 +08:00
										 |  |  | 	}); | 
					
						
							| 
									
										
										
										
											2023-11-20 09:47:07 +08:00
										 |  |  | </script> | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-17 18:02:54 +08:00
										 |  |  | <FilesOverlay show={dragged} /> | 
					
						
							| 
									
										
										
										
											2023-12-20 06:50:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | {#if loaded} | 
					
						
							|  |  |  | 	<div class="w-full font-primary"> | 
					
						
							| 
									
										
										
										
											2024-12-02 15:29:21 +08:00
										 |  |  | 		<div class=" mx-auto inset-x-0 bg-transparent flex justify-center"> | 
					
						
							| 
									
										
										
										
											2024-12-25 12:30:30 +08:00
										 |  |  | 			<div | 
					
						
							|  |  |  | 				class="flex flex-col px-3 {($settings?.widescreenMode ?? null) | 
					
						
							|  |  |  | 					? 'max-w-full' | 
					
						
							|  |  |  | 					: 'max-w-6xl'} w-full" | 
					
						
							|  |  |  | 			> | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 				<div class="relative"> | 
					
						
							|  |  |  | 					{#if autoScroll === false && history?.currentId} | 
					
						
							|  |  |  | 						<div | 
					
						
							|  |  |  | 							class=" absolute -top-12 left-0 right-0 flex justify-center z-30 pointer-events-none" | 
					
						
							| 
									
										
										
										
											2024-06-02 09:11:54 +08:00
										 |  |  | 						> | 
					
						
							|  |  |  | 							<button | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 								class=" bg-white border border-gray-100 dark:border-none dark:bg-white/20 p-1.5 rounded-full pointer-events-auto" | 
					
						
							| 
									
										
										
										
											2024-06-02 09:11:54 +08:00
										 |  |  | 								on:click={() => { | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 									autoScroll = true; | 
					
						
							|  |  |  | 									scrollToBottom(); | 
					
						
							| 
									
										
										
										
											2024-06-02 09:11:54 +08:00
										 |  |  | 								}} | 
					
						
							|  |  |  | 							> | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 								<svg | 
					
						
							|  |  |  | 									xmlns="http://www.w3.org/2000/svg" | 
					
						
							|  |  |  | 									viewBox="0 0 20 20" | 
					
						
							|  |  |  | 									fill="currentColor" | 
					
						
							|  |  |  | 									class="w-5 h-5" | 
					
						
							|  |  |  | 								> | 
					
						
							|  |  |  | 									<path | 
					
						
							|  |  |  | 										fill-rule="evenodd" | 
					
						
							|  |  |  | 										d="M10 3a.75.75 0 01.75.75v10.638l3.96-4.158a.75.75 0 111.08 1.04l-5.25 5.5a.75.75 0 01-1.08 0l-5.25-5.5a.75.75 0 111.08-1.04l3.96 4.158V3.75A.75.75 0 0110 3z" | 
					
						
							|  |  |  | 										clip-rule="evenodd" | 
					
						
							| 
									
										
										
										
											2024-11-11 11:11:06 +08:00
										 |  |  | 									/> | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 								</svg> | 
					
						
							| 
									
										
										
										
											2024-06-02 09:11:54 +08:00
										 |  |  | 							</button> | 
					
						
							|  |  |  | 						</div> | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 					{/if} | 
					
						
							|  |  |  | 				</div> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				<div class="w-full relative"> | 
					
						
							| 
									
										
										
										
											2025-01-16 15:39:29 +08:00
										 |  |  | 					{#if atSelectedModel !== undefined || selectedToolIds.length > 0 || webSearchEnabled || imageGenerationEnabled} | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 						<div | 
					
						
							| 
									
										
										
										
											2024-12-02 15:16:00 +08:00
										 |  |  | 							class="px-3 pb-0.5 pt-1.5 text-left w-full flex flex-col absolute bottom-0 left-0 right-0 bg-gradient-to-t from-white dark:from-gray-900 z-10" | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 						> | 
					
						
							|  |  |  | 							{#if selectedToolIds.length > 0} | 
					
						
							|  |  |  | 								<div class="flex items-center justify-between w-full"> | 
					
						
							| 
									
										
										
										
											2024-11-19 19:04:49 +08:00
										 |  |  | 									<div class="flex items-center gap-2.5 text-sm dark:text-gray-500"> | 
					
						
							|  |  |  | 										<div class="pl-1"> | 
					
						
							|  |  |  | 											<span class="relative flex size-2"> | 
					
						
							|  |  |  | 												<span | 
					
						
							|  |  |  | 													class="animate-ping absolute inline-flex h-full w-full rounded-full bg-yellow-400 opacity-75" | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 												/> | 
					
						
							| 
									
										
										
										
											2024-11-19 19:04:49 +08:00
										 |  |  | 												<span class="relative inline-flex rounded-full size-2 bg-yellow-500" /> | 
					
						
							|  |  |  | 											</span> | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 										</div> | 
					
						
							| 
									
										
										
										
											2025-01-16 15:39:29 +08:00
										 |  |  | 										<div class="  text-ellipsis line-clamp-1 flex"> | 
					
						
							| 
									
										
										
										
											2024-11-20 09:10:43 +08:00
										 |  |  | 											{#each selectedToolIds.map((id) => { | 
					
						
							|  |  |  | 												return $tools ? $tools.find((t) => t.id === id) : { id: id, name: id }; | 
					
						
							|  |  |  | 											}) as tool, toolIdx (toolIdx)} | 
					
						
							|  |  |  | 												<Tooltip | 
					
						
							|  |  |  | 													content={tool?.meta?.description ?? ''} | 
					
						
							|  |  |  | 													className=" {toolIdx !== 0 ? 'pl-0.5' : ''} flex-shrink-0" | 
					
						
							|  |  |  | 													placement="top" | 
					
						
							|  |  |  | 												> | 
					
						
							|  |  |  | 													{tool.name} | 
					
						
							|  |  |  | 												</Tooltip> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 												{#if toolIdx !== selectedToolIds.length - 1} | 
					
						
							|  |  |  | 													<span>, </span> | 
					
						
							|  |  |  | 												{/if} | 
					
						
							|  |  |  | 											{/each} | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 										</div> | 
					
						
							|  |  |  | 									</div> | 
					
						
							| 
									
										
										
										
											2024-11-13 12:33:12 +08:00
										 |  |  | 								</div> | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 							{/if} | 
					
						
							| 
									
										
										
										
											2024-11-13 12:33:12 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-16 15:39:29 +08:00
										 |  |  | 							{#if imageGenerationEnabled} | 
					
						
							|  |  |  | 								<div class="flex items-center justify-between w-full"> | 
					
						
							|  |  |  | 									<div class="flex items-center gap-2.5 text-sm dark:text-gray-500"> | 
					
						
							|  |  |  | 										<div class="pl-1"> | 
					
						
							|  |  |  | 											<span class="relative flex size-2"> | 
					
						
							|  |  |  | 												<span | 
					
						
							|  |  |  | 													class="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75" | 
					
						
							|  |  |  | 												/> | 
					
						
							|  |  |  | 												<span class="relative inline-flex rounded-full size-2 bg-green-500" /> | 
					
						
							|  |  |  | 											</span> | 
					
						
							|  |  |  | 										</div> | 
					
						
							|  |  |  | 										<div class=" ">{$i18n.t('Image generation')}</div> | 
					
						
							|  |  |  | 									</div> | 
					
						
							|  |  |  | 								</div> | 
					
						
							|  |  |  | 							{/if} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-19 01:15:23 +08:00
										 |  |  | 							{#if webSearchEnabled} | 
					
						
							|  |  |  | 								<div class="flex items-center justify-between w-full"> | 
					
						
							|  |  |  | 									<div class="flex items-center gap-2.5 text-sm dark:text-gray-500"> | 
					
						
							|  |  |  | 										<div class="pl-1"> | 
					
						
							|  |  |  | 											<span class="relative flex size-2"> | 
					
						
							|  |  |  | 												<span | 
					
						
							|  |  |  | 													class="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75" | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 												/> | 
					
						
							| 
									
										
										
										
											2024-11-19 19:04:49 +08:00
										 |  |  | 												<span class="relative inline-flex rounded-full size-2 bg-green-500" /> | 
					
						
							|  |  |  | 											</span> | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 										</div> | 
					
						
							| 
									
										
										
										
											2025-01-16 15:39:29 +08:00
										 |  |  | 										<div class=" ">{$i18n.t('Search the web')}</div> | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 									</div> | 
					
						
							| 
									
										
										
										
											2024-11-19 19:04:49 +08:00
										 |  |  | 								</div> | 
					
						
							|  |  |  | 							{/if} | 
					
						
							| 
									
										
										
										
											2024-08-23 20:31:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-19 19:04:49 +08:00
										 |  |  | 							{#if atSelectedModel !== undefined} | 
					
						
							|  |  |  | 								<div class="flex items-center justify-between w-full"> | 
					
						
							|  |  |  | 									<div class="pl-[1px] flex items-center gap-2 text-sm dark:text-gray-500"> | 
					
						
							|  |  |  | 										<img | 
					
						
							|  |  |  | 											crossorigin="anonymous" | 
					
						
							|  |  |  | 											alt="model profile" | 
					
						
							|  |  |  | 											class="size-3.5 max-w-[28px] object-cover rounded-full" | 
					
						
							|  |  |  | 											src={$models.find((model) => model.id === atSelectedModel.id)?.info?.meta | 
					
						
							|  |  |  | 												?.profile_image_url ?? | 
					
						
							|  |  |  | 												($i18n.language === 'dg-DG' | 
					
						
							|  |  |  | 													? `/doge.png` | 
					
						
							|  |  |  | 													: `${WEBUI_BASE_URL}/static/favicon.png`)} | 
					
						
							|  |  |  | 										/> | 
					
						
							|  |  |  | 										<div class="translate-y-[0.5px]"> | 
					
						
							|  |  |  | 											Talking to <span class=" font-medium">{atSelectedModel.name}</span> | 
					
						
							|  |  |  | 										</div> | 
					
						
							|  |  |  | 									</div> | 
					
						
							| 
									
										
										
										
											2024-11-11 11:11:06 +08:00
										 |  |  | 									<div> | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 										<button | 
					
						
							|  |  |  | 											class="flex items-center dark:text-gray-500" | 
					
						
							|  |  |  | 											on:click={() => { | 
					
						
							| 
									
										
										
										
											2024-11-19 19:04:49 +08:00
										 |  |  | 												atSelectedModel = undefined; | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 											}} | 
					
						
							| 
									
										
										
										
											2024-11-11 11:11:06 +08:00
										 |  |  | 										> | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 											<XMark /> | 
					
						
							|  |  |  | 										</button> | 
					
						
							| 
									
										
										
										
											2024-11-11 11:11:06 +08:00
										 |  |  | 									</div> | 
					
						
							|  |  |  | 								</div> | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 							{/if} | 
					
						
							|  |  |  | 						</div> | 
					
						
							|  |  |  | 					{/if} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					<Commands | 
					
						
							|  |  |  | 						bind:this={commandsElement} | 
					
						
							|  |  |  | 						bind:prompt | 
					
						
							|  |  |  | 						bind:files | 
					
						
							|  |  |  | 						on:upload={(e) => { | 
					
						
							|  |  |  | 							dispatch('upload', e.detail); | 
					
						
							|  |  |  | 						}} | 
					
						
							|  |  |  | 						on:select={(e) => { | 
					
						
							|  |  |  | 							const data = e.detail; | 
					
						
							| 
									
										
										
										
											2024-08-23 20:31:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 							if (data?.type === 'model') { | 
					
						
							|  |  |  | 								atSelectedModel = data.data; | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 							const chatInputElement = document.getElementById('chat-input'); | 
					
						
							|  |  |  | 							chatInputElement?.focus(); | 
					
						
							|  |  |  | 						}} | 
					
						
							|  |  |  | 					/> | 
					
						
							|  |  |  | 				</div> | 
					
						
							| 
									
										
										
										
											2023-12-14 08:21:50 +08:00
										 |  |  | 			</div> | 
					
						
							| 
									
										
										
										
											2024-01-02 16:55:28 +08:00
										 |  |  | 		</div> | 
					
						
							| 
									
										
										
										
											2024-05-02 17:20:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 		<div class="{transparentBackground ? 'bg-transparent' : 'bg-white dark:bg-gray-900'} "> | 
					
						
							| 
									
										
										
										
											2024-12-25 11:20:38 +08:00
										 |  |  | 			<div | 
					
						
							|  |  |  | 				class="{($settings?.widescreenMode ?? null) | 
					
						
							|  |  |  | 					? 'max-w-full' | 
					
						
							|  |  |  | 					: 'max-w-6xl'} px-2.5 mx-auto inset-x-0" | 
					
						
							|  |  |  | 			> | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 				<div class=""> | 
					
						
							|  |  |  | 					<input | 
					
						
							|  |  |  | 						bind:this={filesInputElement} | 
					
						
							|  |  |  | 						bind:files={inputFiles} | 
					
						
							|  |  |  | 						type="file" | 
					
						
							|  |  |  | 						hidden | 
					
						
							|  |  |  | 						multiple | 
					
						
							|  |  |  | 						on:change={async () => { | 
					
						
							|  |  |  | 							if (inputFiles && inputFiles.length > 0) { | 
					
						
							|  |  |  | 								const _inputFiles = Array.from(inputFiles); | 
					
						
							|  |  |  | 								inputFilesHandler(_inputFiles); | 
					
						
							|  |  |  | 							} else { | 
					
						
							|  |  |  | 								toast.error($i18n.t(`File not found.`)); | 
					
						
							|  |  |  | 							} | 
					
						
							| 
									
										
										
										
											2024-06-19 04:50:18 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 							filesInputElement.value = ''; | 
					
						
							|  |  |  | 						}} | 
					
						
							|  |  |  | 					/> | 
					
						
							| 
									
										
										
										
											2024-06-07 11:33:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 					{#if recording} | 
					
						
							|  |  |  | 						<VoiceRecording | 
					
						
							|  |  |  | 							bind:recording | 
					
						
							|  |  |  | 							on:cancel={async () => { | 
					
						
							|  |  |  | 								recording = false; | 
					
						
							| 
									
										
										
										
											2024-06-07 11:33:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 								await tick(); | 
					
						
							|  |  |  | 								document.getElementById('chat-input')?.focus(); | 
					
						
							|  |  |  | 							}} | 
					
						
							|  |  |  | 							on:confirm={async (e) => { | 
					
						
							|  |  |  | 								const { text, filename } = e.detail; | 
					
						
							|  |  |  | 								prompt = `${prompt}${text} `; | 
					
						
							| 
									
										
										
										
											2024-06-07 11:33:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 								recording = false; | 
					
						
							| 
									
										
										
										
											2024-06-07 11:33:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 								await tick(); | 
					
						
							|  |  |  | 								document.getElementById('chat-input')?.focus(); | 
					
						
							| 
									
										
										
										
											2024-06-07 12:56:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 								if ($settings?.speechAutoSend ?? false) { | 
					
						
							|  |  |  | 									dispatch('submit', prompt); | 
					
						
							|  |  |  | 								} | 
					
						
							|  |  |  | 							}} | 
					
						
							|  |  |  | 						/> | 
					
						
							|  |  |  | 					{:else} | 
					
						
							|  |  |  | 						<form | 
					
						
							|  |  |  | 							class="w-full flex gap-1.5" | 
					
						
							|  |  |  | 							on:submit|preventDefault={() => { | 
					
						
							|  |  |  | 								// check if selectedModels support image input | 
					
						
							| 
									
										
										
										
											2024-10-05 18:07:56 +08:00
										 |  |  | 								dispatch('submit', prompt); | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 							}} | 
					
						
							| 
									
										
										
										
											2024-06-07 11:33:23 +08:00
										 |  |  | 						> | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 							<div | 
					
						
							| 
									
										
										
										
											2024-12-31 16:51:43 +08:00
										 |  |  | 								class="flex-1 flex flex-col relative w-full rounded-3xl px-1 bg-gray-600/5 dark:bg-gray-400/5 dark:text-gray-100" | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 								dir={$settings?.chatDirection ?? 'LTR'} | 
					
						
							|  |  |  | 							> | 
					
						
							|  |  |  | 								{#if files.length > 0} | 
					
						
							| 
									
										
										
										
											2025-01-23 03:04:12 +08:00
										 |  |  | 									<div class="mx-1 mt-2.5 mb-1 flex items-center flex-wrap gap-2"> | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 										{#each files as file, fileIdx} | 
					
						
							|  |  |  | 											{#if file.type === 'image'} | 
					
						
							|  |  |  | 												<div class=" relative group"> | 
					
						
							| 
									
										
										
										
											2025-01-23 03:04:12 +08:00
										 |  |  | 													<div class="relative flex items-center"> | 
					
						
							| 
									
										
										
										
											2024-12-18 17:27:32 +08:00
										 |  |  | 														<Image | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 															src={file.url} | 
					
						
							|  |  |  | 															alt="input" | 
					
						
							| 
									
										
										
										
											2025-01-23 03:04:12 +08:00
										 |  |  | 															imageClassName=" size-14 rounded-xl object-cover" | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 														/> | 
					
						
							|  |  |  | 														{#if atSelectedModel ? visionCapableModels.length === 0 : selectedModels.length !== visionCapableModels.length} | 
					
						
							|  |  |  | 															<Tooltip | 
					
						
							|  |  |  | 																className=" absolute top-1 left-1" | 
					
						
							|  |  |  | 																content={$i18n.t('{{ models }}', { | 
					
						
							|  |  |  | 																	models: [ | 
					
						
							|  |  |  | 																		...(atSelectedModel ? [atSelectedModel] : selectedModels) | 
					
						
							|  |  |  | 																	] | 
					
						
							|  |  |  | 																		.filter((id) => !visionCapableModels.includes(id)) | 
					
						
							|  |  |  | 																		.join(', ') | 
					
						
							|  |  |  | 																})} | 
					
						
							|  |  |  | 															> | 
					
						
							|  |  |  | 																<svg | 
					
						
							|  |  |  | 																	xmlns="http://www.w3.org/2000/svg" | 
					
						
							|  |  |  | 																	viewBox="0 0 24 24" | 
					
						
							|  |  |  | 																	fill="currentColor" | 
					
						
							|  |  |  | 																	class="size-4 fill-yellow-300" | 
					
						
							|  |  |  | 																> | 
					
						
							|  |  |  | 																	<path | 
					
						
							|  |  |  | 																		fill-rule="evenodd" | 
					
						
							|  |  |  | 																		d="M9.401 3.003c1.155-2 4.043-2 5.197 0l7.355 12.748c1.154 2-.29 4.5-2.599 4.5H4.645c-2.309 0-3.752-2.5-2.598-4.5L9.4 3.003ZM12 8.25a.75.75 0 0 1 .75.75v3.75a.75.75 0 0 1-1.5 0V9a.75.75 0 0 1 .75-.75Zm0 8.25a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Z" | 
					
						
							|  |  |  | 																		clip-rule="evenodd" | 
					
						
							|  |  |  | 																	/> | 
					
						
							|  |  |  | 																</svg> | 
					
						
							|  |  |  | 															</Tooltip> | 
					
						
							|  |  |  | 														{/if} | 
					
						
							|  |  |  | 													</div> | 
					
						
							|  |  |  | 													<div class=" absolute -top-1 -right-1"> | 
					
						
							|  |  |  | 														<button | 
					
						
							|  |  |  | 															class=" bg-gray-400 text-white border border-white rounded-full group-hover:visible invisible transition" | 
					
						
							|  |  |  | 															type="button" | 
					
						
							|  |  |  | 															on:click={() => { | 
					
						
							|  |  |  | 																files.splice(fileIdx, 1); | 
					
						
							|  |  |  | 																files = files; | 
					
						
							|  |  |  | 															}} | 
					
						
							| 
									
										
										
										
											2024-06-07 11:33:23 +08:00
										 |  |  | 														> | 
					
						
							|  |  |  | 															<svg | 
					
						
							|  |  |  | 																xmlns="http://www.w3.org/2000/svg" | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 																viewBox="0 0 20 20" | 
					
						
							| 
									
										
										
										
											2024-06-07 11:33:23 +08:00
										 |  |  | 																fill="currentColor" | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 																class="w-4 h-4" | 
					
						
							| 
									
										
										
										
											2024-06-07 11:33:23 +08:00
										 |  |  | 															> | 
					
						
							|  |  |  | 																<path | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 																	d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" | 
					
						
							| 
									
										
										
										
											2024-06-07 11:33:23 +08:00
										 |  |  | 																/> | 
					
						
							|  |  |  | 															</svg> | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 														</button> | 
					
						
							|  |  |  | 													</div> | 
					
						
							| 
									
										
										
										
											2024-06-07 11:33:23 +08:00
										 |  |  | 												</div> | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 											{:else} | 
					
						
							|  |  |  | 												<FileItem | 
					
						
							|  |  |  | 													item={file} | 
					
						
							|  |  |  | 													name={file.name} | 
					
						
							|  |  |  | 													type={file.type} | 
					
						
							|  |  |  | 													size={file?.size} | 
					
						
							|  |  |  | 													loading={file.status === 'uploading'} | 
					
						
							|  |  |  | 													dismissible={true} | 
					
						
							|  |  |  | 													edit={true} | 
					
						
							| 
									
										
										
										
											2025-01-14 01:21:00 +08:00
										 |  |  | 													on:dismiss={async () => { | 
					
						
							| 
									
										
										
										
											2025-01-23 03:04:12 +08:00
										 |  |  | 														if (file.type !== 'collection' && !file?.collection) { | 
					
						
							| 
									
										
										
										
											2025-01-14 01:21:00 +08:00
										 |  |  | 															if (file.id) { | 
					
						
							|  |  |  | 																// This will handle both file deletion and Chroma cleanup | 
					
						
							|  |  |  | 																await deleteFileById(localStorage.token, file.id); | 
					
						
							|  |  |  | 															} | 
					
						
							|  |  |  | 														} | 
					
						
							| 
									
										
										
										
											2025-01-23 03:04:12 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 														// Remove from UI state | 
					
						
							|  |  |  | 														files.splice(fileIdx, 1); | 
					
						
							|  |  |  | 														files = files; | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 													}} | 
					
						
							|  |  |  | 													on:click={() => { | 
					
						
							|  |  |  | 														console.log(file); | 
					
						
							|  |  |  | 													}} | 
					
						
							|  |  |  | 												/> | 
					
						
							|  |  |  | 											{/if} | 
					
						
							|  |  |  | 										{/each} | 
					
						
							|  |  |  | 									</div> | 
					
						
							|  |  |  | 								{/if} | 
					
						
							| 
									
										
										
										
											2024-06-02 09:11:54 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 								<div class=" flex"> | 
					
						
							| 
									
										
										
										
											2024-12-02 15:35:24 +08:00
										 |  |  | 									<div class="ml-1 self-end mb-1.5 flex space-x-1"> | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 										<InputMenu | 
					
						
							| 
									
										
										
										
											2025-01-16 15:32:13 +08:00
										 |  |  | 											bind:imageGenerationEnabled | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 											bind:webSearchEnabled | 
					
						
							|  |  |  | 											bind:selectedToolIds | 
					
						
							| 
									
										
										
										
											2024-12-18 17:27:32 +08:00
										 |  |  | 											{screenCaptureHandler} | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 											uploadFilesHandler={() => { | 
					
						
							|  |  |  | 												filesInputElement.click(); | 
					
						
							|  |  |  | 											}} | 
					
						
							| 
									
										
										
										
											2024-12-19 01:04:55 +08:00
										 |  |  | 											uploadGoogleDriveHandler={async () => { | 
					
						
							|  |  |  | 												try { | 
					
						
							|  |  |  | 													const fileData = await createPicker(); | 
					
						
							|  |  |  | 													if (fileData) { | 
					
						
							|  |  |  | 														const file = new File([fileData.blob], fileData.name, { | 
					
						
							|  |  |  | 															type: fileData.blob.type | 
					
						
							|  |  |  | 														}); | 
					
						
							|  |  |  | 														await uploadFileHandler(file); | 
					
						
							|  |  |  | 													} else { | 
					
						
							|  |  |  | 														console.log('No file was selected from Google Drive'); | 
					
						
							|  |  |  | 													} | 
					
						
							|  |  |  | 												} catch (error) { | 
					
						
							|  |  |  | 													console.error('Google Drive Error:', error); | 
					
						
							|  |  |  | 													toast.error( | 
					
						
							|  |  |  | 														$i18n.t('Error accessing Google Drive: {{error}}', { | 
					
						
							|  |  |  | 															error: error.message | 
					
						
							|  |  |  | 														}) | 
					
						
							|  |  |  | 													); | 
					
						
							| 
									
										
										
										
											2024-12-16 05:08:32 +08:00
										 |  |  | 												} | 
					
						
							| 
									
										
										
										
											2024-12-19 01:04:55 +08:00
										 |  |  | 											}} | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 											onClose={async () => { | 
					
						
							|  |  |  | 												await tick(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 												const chatInput = document.getElementById('chat-input'); | 
					
						
							|  |  |  | 												chatInput?.focus(); | 
					
						
							|  |  |  | 											}} | 
					
						
							| 
									
										
										
										
											2024-06-02 09:11:54 +08:00
										 |  |  | 										> | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 											<button | 
					
						
							| 
									
										
										
										
											2024-12-02 15:35:24 +08:00
										 |  |  | 												class="bg-transparent hover:bg-white/80 text-gray-800 dark:text-white dark:hover:bg-gray-800 transition rounded-full p-2 outline-none focus:outline-none" | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 												type="button" | 
					
						
							|  |  |  | 												aria-label="More" | 
					
						
							| 
									
										
										
										
											2024-06-02 09:11:54 +08:00
										 |  |  | 											> | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 												<svg | 
					
						
							|  |  |  | 													xmlns="http://www.w3.org/2000/svg" | 
					
						
							| 
									
										
										
										
											2024-12-02 15:35:24 +08:00
										 |  |  | 													viewBox="0 0 20 20" | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 													fill="currentColor" | 
					
						
							| 
									
										
										
										
											2024-12-02 15:35:24 +08:00
										 |  |  | 													class="size-5" | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 												> | 
					
						
							|  |  |  | 													<path | 
					
						
							| 
									
										
										
										
											2024-12-02 15:35:24 +08:00
										 |  |  | 														d="M10.75 4.75a.75.75 0 0 0-1.5 0v4.5h-4.5a.75.75 0 0 0 0 1.5h4.5v4.5a.75.75 0 0 0 1.5 0v-4.5h4.5a.75.75 0 0 0 0-1.5h-4.5v-4.5Z" | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 													/> | 
					
						
							|  |  |  | 												</svg> | 
					
						
							|  |  |  | 											</button> | 
					
						
							|  |  |  | 										</InputMenu> | 
					
						
							|  |  |  | 									</div> | 
					
						
							| 
									
										
										
										
											2023-11-25 16:21:07 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 									{#if $settings?.richTextInput ?? true} | 
					
						
							|  |  |  | 										<div | 
					
						
							| 
									
										
										
										
											2024-12-01 08:01:26 +08:00
										 |  |  | 											class="scrollbar-hidden text-left bg-transparent dark:text-gray-100 outline-none w-full py-2.5 px-1 rounded-xl resize-none h-fit max-h-80 overflow-auto" | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 										> | 
					
						
							|  |  |  | 											<RichTextInput | 
					
						
							|  |  |  | 												bind:this={chatInputElement} | 
					
						
							| 
									
										
										
										
											2024-11-29 16:16:49 +08:00
										 |  |  | 												bind:value={prompt} | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 												id="chat-input" | 
					
						
							| 
									
										
										
										
											2024-11-21 14:46:51 +08:00
										 |  |  | 												messageInput={true} | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 												shiftEnter={!$mobile || | 
					
						
							|  |  |  | 													!( | 
					
						
							|  |  |  | 														'ontouchstart' in window || | 
					
						
							|  |  |  | 														navigator.maxTouchPoints > 0 || | 
					
						
							|  |  |  | 														navigator.msMaxTouchPoints > 0 | 
					
						
							|  |  |  | 													)} | 
					
						
							| 
									
										
										
										
											2024-11-21 14:46:51 +08:00
										 |  |  | 												placeholder={placeholder ? placeholder : $i18n.t('Send a Message')} | 
					
						
							|  |  |  | 												largeTextAsFile={$settings?.largeTextAsFile ?? false} | 
					
						
							| 
									
										
										
										
											2024-11-29 15:26:09 +08:00
										 |  |  | 												autocomplete={true} | 
					
						
							| 
									
										
										
										
											2024-11-29 16:16:49 +08:00
										 |  |  | 												generateAutoCompletion={async (text) => { | 
					
						
							|  |  |  | 													if (selectedModelIds.length === 0 || !selectedModelIds.at(0)) { | 
					
						
							|  |  |  | 														toast.error($i18n.t('Please select a model first.')); | 
					
						
							|  |  |  | 													} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 													const res = await generateAutoCompletion( | 
					
						
							|  |  |  | 														localStorage.token, | 
					
						
							|  |  |  | 														selectedModelIds.at(0), | 
					
						
							| 
									
										
										
										
											2024-11-30 16:29:27 +08:00
										 |  |  | 														text, | 
					
						
							|  |  |  | 														history?.currentId | 
					
						
							|  |  |  | 															? createMessagesList(history, history.currentId) | 
					
						
							|  |  |  | 															: null | 
					
						
							| 
									
										
										
										
											2024-11-29 16:16:49 +08:00
										 |  |  | 													).catch((error) => { | 
					
						
							|  |  |  | 														console.log(error); | 
					
						
							| 
									
										
										
										
											2024-12-01 10:30:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-29 16:16:49 +08:00
										 |  |  | 														return null; | 
					
						
							|  |  |  | 													}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 													console.log(res); | 
					
						
							|  |  |  | 													return res; | 
					
						
							|  |  |  | 												}} | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 												on:keydown={async (e) => { | 
					
						
							|  |  |  | 													e = e.detail.event; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 													const isCtrlPressed = e.ctrlKey || e.metaKey; // metaKey is for Cmd key on Mac | 
					
						
							|  |  |  | 													const commandsContainerElement = | 
					
						
							|  |  |  | 														document.getElementById('commands-container'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-25 06:38:54 +08:00
										 |  |  | 													if (e.key === 'Escape') { | 
					
						
							|  |  |  | 														stopResponse(); | 
					
						
							|  |  |  | 													} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 													// Command/Ctrl + Shift + Enter to submit a message pair | 
					
						
							|  |  |  | 													if (isCtrlPressed && e.key === 'Enter' && e.shiftKey) { | 
					
						
							|  |  |  | 														e.preventDefault(); | 
					
						
							|  |  |  | 														createMessagePair(prompt); | 
					
						
							|  |  |  | 													} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 													// Check if Ctrl + R is pressed | 
					
						
							|  |  |  | 													if (prompt === '' && isCtrlPressed && e.key.toLowerCase() === 'r') { | 
					
						
							|  |  |  | 														e.preventDefault(); | 
					
						
							|  |  |  | 														console.log('regenerate'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 														const regenerateButton = [ | 
					
						
							|  |  |  | 															...document.getElementsByClassName('regenerate-response-button') | 
					
						
							|  |  |  | 														]?.at(-1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 														regenerateButton?.click(); | 
					
						
							|  |  |  | 													} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 													if (prompt === '' && e.key == 'ArrowUp') { | 
					
						
							|  |  |  | 														e.preventDefault(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 														const userMessageElement = [ | 
					
						
							|  |  |  | 															...document.getElementsByClassName('user-message') | 
					
						
							|  |  |  | 														]?.at(-1); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-20 04:19:06 +08:00
										 |  |  | 														if (userMessageElement) { | 
					
						
							|  |  |  | 															userMessageElement.scrollIntoView({ block: 'center' }); | 
					
						
							|  |  |  | 															const editButton = [ | 
					
						
							|  |  |  | 																...document.getElementsByClassName('edit-user-message-button') | 
					
						
							|  |  |  | 															]?.at(-1); | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-20 04:19:06 +08:00
										 |  |  | 															editButton?.click(); | 
					
						
							|  |  |  | 														} | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 													} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-26 14:43:34 +08:00
										 |  |  | 													if (commandsContainerElement) { | 
					
						
							|  |  |  | 														if (commandsContainerElement && e.key === 'ArrowUp') { | 
					
						
							|  |  |  | 															e.preventDefault(); | 
					
						
							|  |  |  | 															commandsElement.selectUp(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 															const commandOptionButton = [ | 
					
						
							|  |  |  | 																...document.getElementsByClassName('selected-command-option-button') | 
					
						
							|  |  |  | 															]?.at(-1); | 
					
						
							|  |  |  | 															commandOptionButton.scrollIntoView({ block: 'center' }); | 
					
						
							|  |  |  | 														} | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-26 14:43:34 +08:00
										 |  |  | 														if (commandsContainerElement && e.key === 'ArrowDown') { | 
					
						
							|  |  |  | 															e.preventDefault(); | 
					
						
							|  |  |  | 															commandsElement.selectDown(); | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-26 14:43:34 +08:00
										 |  |  | 															const commandOptionButton = [ | 
					
						
							|  |  |  | 																...document.getElementsByClassName('selected-command-option-button') | 
					
						
							|  |  |  | 															]?.at(-1); | 
					
						
							|  |  |  | 															commandOptionButton.scrollIntoView({ block: 'center' }); | 
					
						
							|  |  |  | 														} | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-26 14:43:34 +08:00
										 |  |  | 														if (commandsContainerElement && e.key === 'Tab') { | 
					
						
							|  |  |  | 															e.preventDefault(); | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-26 14:43:34 +08:00
										 |  |  | 															const commandOptionButton = [ | 
					
						
							|  |  |  | 																...document.getElementsByClassName('selected-command-option-button') | 
					
						
							|  |  |  | 															]?.at(-1); | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-26 14:43:34 +08:00
										 |  |  | 															commandOptionButton?.click(); | 
					
						
							|  |  |  | 														} | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-26 14:43:34 +08:00
										 |  |  | 														if (commandsContainerElement && e.key === 'Enter') { | 
					
						
							|  |  |  | 															e.preventDefault(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 															const commandOptionButton = [ | 
					
						
							|  |  |  | 																...document.getElementsByClassName('selected-command-option-button') | 
					
						
							|  |  |  | 															]?.at(-1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 															if (commandOptionButton) { | 
					
						
							|  |  |  | 																commandOptionButton?.click(); | 
					
						
							|  |  |  | 															} else { | 
					
						
							|  |  |  | 																document.getElementById('send-message-button')?.click(); | 
					
						
							|  |  |  | 															} | 
					
						
							|  |  |  | 														} | 
					
						
							|  |  |  | 													} else { | 
					
						
							|  |  |  | 														if ( | 
					
						
							|  |  |  | 															!$mobile || | 
					
						
							|  |  |  | 															!( | 
					
						
							|  |  |  | 																'ontouchstart' in window || | 
					
						
							|  |  |  | 																navigator.maxTouchPoints > 0 || | 
					
						
							|  |  |  | 																navigator.msMaxTouchPoints > 0 | 
					
						
							|  |  |  | 															) | 
					
						
							|  |  |  | 														) { | 
					
						
							|  |  |  | 															// Prevent Enter key from creating a new line | 
					
						
							| 
									
										
										
										
											2024-11-26 14:57:54 +08:00
										 |  |  | 															// Uses keyCode '13' for Enter key for chinese/japanese keyboards | 
					
						
							| 
									
										
										
										
											2024-11-26 14:43:34 +08:00
										 |  |  | 															if (e.keyCode === 13 && !e.shiftKey) { | 
					
						
							|  |  |  | 																e.preventDefault(); | 
					
						
							|  |  |  | 															} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 															// Submit the prompt when Enter key is pressed | 
					
						
							|  |  |  | 															if (prompt !== '' && e.keyCode === 13 && !e.shiftKey) { | 
					
						
							|  |  |  | 																dispatch('submit', prompt); | 
					
						
							|  |  |  | 															} | 
					
						
							|  |  |  | 														} | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 													} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 													if (e.key === 'Escape') { | 
					
						
							|  |  |  | 														console.log('Escape'); | 
					
						
							|  |  |  | 														atSelectedModel = undefined; | 
					
						
							|  |  |  | 														selectedToolIds = []; | 
					
						
							|  |  |  | 														webSearchEnabled = false; | 
					
						
							| 
									
										
										
										
											2025-01-19 08:34:13 +08:00
										 |  |  | 														imageGenerationEnabled = false; | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 													} | 
					
						
							|  |  |  | 												}} | 
					
						
							|  |  |  | 												on:paste={async (e) => { | 
					
						
							|  |  |  | 													e = e.detail.event; | 
					
						
							|  |  |  | 													console.log(e); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 													const clipboardData = e.clipboardData || window.clipboardData; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 													if (clipboardData && clipboardData.items) { | 
					
						
							|  |  |  | 														for (const item of clipboardData.items) { | 
					
						
							|  |  |  | 															if (item.type.indexOf('image') !== -1) { | 
					
						
							|  |  |  | 																const blob = item.getAsFile(); | 
					
						
							|  |  |  | 																const reader = new FileReader(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 																reader.onload = function (e) { | 
					
						
							|  |  |  | 																	files = [ | 
					
						
							|  |  |  | 																		...files, | 
					
						
							|  |  |  | 																		{ | 
					
						
							|  |  |  | 																			type: 'image', | 
					
						
							|  |  |  | 																			url: `${e.target.result}` | 
					
						
							|  |  |  | 																		} | 
					
						
							|  |  |  | 																	]; | 
					
						
							|  |  |  | 																}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 																reader.readAsDataURL(blob); | 
					
						
							| 
									
										
										
										
											2024-11-19 12:50:12 +08:00
										 |  |  | 															} else if (item.type === 'text/plain') { | 
					
						
							| 
									
										
										
										
											2024-11-19 12:58:18 +08:00
										 |  |  | 																if ($settings?.largeTextAsFile ?? false) { | 
					
						
							| 
									
										
										
										
											2024-11-19 14:39:55 +08:00
										 |  |  | 																	const text = clipboardData.getData('text/plain'); | 
					
						
							| 
									
										
										
										
											2024-11-19 12:50:12 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-19 12:58:18 +08:00
										 |  |  | 																	if (text.length > PASTED_TEXT_CHARACTER_LIMIT) { | 
					
						
							| 
									
										
										
										
											2024-11-19 14:27:42 +08:00
										 |  |  | 																		e.preventDefault(); | 
					
						
							| 
									
										
										
										
											2024-11-19 12:58:18 +08:00
										 |  |  | 																		const blob = new Blob([text], { type: 'text/plain' }); | 
					
						
							|  |  |  | 																		const file = new File([blob], `Pasted_Text_${Date.now()}.txt`, { | 
					
						
							|  |  |  | 																			type: 'text/plain' | 
					
						
							|  |  |  | 																		}); | 
					
						
							| 
									
										
										
										
											2024-11-19 12:50:12 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-19 12:58:18 +08:00
										 |  |  | 																		await uploadFileHandler(file, true); | 
					
						
							|  |  |  | 																	} | 
					
						
							| 
									
										
										
										
											2024-11-19 12:50:12 +08:00
										 |  |  | 																} | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 															} | 
					
						
							|  |  |  | 														} | 
					
						
							|  |  |  | 													} | 
					
						
							|  |  |  | 												}} | 
					
						
							|  |  |  | 											/> | 
					
						
							|  |  |  | 										</div> | 
					
						
							|  |  |  | 									{:else} | 
					
						
							|  |  |  | 										<textarea | 
					
						
							| 
									
										
										
										
											2024-10-26 12:31:18 +08:00
										 |  |  | 											id="chat-input" | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 											bind:this={chatInputElement} | 
					
						
							| 
									
										
										
										
											2024-12-10 16:01:19 +08:00
										 |  |  | 											class="scrollbar-hidden bg-transparent dark:text-gray-100 outline-none w-full py-3 px-1 rounded-xl resize-none h-[48px]" | 
					
						
							| 
									
										
										
										
											2024-10-26 12:31:18 +08:00
										 |  |  | 											placeholder={placeholder ? placeholder : $i18n.t('Send a Message')} | 
					
						
							|  |  |  | 											bind:value={prompt} | 
					
						
							|  |  |  | 											on:keypress={(e) => { | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 												if ( | 
					
						
							|  |  |  | 													!$mobile || | 
					
						
							|  |  |  | 													!( | 
					
						
							|  |  |  | 														'ontouchstart' in window || | 
					
						
							|  |  |  | 														navigator.maxTouchPoints > 0 || | 
					
						
							|  |  |  | 														navigator.msMaxTouchPoints > 0 | 
					
						
							|  |  |  | 													) | 
					
						
							|  |  |  | 												) { | 
					
						
							|  |  |  | 													// Prevent Enter key from creating a new line | 
					
						
							|  |  |  | 													if (e.key === 'Enter' && !e.shiftKey) { | 
					
						
							|  |  |  | 														e.preventDefault(); | 
					
						
							|  |  |  | 													} | 
					
						
							| 
									
										
										
										
											2024-10-26 12:31:18 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 													// Submit the prompt when Enter key is pressed | 
					
						
							|  |  |  | 													if (prompt !== '' && e.key === 'Enter' && !e.shiftKey) { | 
					
						
							|  |  |  | 														dispatch('submit', prompt); | 
					
						
							|  |  |  | 													} | 
					
						
							| 
									
										
										
										
											2024-10-26 12:31:18 +08:00
										 |  |  | 												} | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 											}} | 
					
						
							|  |  |  | 											on:keydown={async (e) => { | 
					
						
							| 
									
										
										
										
											2024-10-26 12:31:18 +08:00
										 |  |  | 												const isCtrlPressed = e.ctrlKey || e.metaKey; // metaKey is for Cmd key on Mac | 
					
						
							|  |  |  | 												const commandsContainerElement = | 
					
						
							|  |  |  | 													document.getElementById('commands-container'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-25 06:38:54 +08:00
										 |  |  | 												if (e.key === 'Escape') { | 
					
						
							|  |  |  | 													stopResponse(); | 
					
						
							|  |  |  | 												} | 
					
						
							| 
									
										
										
										
											2024-10-26 12:31:18 +08:00
										 |  |  | 												// Command/Ctrl + Shift + Enter to submit a message pair | 
					
						
							|  |  |  | 												if (isCtrlPressed && e.key === 'Enter' && e.shiftKey) { | 
					
						
							|  |  |  | 													e.preventDefault(); | 
					
						
							|  |  |  | 													createMessagePair(prompt); | 
					
						
							|  |  |  | 												} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 												// Check if Ctrl + R is pressed | 
					
						
							|  |  |  | 												if (prompt === '' && isCtrlPressed && e.key.toLowerCase() === 'r') { | 
					
						
							|  |  |  | 													e.preventDefault(); | 
					
						
							|  |  |  | 													console.log('regenerate'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 													const regenerateButton = [ | 
					
						
							|  |  |  | 														...document.getElementsByClassName('regenerate-response-button') | 
					
						
							|  |  |  | 													]?.at(-1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 													regenerateButton?.click(); | 
					
						
							|  |  |  | 												} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 												if (prompt === '' && e.key == 'ArrowUp') { | 
					
						
							|  |  |  | 													e.preventDefault(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 													const userMessageElement = [ | 
					
						
							|  |  |  | 														...document.getElementsByClassName('user-message') | 
					
						
							|  |  |  | 													]?.at(-1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 													const editButton = [ | 
					
						
							|  |  |  | 														...document.getElementsByClassName('edit-user-message-button') | 
					
						
							|  |  |  | 													]?.at(-1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 													console.log(userMessageElement); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 													userMessageElement.scrollIntoView({ block: 'center' }); | 
					
						
							|  |  |  | 													editButton?.click(); | 
					
						
							|  |  |  | 												} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 												if (commandsContainerElement && e.key === 'ArrowUp') { | 
					
						
							|  |  |  | 													e.preventDefault(); | 
					
						
							|  |  |  | 													commandsElement.selectUp(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 													const commandOptionButton = [ | 
					
						
							|  |  |  | 														...document.getElementsByClassName('selected-command-option-button') | 
					
						
							|  |  |  | 													]?.at(-1); | 
					
						
							|  |  |  | 													commandOptionButton.scrollIntoView({ block: 'center' }); | 
					
						
							|  |  |  | 												} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 												if (commandsContainerElement && e.key === 'ArrowDown') { | 
					
						
							|  |  |  | 													e.preventDefault(); | 
					
						
							|  |  |  | 													commandsElement.selectDown(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 													const commandOptionButton = [ | 
					
						
							|  |  |  | 														...document.getElementsByClassName('selected-command-option-button') | 
					
						
							|  |  |  | 													]?.at(-1); | 
					
						
							|  |  |  | 													commandOptionButton.scrollIntoView({ block: 'center' }); | 
					
						
							|  |  |  | 												} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 												if (commandsContainerElement && e.key === 'Enter') { | 
					
						
							|  |  |  | 													e.preventDefault(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 													const commandOptionButton = [ | 
					
						
							|  |  |  | 														...document.getElementsByClassName('selected-command-option-button') | 
					
						
							|  |  |  | 													]?.at(-1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 													if (e.shiftKey) { | 
					
						
							|  |  |  | 														prompt = `${prompt}\n`; | 
					
						
							|  |  |  | 													} else if (commandOptionButton) { | 
					
						
							|  |  |  | 														commandOptionButton?.click(); | 
					
						
							|  |  |  | 													} else { | 
					
						
							|  |  |  | 														document.getElementById('send-message-button')?.click(); | 
					
						
							|  |  |  | 													} | 
					
						
							|  |  |  | 												} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 												if (commandsContainerElement && e.key === 'Tab') { | 
					
						
							|  |  |  | 													e.preventDefault(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 													const commandOptionButton = [ | 
					
						
							|  |  |  | 														...document.getElementsByClassName('selected-command-option-button') | 
					
						
							|  |  |  | 													]?.at(-1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 													commandOptionButton?.click(); | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 												} else if (e.key === 'Tab') { | 
					
						
							|  |  |  | 													const words = findWordIndices(prompt); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 													if (words.length > 0) { | 
					
						
							|  |  |  | 														const word = words.at(0); | 
					
						
							|  |  |  | 														const fullPrompt = prompt; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 														prompt = prompt.substring(0, word?.endIndex + 1); | 
					
						
							|  |  |  | 														await tick(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 														e.target.scrollTop = e.target.scrollHeight; | 
					
						
							|  |  |  | 														prompt = fullPrompt; | 
					
						
							|  |  |  | 														await tick(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 														e.preventDefault(); | 
					
						
							|  |  |  | 														e.target.setSelectionRange(word?.startIndex, word.endIndex + 1); | 
					
						
							|  |  |  | 													} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 													e.target.style.height = ''; | 
					
						
							| 
									
										
										
										
											2024-12-01 06:16:39 +08:00
										 |  |  | 													e.target.style.height = Math.min(e.target.scrollHeight, 320) + 'px'; | 
					
						
							| 
									
										
										
										
											2024-10-26 12:31:18 +08:00
										 |  |  | 												} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 												if (e.key === 'Escape') { | 
					
						
							|  |  |  | 													console.log('Escape'); | 
					
						
							|  |  |  | 													atSelectedModel = undefined; | 
					
						
							| 
									
										
										
										
											2024-11-13 12:33:12 +08:00
										 |  |  | 													selectedToolIds = []; | 
					
						
							| 
									
										
										
										
											2024-11-11 11:11:06 +08:00
										 |  |  | 													webSearchEnabled = false; | 
					
						
							| 
									
										
										
										
											2025-01-19 08:34:13 +08:00
										 |  |  | 													imageGenerationEnabled = false; | 
					
						
							| 
									
										
										
										
											2024-10-26 12:31:18 +08:00
										 |  |  | 												} | 
					
						
							|  |  |  | 											}} | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 											rows="1" | 
					
						
							|  |  |  | 											on:input={async (e) => { | 
					
						
							|  |  |  | 												e.target.style.height = ''; | 
					
						
							| 
									
										
										
										
											2024-12-01 06:16:39 +08:00
										 |  |  | 												e.target.style.height = Math.min(e.target.scrollHeight, 320) + 'px'; | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 											}} | 
					
						
							|  |  |  | 											on:focus={async (e) => { | 
					
						
							|  |  |  | 												e.target.style.height = ''; | 
					
						
							| 
									
										
										
										
											2024-12-01 06:16:39 +08:00
										 |  |  | 												e.target.style.height = Math.min(e.target.scrollHeight, 320) + 'px'; | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 											}} | 
					
						
							| 
									
										
										
										
											2024-10-26 12:31:18 +08:00
										 |  |  | 											on:paste={async (e) => { | 
					
						
							|  |  |  | 												const clipboardData = e.clipboardData || window.clipboardData; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 												if (clipboardData && clipboardData.items) { | 
					
						
							|  |  |  | 													for (const item of clipboardData.items) { | 
					
						
							|  |  |  | 														if (item.type.indexOf('image') !== -1) { | 
					
						
							|  |  |  | 															const blob = item.getAsFile(); | 
					
						
							|  |  |  | 															const reader = new FileReader(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 															reader.onload = function (e) { | 
					
						
							|  |  |  | 																files = [ | 
					
						
							|  |  |  | 																	...files, | 
					
						
							|  |  |  | 																	{ | 
					
						
							|  |  |  | 																		type: 'image', | 
					
						
							|  |  |  | 																		url: `${e.target.result}` | 
					
						
							|  |  |  | 																	} | 
					
						
							|  |  |  | 																]; | 
					
						
							|  |  |  | 															}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 															reader.readAsDataURL(blob); | 
					
						
							| 
									
										
										
										
											2024-11-19 12:50:12 +08:00
										 |  |  | 														} else if (item.type === 'text/plain') { | 
					
						
							| 
									
										
										
										
											2024-11-19 12:58:18 +08:00
										 |  |  | 															if ($settings?.largeTextAsFile ?? false) { | 
					
						
							| 
									
										
										
										
											2024-11-19 14:39:55 +08:00
										 |  |  | 																const text = clipboardData.getData('text/plain'); | 
					
						
							| 
									
										
										
										
											2024-11-19 12:50:12 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-19 12:58:18 +08:00
										 |  |  | 																if (text.length > PASTED_TEXT_CHARACTER_LIMIT) { | 
					
						
							| 
									
										
										
										
											2024-11-19 14:27:42 +08:00
										 |  |  | 																	e.preventDefault(); | 
					
						
							| 
									
										
										
										
											2024-11-19 12:58:18 +08:00
										 |  |  | 																	const blob = new Blob([text], { type: 'text/plain' }); | 
					
						
							|  |  |  | 																	const file = new File([blob], `Pasted_Text_${Date.now()}.txt`, { | 
					
						
							|  |  |  | 																		type: 'text/plain' | 
					
						
							|  |  |  | 																	}); | 
					
						
							| 
									
										
										
										
											2024-11-19 12:50:12 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-19 12:58:18 +08:00
										 |  |  | 																	await uploadFileHandler(file, true); | 
					
						
							|  |  |  | 																} | 
					
						
							| 
									
										
										
										
											2024-11-19 12:50:12 +08:00
										 |  |  | 															} | 
					
						
							| 
									
										
										
										
											2024-10-26 12:31:18 +08:00
										 |  |  | 														} | 
					
						
							|  |  |  | 													} | 
					
						
							|  |  |  | 												} | 
					
						
							|  |  |  | 											}} | 
					
						
							|  |  |  | 										/> | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 									{/if} | 
					
						
							| 
									
										
										
										
											2024-05-02 17:20:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-02 15:16:00 +08:00
										 |  |  | 									<div class="self-end mb-1.5 flex space-x-1 mr-1"> | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 										{#if !history?.currentId || history.messages[history.currentId]?.done == true} | 
					
						
							|  |  |  | 											<Tooltip content={$i18n.t('Record voice')}> | 
					
						
							|  |  |  | 												<button | 
					
						
							|  |  |  | 													id="voice-input-button" | 
					
						
							| 
									
										
										
										
											2024-12-02 15:16:00 +08:00
										 |  |  | 													class=" text-gray-600 dark:text-gray-300 hover:text-gray-700 dark:hover:text-gray-200 transition rounded-full p-1.5 mr-0.5 self-center" | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 													type="button" | 
					
						
							|  |  |  | 													on:click={async () => { | 
					
						
							|  |  |  | 														try { | 
					
						
							|  |  |  | 															let stream = await navigator.mediaDevices | 
					
						
							|  |  |  | 																.getUserMedia({ audio: true }) | 
					
						
							|  |  |  | 																.catch(function (err) { | 
					
						
							|  |  |  | 																	toast.error( | 
					
						
							|  |  |  | 																		$i18n.t( | 
					
						
							|  |  |  | 																			`Permission denied when accessing microphone: {{error}}`, | 
					
						
							|  |  |  | 																			{ | 
					
						
							|  |  |  | 																				error: err | 
					
						
							|  |  |  | 																			} | 
					
						
							|  |  |  | 																		) | 
					
						
							|  |  |  | 																	); | 
					
						
							|  |  |  | 																	return null; | 
					
						
							|  |  |  | 																}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 															if (stream) { | 
					
						
							|  |  |  | 																recording = true; | 
					
						
							|  |  |  | 																const tracks = stream.getTracks(); | 
					
						
							|  |  |  | 																tracks.forEach((track) => track.stop()); | 
					
						
							|  |  |  | 															} | 
					
						
							|  |  |  | 															stream = null; | 
					
						
							|  |  |  | 														} catch { | 
					
						
							|  |  |  | 															toast.error($i18n.t('Permission denied when accessing microphone')); | 
					
						
							| 
									
										
										
										
											2024-06-08 06:18:45 +08:00
										 |  |  | 														} | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 													}} | 
					
						
							|  |  |  | 													aria-label="Voice Input" | 
					
						
							| 
									
										
										
										
											2024-06-02 09:11:54 +08:00
										 |  |  | 												> | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 													<svg | 
					
						
							|  |  |  | 														xmlns="http://www.w3.org/2000/svg" | 
					
						
							|  |  |  | 														viewBox="0 0 20 20" | 
					
						
							|  |  |  | 														fill="currentColor" | 
					
						
							|  |  |  | 														class="w-5 h-5 translate-y-[0.5px]" | 
					
						
							|  |  |  | 													> | 
					
						
							|  |  |  | 														<path d="M7 4a3 3 0 016 0v6a3 3 0 11-6 0V4z" /> | 
					
						
							|  |  |  | 														<path | 
					
						
							|  |  |  | 															d="M5.5 9.643a.75.75 0 00-1.5 0V10c0 3.06 2.29 5.585 5.25 5.954V17.5h-1.5a.75.75 0 000 1.5h4.5a.75.75 0 000-1.5h-1.5v-1.546A6.001 6.001 0 0016 10v-.357a.75.75 0 00-1.5 0V10a4.5 4.5 0 01-9 0v-.357z" | 
					
						
							|  |  |  | 														/> | 
					
						
							|  |  |  | 													</svg> | 
					
						
							|  |  |  | 												</button> | 
					
						
							|  |  |  | 											</Tooltip> | 
					
						
							|  |  |  | 										{/if} | 
					
						
							| 
									
										
										
										
											2024-06-08 06:12:34 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-02 15:16:00 +08:00
										 |  |  | 										{#if !history.currentId || history.messages[history.currentId]?.done == true} | 
					
						
							|  |  |  | 											{#if prompt === ''} | 
					
						
							|  |  |  | 												<div class=" flex items-center"> | 
					
						
							|  |  |  | 													<Tooltip content={$i18n.t('Call')}> | 
					
						
							|  |  |  | 														<button | 
					
						
							|  |  |  | 															class=" bg-black text-white hover:bg-gray-900 dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full p-2 self-center" | 
					
						
							|  |  |  | 															type="button" | 
					
						
							|  |  |  | 															on:click={async () => { | 
					
						
							|  |  |  | 																if (selectedModels.length > 1) { | 
					
						
							|  |  |  | 																	toast.error($i18n.t('Select only one model to call')); | 
					
						
							| 
									
										
										
										
											2024-08-24 00:22:50 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-02 15:16:00 +08:00
										 |  |  | 																	return; | 
					
						
							|  |  |  | 																} | 
					
						
							| 
									
										
										
										
											2024-06-08 11:57:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-02 15:16:00 +08:00
										 |  |  | 																if ($config.audio.stt.engine === 'web') { | 
					
						
							|  |  |  | 																	toast.error( | 
					
						
							|  |  |  | 																		$i18n.t( | 
					
						
							|  |  |  | 																			'Call feature is not supported when using Web STT engine' | 
					
						
							|  |  |  | 																		) | 
					
						
							|  |  |  | 																	); | 
					
						
							| 
									
										
										
										
											2024-06-08 11:57:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-02 15:16:00 +08:00
										 |  |  | 																	return; | 
					
						
							|  |  |  | 																} | 
					
						
							|  |  |  | 																// check if user has access to getUserMedia | 
					
						
							|  |  |  | 																try { | 
					
						
							|  |  |  | 																	let stream = await navigator.mediaDevices.getUserMedia({ | 
					
						
							|  |  |  | 																		audio: true | 
					
						
							|  |  |  | 																	}); | 
					
						
							|  |  |  | 																	// If the user grants the permission, proceed to show the call overlay | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-02 15:16:00 +08:00
										 |  |  | 																	if (stream) { | 
					
						
							|  |  |  | 																		const tracks = stream.getTracks(); | 
					
						
							|  |  |  | 																		tracks.forEach((track) => track.stop()); | 
					
						
							|  |  |  | 																	} | 
					
						
							| 
									
										
										
										
											2024-08-24 00:22:50 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-02 15:16:00 +08:00
										 |  |  | 																	stream = null; | 
					
						
							| 
									
										
										
										
											2024-08-23 22:42:36 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-02 15:16:00 +08:00
										 |  |  | 																	showCallOverlay.set(true); | 
					
						
							|  |  |  | 																	showControls.set(true); | 
					
						
							|  |  |  | 																} catch (err) { | 
					
						
							|  |  |  | 																	// If the user denies the permission or an error occurs, show an error message | 
					
						
							|  |  |  | 																	toast.error( | 
					
						
							|  |  |  | 																		$i18n.t('Permission denied when accessing media devices') | 
					
						
							|  |  |  | 																	); | 
					
						
							|  |  |  | 																} | 
					
						
							|  |  |  | 															}} | 
					
						
							|  |  |  | 															aria-label="Call" | 
					
						
							|  |  |  | 														> | 
					
						
							|  |  |  | 															<Headphone className="size-5" /> | 
					
						
							|  |  |  | 														</button> | 
					
						
							|  |  |  | 													</Tooltip> | 
					
						
							|  |  |  | 												</div> | 
					
						
							|  |  |  | 											{:else} | 
					
						
							|  |  |  | 												<div class=" flex items-center"> | 
					
						
							|  |  |  | 													<Tooltip content={$i18n.t('Send message')}> | 
					
						
							|  |  |  | 														<button | 
					
						
							|  |  |  | 															id="send-message-button" | 
					
						
							|  |  |  | 															class="{prompt !== '' | 
					
						
							|  |  |  | 																? 'bg-black text-white hover:bg-gray-900 dark:bg-white dark:text-black dark:hover:bg-gray-100 ' | 
					
						
							|  |  |  | 																: 'text-white bg-gray-200 dark:text-gray-900 dark:bg-gray-700 disabled'} transition rounded-full p-1.5 self-center" | 
					
						
							|  |  |  | 															type="submit" | 
					
						
							|  |  |  | 															disabled={prompt === ''} | 
					
						
							|  |  |  | 														> | 
					
						
							|  |  |  | 															<svg | 
					
						
							|  |  |  | 																xmlns="http://www.w3.org/2000/svg" | 
					
						
							|  |  |  | 																viewBox="0 0 16 16" | 
					
						
							|  |  |  | 																fill="currentColor" | 
					
						
							|  |  |  | 																class="size-6" | 
					
						
							|  |  |  | 															> | 
					
						
							|  |  |  | 																<path | 
					
						
							|  |  |  | 																	fill-rule="evenodd" | 
					
						
							|  |  |  | 																	d="M8 14a.75.75 0 0 1-.75-.75V4.56L4.03 7.78a.75.75 0 0 1-1.06-1.06l4.5-4.5a.75.75 0 0 1 1.06 0l4.5 4.5a.75.75 0 0 1-1.06 1.06L8.75 4.56v8.69A.75.75 0 0 1 8 14Z" | 
					
						
							|  |  |  | 																	clip-rule="evenodd" | 
					
						
							|  |  |  | 																/> | 
					
						
							|  |  |  | 															</svg> | 
					
						
							|  |  |  | 														</button> | 
					
						
							|  |  |  | 													</Tooltip> | 
					
						
							|  |  |  | 												</div> | 
					
						
							|  |  |  | 											{/if} | 
					
						
							|  |  |  | 										{:else} | 
					
						
							|  |  |  | 											<div class=" flex items-center"> | 
					
						
							|  |  |  | 												<Tooltip content={$i18n.t('Stop')}> | 
					
						
							|  |  |  | 													<button | 
					
						
							|  |  |  | 														class="bg-white hover:bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-white dark:hover:bg-gray-800 transition rounded-full p-1.5" | 
					
						
							|  |  |  | 														on:click={() => { | 
					
						
							|  |  |  | 															stopResponse(); | 
					
						
							|  |  |  | 														}} | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 													> | 
					
						
							| 
									
										
										
										
											2024-12-02 15:16:00 +08:00
										 |  |  | 														<svg | 
					
						
							|  |  |  | 															xmlns="http://www.w3.org/2000/svg" | 
					
						
							|  |  |  | 															viewBox="0 0 24 24" | 
					
						
							|  |  |  | 															fill="currentColor" | 
					
						
							|  |  |  | 															class="size-6" | 
					
						
							|  |  |  | 														> | 
					
						
							|  |  |  | 															<path | 
					
						
							|  |  |  | 																fill-rule="evenodd" | 
					
						
							|  |  |  | 																d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12zm6-2.438c0-.724.588-1.312 1.313-1.312h4.874c.725 0 1.313.588 1.313 1.313v4.874c0 .725-.588 1.313-1.313 1.313H9.564a1.312 1.312 0 01-1.313-1.313V9.564z" | 
					
						
							|  |  |  | 																clip-rule="evenodd" | 
					
						
							|  |  |  | 															/> | 
					
						
							|  |  |  | 														</svg> | 
					
						
							|  |  |  | 													</button> | 
					
						
							|  |  |  | 												</Tooltip> | 
					
						
							|  |  |  | 											</div> | 
					
						
							|  |  |  | 										{/if} | 
					
						
							| 
									
										
										
										
											2024-06-07 11:33:23 +08:00
										 |  |  | 									</div> | 
					
						
							|  |  |  | 								</div> | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | 							</div> | 
					
						
							|  |  |  | 						</form> | 
					
						
							|  |  |  | 					{/if} | 
					
						
							|  |  |  | 				</div> | 
					
						
							| 
									
										
										
										
											2023-11-20 09:47:07 +08:00
										 |  |  | 			</div> | 
					
						
							|  |  |  | 		</div> | 
					
						
							|  |  |  | 	</div> | 
					
						
							| 
									
										
										
										
											2024-11-17 08:01:02 +08:00
										 |  |  | {/if} |