Ver Fonte

feat: response autocopy

Timothy J. Baek há 1 ano atrás
pai
commit
6de9db6772

+ 49 - 0
src/lib/components/chat/SettingsModal.svelte

@@ -52,6 +52,7 @@
 	// Addons
 	// Addons
 	let titleAutoGenerate = true;
 	let titleAutoGenerate = true;
 	let speechAutoSend = false;
 	let speechAutoSend = false;
+	let responseAutoCopy = false;
 
 
 	let gravatarEmail = '';
 	let gravatarEmail = '';
 	let OPENAI_API_KEY = '';
 	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 () => {
 	const toggleAuthHeader = async () => {
 		authEnabled = !authEnabled;
 		authEnabled = !authEnabled;
 	};
 	};
@@ -319,6 +342,8 @@
 		console.log(settings);
 		console.log(settings);
 
 
 		theme = localStorage.theme ?? 'dark';
 		theme = localStorage.theme ?? 'dark';
+		notificationEnabled = settings.notificationEnabled ?? false;
+
 		API_BASE_URL = settings.API_BASE_URL ?? OLLAMA_API_BASE_URL;
 		API_BASE_URL = settings.API_BASE_URL ?? OLLAMA_API_BASE_URL;
 		system = settings.system ?? '';
 		system = settings.system ?? '';
 
 
@@ -334,6 +359,8 @@
 
 
 		titleAutoGenerate = settings.titleAutoGenerate ?? true;
 		titleAutoGenerate = settings.titleAutoGenerate ?? true;
 		speechAutoSend = settings.speechAutoSend ?? false;
 		speechAutoSend = settings.speechAutoSend ?? false;
+		responseAutoCopy = settings.responseAutoCopy ?? false;
+
 		gravatarEmail = settings.gravatarEmail ?? '';
 		gravatarEmail = settings.gravatarEmail ?? '';
 		OPENAI_API_KEY = settings.OPENAI_API_KEY ?? '';
 		OPENAI_API_KEY = settings.OPENAI_API_KEY ?? '';
 
 
@@ -887,6 +914,28 @@
 										</button>
 										</button>
 									</div>
 									</div>
 								</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>
 							</div>
 
 
 							<hr class=" dark:border-gray-700" />
 							<hr class=" dark:border-gray-700" />

+ 35 - 0
src/lib/utils/index.ts

@@ -65,3 +65,38 @@ export const getGravatarURL = (email) => {
 	// Grab the actual image URL
 	// Grab the actual image URL
 	return `https://www.gravatar.com/avatar/${hash}`;
 	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);
+		}
+	);
+};

+ 51 - 0
src/routes/(app)/+page.svelte

@@ -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
 	// Ollama functions
 	//////////////////////////
 	//////////////////////////
@@ -236,6 +271,10 @@
 										}
 										}
 									);
 									);
 								}
 								}
+
+								if ($settings.responseAutoCopy) {
+									copyToClipboard(responseMessage.content);
+								}
 							}
 							}
 						}
 						}
 					}
 					}
@@ -440,6 +479,18 @@
 				stopResponseFlag = false;
 				stopResponseFlag = false;
 
 
 				await tick();
 				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) {
 				if (autoScroll) {
 					window.scrollTo({ top: document.body.scrollHeight });
 					window.scrollTo({ top: document.body.scrollHeight });
 				}
 				}

+ 51 - 0
src/routes/(app)/c/[id]/+page.svelte

@@ -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
 	// Ollama functions
 	//////////////////////////
 	//////////////////////////
@@ -250,6 +285,10 @@
 										}
 										}
 									);
 									);
 								}
 								}
+
+								if ($settings.responseAutoCopy) {
+									copyToClipboard(responseMessage.content);
+								}
 							}
 							}
 						}
 						}
 					}
 					}
@@ -454,6 +493,18 @@
 				stopResponseFlag = false;
 				stopResponseFlag = false;
 
 
 				await tick();
 				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) {
 				if (autoScroll) {
 					window.scrollTo({ top: document.body.scrollHeight });
 					window.scrollTo({ top: document.body.scrollHeight });
 				}
 				}