您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Sends a desktop notification when the ChatGPT response/answer has finished generating. It allows for toggle button dragging, memorizes the toggle button position, prevents the toggle button from going out of window bounds, and remembers the state.
// ==UserScript== // @name ChatGPT desktop notification for completed response/answer // @author NWP // @description Sends a desktop notification when the ChatGPT response/answer has finished generating. It allows for toggle button dragging, memorizes the toggle button position, prevents the toggle button from going out of window bounds, and remembers the state. // @namespace https://gf.qytechs.cn/users/877912 // @version 0.3 // @license MIT // @match https://chatgpt.com/* // @grant GM_notification // @grant GM_getValue // @grant GM_setValue // ==/UserScript== (function() { 'use strict'; let stopButtonDetected = false; let notificationsEnabled = GM_getValue('notificationsEnabled', true); function sendNotification(title, text) { if (notificationsEnabled) { GM_notification({ title: title, text: text, timeout: 5000 }); } } function checkButtonState() { const stopButton = document.querySelector('button.mb-1.me-1.flex.h-8.w-8.items-center.justify-center.rounded-full.bg-black.text-white:not([disabled]) svg.icon-lg rect'); if (stopButton) { if (!stopButtonDetected) { stopButtonDetected = true; } } else if (stopButtonDetected) { sendNotification('Answer finished', 'The ChatGPT answer completed.'); stopButtonDetected = false; } } const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.type === 'childList' || mutation.type === 'attributes') { checkButtonState(); } }); }); const config = { attributes: true, childList: true, subtree: true, attributeFilter: ['disabled', 'hidden'] }; observer.observe(document.body, config); checkButtonState(); function toggleNotifications() { notificationsEnabled = !notificationsEnabled; GM_setValue('notificationsEnabled', notificationsEnabled); updateToggleButton(); } function updateToggleButton() { const buttonText = notificationsEnabled ? "ON" : "OFF"; document.getElementById('notificationToggle').textContent = `Answer notification: ${buttonText}`; } function updateButtonPosition() { const button = document.getElementById('notificationToggle'); const viewportWidth = window.innerWidth; const viewportHeight = window.innerHeight; const leftPercent = GM_getValue('buttonLeftPercent', 90); const topPercent = GM_getValue('buttonTopPercent', 90); button.style.left = `${leftPercent}%`; button.style.top = `${topPercent}%`; const buttonRect = button.getBoundingClientRect(); if (buttonRect.right > viewportWidth) { button.style.left = `${viewportWidth - buttonRect.width}px`; } if (buttonRect.bottom > viewportHeight) { button.style.top = `${viewportHeight - buttonRect.height}px`; } } function createToggleButton() { const button = document.createElement('div'); button.id = 'notificationToggle'; button.style.position = 'fixed'; button.style.width = '11.875em'; button.style.height = '2.5em'; button.style.padding = '0.5em'; button.style.backgroundColor = '#444'; button.style.color = '#fff'; button.style.borderRadius = '0.5em'; button.style.cursor = 'pointer'; button.style.zIndex = 10000; button.style.userSelect = 'none'; button.style.MozUserSelect = 'none'; button.style.WebkitUserSelect = 'none'; button.textContent = `Answer notification: ${notificationsEnabled ? "ON" : "OFF"}`; button.addEventListener('click', toggleNotifications); let isDragging = false; let offsetX, offsetY; button.addEventListener('mousedown', (e) => { isDragging = true; offsetX = e.clientX - button.getBoundingClientRect().left; offsetY = e.clientY - button.getBoundingClientRect().top; button.style.cursor = 'move'; }); document.addEventListener('mousemove', (e) => { if (isDragging) { const newLeft = e.clientX - offsetX; const newTop = e.clientY - offsetY; const buttonWidth = button.offsetWidth; const buttonHeight = button.offsetHeight; const viewportWidth = window.innerWidth; const viewportHeight = window.innerHeight; if (newLeft < 0) { button.style.left = '0px'; } else if (newLeft + buttonWidth > viewportWidth) { button.style.left = `${viewportWidth - buttonWidth}px`; } else { button.style.left = `${newLeft}px`; } if (newTop < 0) { button.style.top = '0px'; } else if (newTop + buttonHeight > viewportHeight) { button.style.top = `${viewportHeight - buttonHeight}px`; } else { button.style.top = `${newTop}px`; } } }); document.addEventListener('mouseup', () => { if (isDragging) { isDragging = false; button.style.cursor = 'pointer'; const viewportWidth = window.innerWidth; const viewportHeight = window.innerHeight; const leftPercent = (parseInt(button.style.left) / viewportWidth) * 100; const topPercent = (parseInt(button.style.top) / viewportHeight) * 100; GM_setValue('buttonLeftPercent', leftPercent); GM_setValue('buttonTopPercent', topPercent); } }); window.addEventListener('resize', updateButtonPosition); document.body.appendChild(button); updateButtonPosition(); } createToggleButton(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址