Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save aleclarson/259c6b778deb04079f1ab4319c306e26 to your computer and use it in GitHub Desktop.
Save aleclarson/259c6b778deb04079f1ab4319c306e26 to your computer and use it in GitHub Desktop.
Google AI Studio: Finished notification – Tampermonkey Userscript
// ==UserScript==
// @name Gemini Finished Notification
// @namespace http://tampermonkey.net/
// @version 0.3
// @description Notify when Gemini is done loading a model response
// @author Claude 3
// @match https://aistudio.google.com/app/prompts/*
// @grant GM_notification
// @grant GM_getTab
// @grant GM_openInTab
// ==/UserScript==
(function () {
"use strict";
function waitForElement(selector, timeout = 10000) {
return new Promise((resolve, reject) => {
const interval = setInterval(() => {
const element = document.querySelector(selector);
if (element) {
clearInterval(interval);
resolve(element);
}
}, 100);
setTimeout(() => {
clearInterval(interval);
reject(new Error(`Timed out waiting for element: ${selector}`));
}, timeout);
});
}
async function observeLoadingIndicator() {
const targetNode = await waitForElement(".prompt-content");
let hasLoadingIndicator = false;
let notifyTimeout;
const notifySoon = (content) => {
notifyTimeout = setTimeout(() => {
GM_notification({
title: "Gemini is done",
text: content ? content.textContent.slice(0, 100) + '…' : 'Come see what Gemini wrote!',
silent: false,
onclick: function () {
window.focus();
}
});
// Allow another notification to be sent.
hasLoadingIndicator = false;
notifyTimeout = null;
}, 1000);
}
const observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
if (mutation.type === "childList") {
const content =
mutation.target.querySelector(".model-response-contents") ||
(mutation.target.matches(".model-response-contents") ? mutation.target : null);
if (!content) {
return;
}
if (notifyTimeout) {
clearTimeout(notifyTimeout);
notifySoon(content);
} else {
const loadingIndicator = content.querySelector(
"loading-indicator"
);
if (hasLoadingIndicator && !loadingIndicator) {
notifySoon(content);
}
hasLoadingIndicator = !!loadingIndicator;
}
}
});
});
observer.observe(targetNode, { childList: true, subtree: true });
}
observeLoadingIndicator();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment