forked from open-webui/open-webui
		
	feat: response autocopy
This commit is contained in:
		
							parent
							
								
									47b7b63791
								
							
						
					
					
						commit
						6de9db6772
					
				
					 4 changed files with 186 additions and 0 deletions
				
			
		|  | @ -52,6 +52,7 @@ | |||
| 	// Addons | ||||
| 	let titleAutoGenerate = true; | ||||
| 	let speechAutoSend = false; | ||||
| 	let responseAutoCopy = false; | ||||
| 
 | ||||
| 	let gravatarEmail = ''; | ||||
| 	let OPENAI_API_KEY = ''; | ||||
|  | @ -123,6 +124,28 @@ | |||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
| 	const toggleResponseAutoCopy = async () => { | ||||
| 		const permission = await navigator.clipboard | ||||
| 			.readText() | ||||
| 			.then(() => { | ||||
| 				return 'granted'; | ||||
| 			}) | ||||
| 			.catch(() => { | ||||
| 				return ''; | ||||
| 			}); | ||||
| 
 | ||||
| 		console.log(permission); | ||||
| 
 | ||||
| 		if (permission === 'granted') { | ||||
| 			responseAutoCopy = !responseAutoCopy; | ||||
| 			saveSettings({ responseAutoCopy: responseAutoCopy }); | ||||
| 		} else { | ||||
| 			toast.error( | ||||
| 				'Clipboard write permission denied. Please check your browser settings to grant the necessary access.' | ||||
| 			); | ||||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
| 	const toggleAuthHeader = async () => { | ||||
| 		authEnabled = !authEnabled; | ||||
| 	}; | ||||
|  | @ -319,6 +342,8 @@ | |||
| 		console.log(settings); | ||||
| 
 | ||||
| 		theme = localStorage.theme ?? 'dark'; | ||||
| 		notificationEnabled = settings.notificationEnabled ?? false; | ||||
| 
 | ||||
| 		API_BASE_URL = settings.API_BASE_URL ?? OLLAMA_API_BASE_URL; | ||||
| 		system = settings.system ?? ''; | ||||
| 
 | ||||
|  | @ -334,6 +359,8 @@ | |||
| 
 | ||||
| 		titleAutoGenerate = settings.titleAutoGenerate ?? true; | ||||
| 		speechAutoSend = settings.speechAutoSend ?? false; | ||||
| 		responseAutoCopy = settings.responseAutoCopy ?? false; | ||||
| 
 | ||||
| 		gravatarEmail = settings.gravatarEmail ?? ''; | ||||
| 		OPENAI_API_KEY = settings.OPENAI_API_KEY ?? ''; | ||||
| 
 | ||||
|  | @ -887,6 +914,28 @@ | |||
| 										</button> | ||||
| 									</div> | ||||
| 								</div> | ||||
| 
 | ||||
| 								<div> | ||||
| 									<div class=" py-0.5 flex w-full justify-between"> | ||||
| 										<div class=" self-center text-xs font-medium"> | ||||
| 											Response AutoCopy to Clipboard | ||||
| 										</div> | ||||
| 
 | ||||
| 										<button | ||||
| 											class="p-1 px-3 text-xs flex rounded transition" | ||||
| 											on:click={() => { | ||||
| 												toggleResponseAutoCopy(); | ||||
| 											}} | ||||
| 											type="button" | ||||
| 										> | ||||
| 											{#if responseAutoCopy === true} | ||||
| 												<span class="ml-2 self-center">On</span> | ||||
| 											{:else} | ||||
| 												<span class="ml-2 self-center">Off</span> | ||||
| 											{/if} | ||||
| 										</button> | ||||
| 									</div> | ||||
| 								</div> | ||||
| 							</div> | ||||
| 
 | ||||
| 							<hr class=" dark:border-gray-700" /> | ||||
|  |  | |||
|  | @ -65,3 +65,38 @@ export const getGravatarURL = (email) => { | |||
| 	// Grab the actual image URL
 | ||||
| 	return `https://www.gravatar.com/avatar/${hash}`; | ||||
| }; | ||||
| 
 | ||||
| const copyToClipboard = (text) => { | ||||
| 	if (!navigator.clipboard) { | ||||
| 		var textArea = document.createElement('textarea'); | ||||
| 		textArea.value = text; | ||||
| 
 | ||||
| 		// Avoid scrolling to bottom
 | ||||
| 		textArea.style.top = '0'; | ||||
| 		textArea.style.left = '0'; | ||||
| 		textArea.style.position = 'fixed'; | ||||
| 
 | ||||
| 		document.body.appendChild(textArea); | ||||
| 		textArea.focus(); | ||||
| 		textArea.select(); | ||||
| 
 | ||||
| 		try { | ||||
| 			var successful = document.execCommand('copy'); | ||||
| 			var msg = successful ? 'successful' : 'unsuccessful'; | ||||
| 			console.log('Fallback: Copying text command was ' + msg); | ||||
| 		} catch (err) { | ||||
| 			console.error('Fallback: Oops, unable to copy', err); | ||||
| 		} | ||||
| 
 | ||||
| 		document.body.removeChild(textArea); | ||||
| 		return; | ||||
| 	} | ||||
| 	navigator.clipboard.writeText(text).then( | ||||
| 		function () { | ||||
| 			console.log('Async: Copying to clipboard was successful!'); | ||||
| 		}, | ||||
| 		function (err) { | ||||
| 			console.error('Async: Could not copy text: ', err); | ||||
| 		} | ||||
| 	); | ||||
| }; | ||||
|  |  | |||
|  | @ -88,6 +88,41 @@ | |||
| 		}); | ||||
| 	}; | ||||
| 
 | ||||
| 	const copyToClipboard = (text) => { | ||||
| 		if (!navigator.clipboard) { | ||||
| 			var textArea = document.createElement('textarea'); | ||||
| 			textArea.value = text; | ||||
| 
 | ||||
| 			// Avoid scrolling to bottom | ||||
| 			textArea.style.top = '0'; | ||||
| 			textArea.style.left = '0'; | ||||
| 			textArea.style.position = 'fixed'; | ||||
| 
 | ||||
| 			document.body.appendChild(textArea); | ||||
| 			textArea.focus(); | ||||
| 			textArea.select(); | ||||
| 
 | ||||
| 			try { | ||||
| 				var successful = document.execCommand('copy'); | ||||
| 				var msg = successful ? 'successful' : 'unsuccessful'; | ||||
| 				console.log('Fallback: Copying text command was ' + msg); | ||||
| 			} catch (err) { | ||||
| 				console.error('Fallback: Oops, unable to copy', err); | ||||
| 			} | ||||
| 
 | ||||
| 			document.body.removeChild(textArea); | ||||
| 			return; | ||||
| 		} | ||||
| 		navigator.clipboard.writeText(text).then( | ||||
| 			function () { | ||||
| 				console.log('Async: Copying to clipboard was successful!'); | ||||
| 			}, | ||||
| 			function (err) { | ||||
| 				console.error('Async: Could not copy text: ', err); | ||||
| 			} | ||||
| 		); | ||||
| 	}; | ||||
| 
 | ||||
| 	////////////////////////// | ||||
| 	// Ollama functions | ||||
| 	////////////////////////// | ||||
|  | @ -236,6 +271,10 @@ | |||
| 										} | ||||
| 									); | ||||
| 								} | ||||
| 
 | ||||
| 								if ($settings.responseAutoCopy) { | ||||
| 									copyToClipboard(responseMessage.content); | ||||
| 								} | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
|  | @ -440,6 +479,18 @@ | |||
| 				stopResponseFlag = false; | ||||
| 
 | ||||
| 				await tick(); | ||||
| 
 | ||||
| 				if ($settings.notificationEnabled && !document.hasFocus()) { | ||||
| 					const notification = new Notification(`OpenAI ${model}`, { | ||||
| 						body: responseMessage.content, | ||||
| 						icon: '/favicon.png' | ||||
| 					}); | ||||
| 				} | ||||
| 
 | ||||
| 				if ($settings.responseAutoCopy) { | ||||
| 					copyToClipboard(responseMessage.content); | ||||
| 				} | ||||
| 
 | ||||
| 				if (autoScroll) { | ||||
| 					window.scrollTo({ top: document.body.scrollHeight }); | ||||
| 				} | ||||
|  |  | |||
|  | @ -102,6 +102,41 @@ | |||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
| 	const copyToClipboard = (text) => { | ||||
| 		if (!navigator.clipboard) { | ||||
| 			var textArea = document.createElement('textarea'); | ||||
| 			textArea.value = text; | ||||
| 
 | ||||
| 			// Avoid scrolling to bottom | ||||
| 			textArea.style.top = '0'; | ||||
| 			textArea.style.left = '0'; | ||||
| 			textArea.style.position = 'fixed'; | ||||
| 
 | ||||
| 			document.body.appendChild(textArea); | ||||
| 			textArea.focus(); | ||||
| 			textArea.select(); | ||||
| 
 | ||||
| 			try { | ||||
| 				var successful = document.execCommand('copy'); | ||||
| 				var msg = successful ? 'successful' : 'unsuccessful'; | ||||
| 				console.log('Fallback: Copying text command was ' + msg); | ||||
| 			} catch (err) { | ||||
| 				console.error('Fallback: Oops, unable to copy', err); | ||||
| 			} | ||||
| 
 | ||||
| 			document.body.removeChild(textArea); | ||||
| 			return; | ||||
| 		} | ||||
| 		navigator.clipboard.writeText(text).then( | ||||
| 			function () { | ||||
| 				console.log('Async: Copying to clipboard was successful!'); | ||||
| 			}, | ||||
| 			function (err) { | ||||
| 				console.error('Async: Could not copy text: ', err); | ||||
| 			} | ||||
| 		); | ||||
| 	}; | ||||
| 
 | ||||
| 	////////////////////////// | ||||
| 	// Ollama functions | ||||
| 	////////////////////////// | ||||
|  | @ -250,6 +285,10 @@ | |||
| 										} | ||||
| 									); | ||||
| 								} | ||||
| 
 | ||||
| 								if ($settings.responseAutoCopy) { | ||||
| 									copyToClipboard(responseMessage.content); | ||||
| 								} | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
|  | @ -454,6 +493,18 @@ | |||
| 				stopResponseFlag = false; | ||||
| 
 | ||||
| 				await tick(); | ||||
| 
 | ||||
| 				if ($settings.notificationEnabled && !document.hasFocus()) { | ||||
| 					const notification = new Notification(`OpenAI ${model}`, { | ||||
| 						body: responseMessage.content, | ||||
| 						icon: '/favicon.png' | ||||
| 					}); | ||||
| 				} | ||||
| 
 | ||||
| 				if ($settings.responseAutoCopy) { | ||||
| 					copyToClipboard(responseMessage.content); | ||||
| 				} | ||||
| 
 | ||||
| 				if (autoScroll) { | ||||
| 					window.scrollTo({ top: document.body.scrollHeight }); | ||||
| 				} | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Timothy J. Baek
						Timothy J. Baek