Skip to content

Instantly share code, notes, and snippets.

@cubimon
Created March 22, 2025 00:27
Show Gist options
  • Save cubimon/cd0942c7d44768df044fa7587a071fe0 to your computer and use it in GitHub Desktop.
Save cubimon/cd0942c7d44768df044fa7587a071fe0 to your computer and use it in GitHub Desktop.
Gemini Clipboard Userscript
// ==UserScript==
// @name Gemini Clipboard
// @namespace http://tampermonkey.net/
// @version 2025-03-21
// @description try to take over the world!
// @author You
// @match https://gemini.google.com/app/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=google.com
// @grant none
// ==/UserScript==
(function() {
'use strict';
function waitUntil(check, node, config) {
return new Promise((resolve, reject) => {
if (!config) {
config = { childList: true, subtree: true, attributes: true };
}
if (!node) {
node = document.body;
}
// Callback function to execute when mutations are observed
let observer = null;
const callback = (mutationList, observer) => {
for (const mutation of mutationList) {
if (check(mutation, resolve, observer)) {
return;
}
}
};
// Create an observer instance linked to the callback function
observer = new MutationObserver(callback);
// Start observing the target node for configured mutations
observer.observe(node, config);
});
}
function onResponseContainer(container) {
return waitUntil((mutation, resolve, observer) => {
if (mutation.target.classList.contains('buttons-container-v2')) {
resolve(mutation.target);
observer.disconnect();
return true;
}
}, container, { childList: true, subtree: true });
}
async function clipboardCopy(button) {
const message = button.parentElement.parentElement.parentElement.parentElement.parentElement.innerText;
await navigator.clipboard.writeText(message);
}
function createButton(parentElement) {
console.log(parentElement);
const button = document.createElement('button');
button.classList.add('mdc-button', 'mat-mdc-button-base', 'mat-mdc-menu-trigger', 'mat-mdc-tooltip-trigger', 'icon-button', 'mat-mdc-button', 'mat-unthemed');
button.style = 'width: 32px; height: 32px; min-width: 32px; min-height: 32px; padding: 0; margin: 0;';
const span1 = document.createElement('span');
span1.classList.add('mat-mdc-button-persistent-ripple', 'mdc-icon-button__ripple');
//span.style = 'width: 32px; height: 32px;';
const icon = document.createElement('mat-icon');
icon.classList.add('mat-icon', 'notranslate', 'refresh-icon', 'gds-icon-m', 'google-symbols', 'mat-ligature-font', 'mat-icon-no-color', 'ng-star-inserted');
icon.setAttribute('fonticon', 'content_copy');
icon.setAttribute('data-mat-icon-type', 'font');
icon.setAttribute('data-mat-icon-name', 'content_copy');
icon.style = 'font-size: 1.3125rem; padding: 0; margin: 0; vertical-align: middle; color: var(--gem-sys-color--on-surface-variant);';
const span2 = document.createElement('span');
span2.classList.add('mat-focus-indicator');
const span3 = document.createElement('span');
span3.classList.add('mat-mdc-button-touch-target');
const span4 = document.createElement('span');
span4.classList.add('mat-ripple', 'mat-mdc-button-ripple');
button.appendChild(span1);
button.appendChild(icon);
button.appendChild(span2);
button.appendChild(span3);
button.appendChild(span4);
button.addEventListener('click', async () => await clipboardCopy(button));
return button;
}
function onConversationContainer(container) {
waitUntil((mutation, resolve, observer) => {
if (mutation.target.classList.contains('response-container')) {
onResponseContainer(mutation.target).then((buttoncontainer) => {
buttoncontainer.insertBefore(
createButton(buttoncontainer),
buttoncontainer.children[buttoncontainer.children.length - 2]);
});
}
}, container, { childList: true, subtree: true });
}
waitUntil((mutation, resolve, observer) => {
if (mutation.target.nodeName === 'INFINITE-SCROLLER') {
observer.disconnect();
resolve(mutation.target);
return true;
}
}, document.body, { childList: true, subtree: true }).then(onConversationContainer);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment