forked from open-webui/open-webui
		
	feat: chat history support
This commit is contained in:
		
							parent
							
								
									9ae71b2096
								
							
						
					
					
						commit
						4e4076e267
					
				
					 1 changed files with 391 additions and 131 deletions
				
			
		|  | @ -39,6 +39,22 @@ | ||||||
| 	let title = ''; | 	let title = ''; | ||||||
| 	let prompt = ''; | 	let prompt = ''; | ||||||
| 	let messages = []; | 	let messages = []; | ||||||
|  | 	let history = { | ||||||
|  | 		messages: {}, | ||||||
|  | 		currentId: null | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	$: if (history.currentId !== null) { | ||||||
|  | 		let _messages = []; | ||||||
|  | 
 | ||||||
|  | 		let currentMessage = history.messages[history.currentId]; | ||||||
|  | 		while (currentMessage !== null) { | ||||||
|  | 			_messages.unshift({ ...currentMessage }); | ||||||
|  | 			currentMessage = | ||||||
|  | 				currentMessage.parentId !== null ? history.messages[currentMessage.parentId] : null; | ||||||
|  | 		} | ||||||
|  | 		messages = _messages; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	let showSettings = false; | 	let showSettings = false; | ||||||
| 	let stopResponseFlag = false; | 	let stopResponseFlag = false; | ||||||
|  | @ -260,8 +276,13 @@ | ||||||
| 		if (init || messages.length > 0) { | 		if (init || messages.length > 0) { | ||||||
| 			chatId = uuidv4(); | 			chatId = uuidv4(); | ||||||
| 			autoScroll = true; | 			autoScroll = true; | ||||||
| 			messages = []; | 
 | ||||||
| 			title = ''; | 			title = ''; | ||||||
|  | 			messages = []; | ||||||
|  | 			history = { | ||||||
|  | 				messages: {}, | ||||||
|  | 				currentId: null | ||||||
|  | 			}; | ||||||
| 
 | 
 | ||||||
| 			settings = JSON.parse(localStorage.getItem('settings') ?? JSON.stringify(settings)); | 			settings = JSON.parse(localStorage.getItem('settings') ?? JSON.stringify(settings)); | ||||||
| 
 | 
 | ||||||
|  | @ -311,18 +332,58 @@ | ||||||
| 
 | 
 | ||||||
| 	const loadChat = async (id) => { | 	const loadChat = async (id) => { | ||||||
| 		const chat = await db.get('chats', id); | 		const chat = await db.get('chats', id); | ||||||
|  | 		console.log(chat); | ||||||
| 		if (chatId !== chat.id) { | 		if (chatId !== chat.id) { | ||||||
| 			if (chat.messages.length > 0) { | 			if ('history' in chat) { | ||||||
| 				chat.messages.at(-1).done = true; | 				history = chat.history; | ||||||
|  | 			} else { | ||||||
|  | 				let _history = { | ||||||
|  | 					messages: {}, | ||||||
|  | 					currentId: null | ||||||
|  | 				}; | ||||||
|  | 
 | ||||||
|  | 				let parentMessageId = null; | ||||||
|  | 				let messageId = null; | ||||||
|  | 
 | ||||||
|  | 				for (const message of chat.messages) { | ||||||
|  | 					messageId = uuidv4(); | ||||||
|  | 
 | ||||||
|  | 					if (parentMessageId !== null) { | ||||||
|  | 						_history.messages[parentMessageId].childrenIds = [ | ||||||
|  | 							..._history.messages[parentMessageId].childrenIds, | ||||||
|  | 							messageId | ||||||
|  | 						]; | ||||||
|  | 					} | ||||||
|  | 
 | ||||||
|  | 					_history.messages[messageId] = { | ||||||
|  | 						...message, | ||||||
|  | 						id: messageId, | ||||||
|  | 						parentId: parentMessageId, | ||||||
|  | 						childrenIds: [] | ||||||
|  | 					}; | ||||||
|  | 
 | ||||||
|  | 					parentMessageId = messageId; | ||||||
|  | 				} | ||||||
|  | 				_history.currentId = messageId; | ||||||
|  | 
 | ||||||
|  | 				history = _history; | ||||||
| 			} | 			} | ||||||
| 			messages = chat.messages; | 
 | ||||||
|  | 			console.log(history); | ||||||
|  | 
 | ||||||
| 			title = chat.title; | 			title = chat.title; | ||||||
| 			chatId = chat.id; | 			chatId = chat.id; | ||||||
| 			selectedModel = chat.model ?? selectedModel; | 			selectedModel = chat.model ?? selectedModel; | ||||||
| 			settings.system = chat.system ?? settings.system; | 			settings.system = chat.system ?? settings.system; | ||||||
| 			settings.temperature = chat.temperature ?? settings.temperature; | 			settings.temperature = chat.temperature ?? settings.temperature; | ||||||
|  | 			autoScroll = true; | ||||||
| 
 | 
 | ||||||
| 			await tick(); | 			await tick(); | ||||||
|  | 
 | ||||||
|  | 			if (messages.length > 0) { | ||||||
|  | 				history.messages[messages.at(-1).id].done = true; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
| 			renderLatex(); | 			renderLatex(); | ||||||
| 
 | 
 | ||||||
| 			hljs.highlightAll(); | 			hljs.highlightAll(); | ||||||
|  | @ -368,7 +429,8 @@ | ||||||
| 				options: chat.options, | 				options: chat.options, | ||||||
| 				title: chat.title, | 				title: chat.title, | ||||||
| 				timestamp: chat.timestamp, | 				timestamp: chat.timestamp, | ||||||
| 				messages: chat.messages | 				messages: chat.messages, | ||||||
|  | 				history: chat.history | ||||||
| 			}); | 			}); | ||||||
| 		} | 		} | ||||||
| 		chats = await db.getAllFromIndex('chats', 'timestamp'); | 		chats = await db.getAllFromIndex('chats', 'timestamp'); | ||||||
|  | @ -386,35 +448,44 @@ | ||||||
| 		showSettings = true; | 		showSettings = true; | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	const editMessage = async (messageIdx) => { | 	const editMessageHandler = async (messageId) => { | ||||||
| 		messages = messages.map((message, idx) => { | 		// let editMessage = history.messages[messageId]; | ||||||
| 			if (messageIdx === idx) { | 		history.messages[messageId].edit = true; | ||||||
| 				message.edit = true; | 		history.messages[messageId].editedContent = history.messages[messageId].content; | ||||||
| 				message.editedContent = message.content; |  | ||||||
| 			} |  | ||||||
| 			return message; |  | ||||||
| 		}); |  | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	const confirmEditMessage = async (messageIdx) => { | 	const confirmEditMessage = async (messageId) => { | ||||||
| 		let userPrompt = messages.at(messageIdx).editedContent; | 		history.messages[messageId].edit = false; | ||||||
| 
 | 
 | ||||||
| 		messages.splice(messageIdx, messages.length - messageIdx); | 		let userPrompt = history.messages[messageId].editedContent; | ||||||
| 		messages = messages; | 		let userMessageId = uuidv4(); | ||||||
| 
 | 
 | ||||||
| 		await submitPrompt(userPrompt); | 		let userMessage = { | ||||||
|  | 			id: userMessageId, | ||||||
|  | 			parentId: history.messages[messageId].parentId, | ||||||
|  | 			childrenIds: [], | ||||||
|  | 			role: 'user', | ||||||
|  | 			content: userPrompt | ||||||
|  | 		}; | ||||||
|  | 
 | ||||||
|  | 		let messageParentId = history.messages[messageId].parentId; | ||||||
|  | 
 | ||||||
|  | 		if (messageParentId !== null) { | ||||||
|  | 			history.messages[messageParentId].childrenIds = [ | ||||||
|  | 				...history.messages[messageParentId].childrenIds, | ||||||
|  | 				userMessageId | ||||||
|  | 			]; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		history.messages[userMessageId] = userMessage; | ||||||
|  | 		history.currentId = userMessageId; | ||||||
|  | 
 | ||||||
|  | 		await sendPrompt(userPrompt, userMessageId); | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	const cancelEditMessage = (messageIdx) => { | 	const cancelEditMessage = (messageId) => { | ||||||
| 		messages = messages.map((message, idx) => { | 		history.messages[messageId].edit = false; | ||||||
| 			if (messageIdx === idx) { | 		history.messages[messageId].editedContent = undefined; | ||||||
| 				message.edit = undefined; |  | ||||||
| 				message.editedContent = undefined; |  | ||||||
| 			} |  | ||||||
| 			return message; |  | ||||||
| 		}); |  | ||||||
| 
 |  | ||||||
| 		console.log(messages); |  | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	const rateMessage = async (messageIdx, rating) => { | 	const rateMessage = async (messageIdx, rating) => { | ||||||
|  | @ -434,12 +505,89 @@ | ||||||
| 				temperature: settings.temperature | 				temperature: settings.temperature | ||||||
| 			}, | 			}, | ||||||
| 			timestamp: Date.now(), | 			timestamp: Date.now(), | ||||||
| 			messages: messages | 			messages: messages, | ||||||
|  | 			history: history | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		console.log(messages); | 		console.log(messages); | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
|  | 	const showPreviousMessage = async (message) => { | ||||||
|  | 		if (message.parentId !== null) { | ||||||
|  | 			let messageId = | ||||||
|  | 				history.messages[message.parentId].childrenIds[ | ||||||
|  | 					Math.max(history.messages[message.parentId].childrenIds.indexOf(message.id) - 1, 0) | ||||||
|  | 				]; | ||||||
|  | 
 | ||||||
|  | 			if (message.id !== messageId) { | ||||||
|  | 				let messageChildrenIds = history.messages[messageId].childrenIds; | ||||||
|  | 
 | ||||||
|  | 				while (messageChildrenIds.length !== 0) { | ||||||
|  | 					messageId = messageChildrenIds.at(-1); | ||||||
|  | 					messageChildrenIds = history.messages[messageId].childrenIds; | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				history.currentId = messageId; | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			let childrenIds = Object.values(history.messages) | ||||||
|  | 				.filter((message) => message.parentId === null) | ||||||
|  | 				.map((message) => message.id); | ||||||
|  | 			let messageId = childrenIds[Math.max(childrenIds.indexOf(message.id) - 1, 0)]; | ||||||
|  | 
 | ||||||
|  | 			if (message.id !== messageId) { | ||||||
|  | 				let messageChildrenIds = history.messages[messageId].childrenIds; | ||||||
|  | 
 | ||||||
|  | 				while (messageChildrenIds.length !== 0) { | ||||||
|  | 					messageId = messageChildrenIds.at(-1); | ||||||
|  | 					messageChildrenIds = history.messages[messageId].childrenIds; | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				history.currentId = messageId; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	const showNextMessage = async (message) => { | ||||||
|  | 		if (message.parentId !== null) { | ||||||
|  | 			let messageId = | ||||||
|  | 				history.messages[message.parentId].childrenIds[ | ||||||
|  | 					Math.min( | ||||||
|  | 						history.messages[message.parentId].childrenIds.indexOf(message.id) + 1, | ||||||
|  | 						history.messages[message.parentId].childrenIds.length - 1 | ||||||
|  | 					) | ||||||
|  | 				]; | ||||||
|  | 
 | ||||||
|  | 			if (message.id !== messageId) { | ||||||
|  | 				let messageChildrenIds = history.messages[messageId].childrenIds; | ||||||
|  | 
 | ||||||
|  | 				while (messageChildrenIds.length !== 0) { | ||||||
|  | 					messageId = messageChildrenIds.at(-1); | ||||||
|  | 					messageChildrenIds = history.messages[messageId].childrenIds; | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				history.currentId = messageId; | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			let childrenIds = Object.values(history.messages) | ||||||
|  | 				.filter((message) => message.parentId === null) | ||||||
|  | 				.map((message) => message.id); | ||||||
|  | 			let messageId = | ||||||
|  | 				childrenIds[Math.min(childrenIds.indexOf(message.id) + 1, childrenIds.length - 1)]; | ||||||
|  | 
 | ||||||
|  | 			if (message.id !== messageId) { | ||||||
|  | 				let messageChildrenIds = history.messages[messageId].childrenIds; | ||||||
|  | 
 | ||||||
|  | 				while (messageChildrenIds.length !== 0) { | ||||||
|  | 					messageId = messageChildrenIds.at(-1); | ||||||
|  | 					messageChildrenIds = history.messages[messageId].childrenIds; | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				history.currentId = messageId; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
| 	////////////////////////// | 	////////////////////////// | ||||||
| 	// Ollama functions | 	// Ollama functions | ||||||
| 	////////////////////////// | 	////////////////////////// | ||||||
|  | @ -507,21 +655,46 @@ | ||||||
| 		} | 		} | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	const sendPrompt = async (userPrompt) => { | 	const sendPrompt = async (userPrompt, parentId) => { | ||||||
|  | 		// await Promise.all( | ||||||
|  | 		// 	selectedModels.map((model) => { | ||||||
|  | 		// 		if (selectedModel.includes('gpt-')) { | ||||||
|  | 		// 			await sendPromptOpenAI(userPrompt, parentId); | ||||||
|  | 		// 		} else { | ||||||
|  | 		// 			await sendPromptOllama(userPrompt, parentId); | ||||||
|  | 		// 		} | ||||||
|  | 		// 	}) | ||||||
|  | 		// ); | ||||||
|  | 
 | ||||||
| 		if (selectedModel.includes('gpt-')) { | 		if (selectedModel.includes('gpt-')) { | ||||||
| 			await sendPromptOpenAI(userPrompt); | 			await sendPromptOpenAI(userPrompt, parentId); | ||||||
| 		} else { | 		} else { | ||||||
| 			await sendPromptOllama(userPrompt); | 			await sendPromptOllama(userPrompt, parentId); | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
|  | 		console.log(history); | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	const sendPromptOllama = async (userPrompt) => { | 	const sendPromptOllama = async (userPrompt, parentId) => { | ||||||
|  | 		let responseMessageId = uuidv4(); | ||||||
|  | 
 | ||||||
| 		let responseMessage = { | 		let responseMessage = { | ||||||
|  | 			parentId: parentId, | ||||||
|  | 			id: responseMessageId, | ||||||
|  | 			childrenIds: [], | ||||||
| 			role: 'assistant', | 			role: 'assistant', | ||||||
| 			content: '' | 			content: '' | ||||||
| 		}; | 		}; | ||||||
| 
 | 
 | ||||||
| 		messages = [...messages, responseMessage]; | 		history.messages[responseMessageId] = responseMessage; | ||||||
|  | 		history.currentId = responseMessageId; | ||||||
|  | 		if (parentId !== null) { | ||||||
|  | 			history.messages[parentId].childrenIds = [ | ||||||
|  | 				...history.messages[parentId].childrenIds, | ||||||
|  | 				responseMessageId | ||||||
|  | 			]; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		window.scrollTo({ top: document.body.scrollHeight }); | 		window.scrollTo({ top: document.body.scrollHeight }); | ||||||
| 
 | 
 | ||||||
| 		const res = await fetch(`${API_BASE_URL}/generate`, { | 		const res = await fetch(`${API_BASE_URL}/generate`, { | ||||||
|  | @ -542,8 +715,9 @@ | ||||||
| 				}, | 				}, | ||||||
| 				format: settings.requestFormat ?? undefined, | 				format: settings.requestFormat ?? undefined, | ||||||
| 				context: | 				context: | ||||||
| 					messages.length > 3 && messages.at(-3).context != undefined | 					history.messages[parentId] !== null && | ||||||
| 						? messages.at(-3).context | 					history.messages[parentId].parentId in history.messages | ||||||
|  | 						? history.messages[history.messages[parentId].parentId]?.context ?? undefined | ||||||
| 						: undefined | 						: undefined | ||||||
| 			}) | 			}) | ||||||
| 		}); | 		}); | ||||||
|  | @ -608,7 +782,8 @@ | ||||||
| 					temperature: settings.temperature | 					temperature: settings.temperature | ||||||
| 				}, | 				}, | ||||||
| 				timestamp: Date.now(), | 				timestamp: Date.now(), | ||||||
| 				messages: messages | 				messages: messages, | ||||||
|  | 				history: history | ||||||
| 			}); | 			}); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | @ -715,7 +890,8 @@ | ||||||
| 							temperature: settings.temperature | 							temperature: settings.temperature | ||||||
| 						}, | 						}, | ||||||
| 						timestamp: Date.now(), | 						timestamp: Date.now(), | ||||||
| 						messages: messages | 						messages: messages, | ||||||
|  | 						history: history | ||||||
| 					}); | 					}); | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
|  | @ -747,13 +923,22 @@ | ||||||
| 		} else { | 		} else { | ||||||
| 			document.getElementById('chat-textarea').style.height = ''; | 			document.getElementById('chat-textarea').style.height = ''; | ||||||
| 
 | 
 | ||||||
| 			messages = [ | 			let userMessageId = uuidv4(); | ||||||
| 				...messages, | 
 | ||||||
| 				{ | 			let userMessage = { | ||||||
| 					role: 'user', | 				id: userMessageId, | ||||||
| 					content: userPrompt | 				parentId: messages.length !== 0 ? messages.at(-1).id : null, | ||||||
| 				} | 				childrenIds: [], | ||||||
| 			]; | 				role: 'user', | ||||||
|  | 				content: userPrompt | ||||||
|  | 			}; | ||||||
|  | 
 | ||||||
|  | 			if (messages.length !== 0) { | ||||||
|  | 				history.messages[messages.at(-1).id].childrenIds.push(userMessageId); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			history.messages[userMessageId] = userMessage; | ||||||
|  | 			history.currentId = userMessageId; | ||||||
| 
 | 
 | ||||||
| 			prompt = ''; | 			prompt = ''; | ||||||
| 
 | 
 | ||||||
|  | @ -767,7 +952,8 @@ | ||||||
| 					}, | 					}, | ||||||
| 					title: 'New Chat', | 					title: 'New Chat', | ||||||
| 					timestamp: Date.now(), | 					timestamp: Date.now(), | ||||||
| 					messages: messages | 					messages: messages, | ||||||
|  | 					history: history | ||||||
| 				}); | 				}); | ||||||
| 				chats = await db.getAllFromIndex('chats', 'timestamp'); | 				chats = await db.getAllFromIndex('chats', 'timestamp'); | ||||||
| 			} | 			} | ||||||
|  | @ -776,7 +962,7 @@ | ||||||
| 				window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' }); | 				window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' }); | ||||||
| 			}, 50); | 			}, 50); | ||||||
| 
 | 
 | ||||||
| 			await sendPrompt(userPrompt); | 			await sendPrompt(userPrompt, userMessageId); | ||||||
| 
 | 
 | ||||||
| 			chats = await db.getAllFromIndex('chats', 'timestamp'); | 			chats = await db.getAllFromIndex('chats', 'timestamp'); | ||||||
| 		} | 		} | ||||||
|  | @ -791,7 +977,7 @@ | ||||||
| 			let userMessage = messages.at(-1); | 			let userMessage = messages.at(-1); | ||||||
| 			let userPrompt = userMessage.content; | 			let userPrompt = userMessage.content; | ||||||
| 
 | 
 | ||||||
| 			await sendPrompt(userPrompt); | 			await sendPrompt(userPrompt, userMessage.id); | ||||||
| 
 | 
 | ||||||
| 			chats = await db.getAllFromIndex('chats', 'timestamp'); | 			chats = await db.getAllFromIndex('chats', 'timestamp'); | ||||||
| 		} | 		} | ||||||
|  | @ -1078,7 +1264,7 @@ | ||||||
| 															<div class=" w-full"> | 															<div class=" w-full"> | ||||||
| 																<textarea | 																<textarea | ||||||
| 																	class=" bg-transparent outline-none w-full resize-none" | 																	class=" bg-transparent outline-none w-full resize-none" | ||||||
| 																	bind:value={message.editedContent} | 																	bind:value={history.messages[message.id].editedContent} | ||||||
| 																	on:input={(e) => { | 																	on:input={(e) => { | ||||||
| 																		e.target.style.height = ''; | 																		e.target.style.height = ''; | ||||||
| 																		e.target.style.height = `${e.target.scrollHeight}px`; | 																		e.target.style.height = `${e.target.scrollHeight}px`; | ||||||
|  | @ -1093,7 +1279,7 @@ | ||||||
| 																	<button | 																	<button | ||||||
| 																		class="px-4 py-2.5 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded-lg" | 																		class="px-4 py-2.5 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded-lg" | ||||||
| 																		on:click={() => { | 																		on:click={() => { | ||||||
| 																			confirmEditMessage(messageIdx); | 																			confirmEditMessage(message.id); | ||||||
| 																		}} | 																		}} | ||||||
| 																	> | 																	> | ||||||
| 																		Save & Submit | 																		Save & Submit | ||||||
|  | @ -1102,7 +1288,7 @@ | ||||||
| 																	<button | 																	<button | ||||||
| 																		class=" px-4 py-2.5 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-100 transition outline outline-1 outline-gray-200 dark:outline-gray-600 rounded-lg" | 																		class=" px-4 py-2.5 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-100 transition outline outline-1 outline-gray-200 dark:outline-gray-600 rounded-lg" | ||||||
| 																		on:click={() => { | 																		on:click={() => { | ||||||
| 																			cancelEditMessage(messageIdx); | 																			cancelEditMessage(message.id); | ||||||
| 																		}} | 																		}} | ||||||
| 																	> | 																	> | ||||||
| 																		Cancel | 																		Cancel | ||||||
|  | @ -1113,90 +1299,113 @@ | ||||||
| 															<div class="w-full"> | 															<div class="w-full"> | ||||||
| 																{message.content} | 																{message.content} | ||||||
| 
 | 
 | ||||||
| 																<!-- <div class=" flex justify-start space-x-1"> |  | ||||||
| 																	<div class="flex self-center"> |  | ||||||
| 																		<button |  | ||||||
| 																			class="self-center" |  | ||||||
| 																			on:click={() => { |  | ||||||
| 																				message.selectedContentIdx = Math.max( |  | ||||||
| 																					0, |  | ||||||
| 																					message.selectedContentIdx - 1 |  | ||||||
| 																				); |  | ||||||
| 																				messages = messages; |  | ||||||
| 																			}} |  | ||||||
| 																		> |  | ||||||
| 																			<svg |  | ||||||
| 																				xmlns="http://www.w3.org/2000/svg" |  | ||||||
| 																				viewBox="0 0 20 20" |  | ||||||
| 																				fill="currentColor" |  | ||||||
| 																				class="w-4 h-4" |  | ||||||
| 																			> |  | ||||||
| 																				<path |  | ||||||
| 																					fill-rule="evenodd" |  | ||||||
| 																					d="M12.79 5.23a.75.75 0 01-.02 1.06L8.832 10l3.938 3.71a.75.75 0 11-1.04 1.08l-4.5-4.25a.75.75 0 010-1.08l4.5-4.25a.75.75 0 011.06.02z" |  | ||||||
| 																					clip-rule="evenodd" |  | ||||||
| 																				/> |  | ||||||
| 																			</svg> |  | ||||||
| 																		</button> |  | ||||||
| 
 |  | ||||||
| 																		<div class="text-xs font-bold self-center"> |  | ||||||
| 																			{message.selectedContentIdx + 1} / {message.contents.length} |  | ||||||
| 																		</div> |  | ||||||
| 
 |  | ||||||
| 																		<button |  | ||||||
| 																			class="self-center" |  | ||||||
| 																			on:click={() => { |  | ||||||
| 																				message.selectedContentIdx = Math.min( |  | ||||||
| 																					message.contents.length - 1, |  | ||||||
| 																					message.selectedContentIdx + 1 |  | ||||||
| 																				); |  | ||||||
| 																				messages = messages; |  | ||||||
| 
 |  | ||||||
| 																				console.log(message); |  | ||||||
| 																			}} |  | ||||||
| 																		> |  | ||||||
| 																			<svg |  | ||||||
| 																				xmlns="http://www.w3.org/2000/svg" |  | ||||||
| 																				viewBox="0 0 20 20" |  | ||||||
| 																				fill="currentColor" |  | ||||||
| 																				class="w-4 h-4" |  | ||||||
| 																			> |  | ||||||
| 																				<path |  | ||||||
| 																					fill-rule="evenodd" |  | ||||||
| 																					d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" |  | ||||||
| 																					clip-rule="evenodd" |  | ||||||
| 																				/> |  | ||||||
| 																			</svg> |  | ||||||
| 																		</button> |  | ||||||
| 																	</div> |  | ||||||
| 																	<button |  | ||||||
| 																		class="invisible group-hover:visible p-1 rounded dark:hover:bg-gray-800 transition" |  | ||||||
| 																		on:click={() => { |  | ||||||
| 																			editMessage(messageIdx); |  | ||||||
| 																		}} |  | ||||||
| 																	> |  | ||||||
| 																		<svg |  | ||||||
| 																			xmlns="http://www.w3.org/2000/svg" |  | ||||||
| 																			fill="none" |  | ||||||
| 																			viewBox="0 0 24 24" |  | ||||||
| 																			stroke-width="1.5" |  | ||||||
| 																			stroke="currentColor" |  | ||||||
| 																			class="w-4 h-4" |  | ||||||
| 																		> |  | ||||||
| 																			<path |  | ||||||
| 																				stroke-linecap="round" |  | ||||||
| 																				stroke-linejoin="round" |  | ||||||
| 																				d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125" |  | ||||||
| 																			/> |  | ||||||
| 																		</svg> |  | ||||||
| 																	</button> |  | ||||||
| 																</div> --> |  | ||||||
| 
 |  | ||||||
| 																<div class=" flex justify-start space-x-1"> | 																<div class=" flex justify-start space-x-1"> | ||||||
|  | 																	{#if message.parentId !== null && message.parentId in history.messages && (history.messages[message.parentId]?.childrenIds.length ?? 0) > 1} | ||||||
|  | 																		<div class="flex self-center"> | ||||||
|  | 																			<button | ||||||
|  | 																				class="self-center" | ||||||
|  | 																				on:click={() => { | ||||||
|  | 																					showPreviousMessage(message); | ||||||
|  | 																				}} | ||||||
|  | 																			> | ||||||
|  | 																				<svg | ||||||
|  | 																					xmlns="http://www.w3.org/2000/svg" | ||||||
|  | 																					viewBox="0 0 20 20" | ||||||
|  | 																					fill="currentColor" | ||||||
|  | 																					class="w-4 h-4" | ||||||
|  | 																				> | ||||||
|  | 																					<path | ||||||
|  | 																						fill-rule="evenodd" | ||||||
|  | 																						d="M12.79 5.23a.75.75 0 01-.02 1.06L8.832 10l3.938 3.71a.75.75 0 11-1.04 1.08l-4.5-4.25a.75.75 0 010-1.08l4.5-4.25a.75.75 0 011.06.02z" | ||||||
|  | 																						clip-rule="evenodd" | ||||||
|  | 																					/> | ||||||
|  | 																				</svg> | ||||||
|  | 																			</button> | ||||||
|  | 
 | ||||||
|  | 																			<div class="text-xs font-bold self-center"> | ||||||
|  | 																				{history.messages[message.parentId].childrenIds.indexOf( | ||||||
|  | 																					message.id | ||||||
|  | 																				) + 1} / {history.messages[message.parentId].childrenIds | ||||||
|  | 																					.length} | ||||||
|  | 																			</div> | ||||||
|  | 
 | ||||||
|  | 																			<button | ||||||
|  | 																				class="self-center" | ||||||
|  | 																				on:click={() => { | ||||||
|  | 																					showNextMessage(message); | ||||||
|  | 																				}} | ||||||
|  | 																			> | ||||||
|  | 																				<svg | ||||||
|  | 																					xmlns="http://www.w3.org/2000/svg" | ||||||
|  | 																					viewBox="0 0 20 20" | ||||||
|  | 																					fill="currentColor" | ||||||
|  | 																					class="w-4 h-4" | ||||||
|  | 																				> | ||||||
|  | 																					<path | ||||||
|  | 																						fill-rule="evenodd" | ||||||
|  | 																						d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" | ||||||
|  | 																						clip-rule="evenodd" | ||||||
|  | 																					/> | ||||||
|  | 																				</svg> | ||||||
|  | 																			</button> | ||||||
|  | 																		</div> | ||||||
|  | 																	{:else if message.parentId === null && Object.values(history.messages).filter((message) => message.parentId === null).length > 1} | ||||||
|  | 																		<div class="flex self-center"> | ||||||
|  | 																			<button | ||||||
|  | 																				class="self-center" | ||||||
|  | 																				on:click={() => { | ||||||
|  | 																					showPreviousMessage(message); | ||||||
|  | 																				}} | ||||||
|  | 																			> | ||||||
|  | 																				<svg | ||||||
|  | 																					xmlns="http://www.w3.org/2000/svg" | ||||||
|  | 																					viewBox="0 0 20 20" | ||||||
|  | 																					fill="currentColor" | ||||||
|  | 																					class="w-4 h-4" | ||||||
|  | 																				> | ||||||
|  | 																					<path | ||||||
|  | 																						fill-rule="evenodd" | ||||||
|  | 																						d="M12.79 5.23a.75.75 0 01-.02 1.06L8.832 10l3.938 3.71a.75.75 0 11-1.04 1.08l-4.5-4.25a.75.75 0 010-1.08l4.5-4.25a.75.75 0 011.06.02z" | ||||||
|  | 																						clip-rule="evenodd" | ||||||
|  | 																					/> | ||||||
|  | 																				</svg> | ||||||
|  | 																			</button> | ||||||
|  | 
 | ||||||
|  | 																			<div class="text-xs font-bold self-center"> | ||||||
|  | 																				{Object.values(history.messages) | ||||||
|  | 																					.filter((message) => message.parentId === null) | ||||||
|  | 																					.map((message) => message.id) | ||||||
|  | 																					.indexOf(message.id) + 1} / {Object.values( | ||||||
|  | 																					history.messages | ||||||
|  | 																				).filter((message) => message.parentId === null).length} | ||||||
|  | 																			</div> | ||||||
|  | 
 | ||||||
|  | 																			<button | ||||||
|  | 																				class="self-center" | ||||||
|  | 																				on:click={() => { | ||||||
|  | 																					showNextMessage(message); | ||||||
|  | 																				}} | ||||||
|  | 																			> | ||||||
|  | 																				<svg | ||||||
|  | 																					xmlns="http://www.w3.org/2000/svg" | ||||||
|  | 																					viewBox="0 0 20 20" | ||||||
|  | 																					fill="currentColor" | ||||||
|  | 																					class="w-4 h-4" | ||||||
|  | 																				> | ||||||
|  | 																					<path | ||||||
|  | 																						fill-rule="evenodd" | ||||||
|  | 																						d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" | ||||||
|  | 																						clip-rule="evenodd" | ||||||
|  | 																					/> | ||||||
|  | 																				</svg> | ||||||
|  | 																			</button> | ||||||
|  | 																		</div> | ||||||
|  | 																	{/if} | ||||||
|  | 
 | ||||||
| 																	<button | 																	<button | ||||||
| 																		class="invisible group-hover:visible p-1 rounded dark:hover:bg-gray-800 transition" | 																		class="invisible group-hover:visible p-1 rounded dark:hover:bg-gray-800 transition" | ||||||
| 																		on:click={() => { | 																		on:click={() => { | ||||||
| 																			editMessage(messageIdx); | 																			editMessageHandler(message.id); | ||||||
| 																		}} | 																		}} | ||||||
| 																	> | 																	> | ||||||
| 																		<svg | 																		<svg | ||||||
|  | @ -1223,6 +1432,56 @@ | ||||||
| 
 | 
 | ||||||
| 															{#if message.done} | 															{#if message.done} | ||||||
| 																<div class=" flex justify-start space-x-1 -mt-2"> | 																<div class=" flex justify-start space-x-1 -mt-2"> | ||||||
|  | 																	{#if message.parentId !== null && message.parentId in history.messages && (history.messages[message.parentId]?.childrenIds.length ?? 0) > 1} | ||||||
|  | 																		<div class="flex self-center"> | ||||||
|  | 																			<button | ||||||
|  | 																				class="self-center" | ||||||
|  | 																				on:click={() => { | ||||||
|  | 																					showPreviousMessage(message); | ||||||
|  | 																				}} | ||||||
|  | 																			> | ||||||
|  | 																				<svg | ||||||
|  | 																					xmlns="http://www.w3.org/2000/svg" | ||||||
|  | 																					viewBox="0 0 20 20" | ||||||
|  | 																					fill="currentColor" | ||||||
|  | 																					class="w-4 h-4" | ||||||
|  | 																				> | ||||||
|  | 																					<path | ||||||
|  | 																						fill-rule="evenodd" | ||||||
|  | 																						d="M12.79 5.23a.75.75 0 01-.02 1.06L8.832 10l3.938 3.71a.75.75 0 11-1.04 1.08l-4.5-4.25a.75.75 0 010-1.08l4.5-4.25a.75.75 0 011.06.02z" | ||||||
|  | 																						clip-rule="evenodd" | ||||||
|  | 																					/> | ||||||
|  | 																				</svg> | ||||||
|  | 																			</button> | ||||||
|  | 
 | ||||||
|  | 																			<div class="text-xs font-bold self-center"> | ||||||
|  | 																				{history.messages[message.parentId].childrenIds.indexOf( | ||||||
|  | 																					message.id | ||||||
|  | 																				) + 1} / {history.messages[message.parentId].childrenIds | ||||||
|  | 																					.length} | ||||||
|  | 																			</div> | ||||||
|  | 
 | ||||||
|  | 																			<button | ||||||
|  | 																				class="self-center" | ||||||
|  | 																				on:click={() => { | ||||||
|  | 																					showNextMessage(message); | ||||||
|  | 																				}} | ||||||
|  | 																			> | ||||||
|  | 																				<svg | ||||||
|  | 																					xmlns="http://www.w3.org/2000/svg" | ||||||
|  | 																					viewBox="0 0 20 20" | ||||||
|  | 																					fill="currentColor" | ||||||
|  | 																					class="w-4 h-4" | ||||||
|  | 																				> | ||||||
|  | 																					<path | ||||||
|  | 																						fill-rule="evenodd" | ||||||
|  | 																						d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" | ||||||
|  | 																						clip-rule="evenodd" | ||||||
|  | 																					/> | ||||||
|  | 																				</svg> | ||||||
|  | 																			</button> | ||||||
|  | 																		</div> | ||||||
|  | 																	{/if} | ||||||
| 																	<button | 																	<button | ||||||
| 																		class="{messageIdx + 1 === messages.length | 																		class="{messageIdx + 1 === messages.length | ||||||
| 																			? 'visible' | 																			? 'visible' | ||||||
|  | @ -1344,6 +1603,7 @@ | ||||||
| 									class=" bg-white/20 p-1.5 rounded-full" | 									class=" bg-white/20 p-1.5 rounded-full" | ||||||
| 									on:click={() => { | 									on:click={() => { | ||||||
| 										window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' }); | 										window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' }); | ||||||
|  | 										autoScroll = true; | ||||||
| 									}} | 									}} | ||||||
| 								> | 								> | ||||||
| 									<svg | 									<svg | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Timothy J. Baek
						Timothy J. Baek