Skip to content

Instantly share code, notes, and snippets.

@juanmf
Last active March 18, 2026 15:42
Show Gist options
  • Select an option

  • Save juanmf/2b0e7864b17733766b43d7d0a5c94d00 to your computer and use it in GitHub Desktop.

Select an option

Save juanmf/2b0e7864b17733766b43d7d0a5c94d00 to your computer and use it in GitHub Desktop.
Manipulates DOM elements in Gemini chat to enable PDF friendly prints. By Default Cmd+P only prints what's visible in viewport (display). Copy+Paste in browser console. (Tampermonkey scripts won't run on Gemini). ** Scroll up to reveal older exchanges before running.
// Print Gemini
(function() {
// 1. SMART EXPAND
const expandIcons = document.querySelectorAll('mat-icon[fonticon="expand_more"]');
expandIcons.forEach(icon => {
const clickable = icon.closest('button') || icon.closest('[role="button"]');
if (clickable && clickable.getAttribute('aria-expanded') === 'false') {
const text = clickable.innerText.toLowerCase();
if (!text.includes('thinking')) clickable.click();
}
});
console.log("Expanding and deep-cleaning the DOM...");
setTimeout(() => {
// 2. CAPTURE CONTENT
const chatContainer = document.querySelector('message-list') || document.querySelector('main');
if (!chatContainer) return;
const clone = chatContainer.cloneNode(true);
// fix img
// Process all images inside the clone
clone.querySelectorAll('img').forEach(img => {
// Kill lazy loading and async decoding so the browser fetches them immediately
img.removeAttribute('loading');
img.removeAttribute('decoding');
// Some user-uploaded images use 'blob:' URLs or data attributes.
// This forces the clone to explicitly hold onto the exact source.
const realSrc = img.getAttribute('src') || img.dataset.src;
if (realSrc) {
img.setAttribute('src', realSrc);
}
});
// Replace 'button' in your selector list with 'button:not(:has(img))'
clone.querySelectorAll('input-container, user-profile-picture, side-navigation-content, thought-container, .thought-block, mat-icon, button:not(:has(img)), .action-buttons, footer, .input-area')
.forEach(el => el.remove());
// Remove the button wrapper but keep the image inside it
clone.querySelectorAll('button:has(img)').forEach(btn => {
const img = btn.querySelector('img');
if (img) {
btn.parentNode.replaceChild(img, btn);
}
});
// 3. TOTAL DOCUMENT RECONSTRUCTION
// Wiping head removes all of Gemini's complex positioning/scrolling CSS
document.head.replaceChildren();
// 4. INJECT FRESH, SIMPLE STYLE
const style = document.createElement('style');
style.textContent = `
html, body {
background: white !important;
color: black !important;
height: auto !important;
overflow: visible !important;
margin: 0 !important;
padding: 0 !important;
font-family: -apple-system, system-ui, sans-serif;
}
body {
padding: 40px !important;
}
#print-root { width: 100%; max-width: 850px; margin: 0 auto; }
style {
display: none !important;
}
/* FORCE VERTICAL STACKING: The Overlap Killer */
* {
position: static !important;
display: block !important;
height: auto !important;
width: auto !important;
overflow: visible !important;
float: none !important;
}
/* PRESERVE INLINE ELEMENTS: The Formula & Table Saver */
table, tr, td, th, .math-element, .MathJax, .mjx-container,
span, i, b, strong, a, svg {
display: revert !important;
}
.message-row, .message-content, model-response, .query-content {
margin-bottom: 40px !important;
padding-bottom: 20px;
border-bottom: 1px solid #eee;
}
/* Tables & Formulas */
table { border-collapse: collapse; width: 100%; margin: 20px 0; border: 1px solid #ddd; }
th, td { border: 1px solid #ddd; padding: 12px; text-align: left; }
.math-element, .MathJax {
margin: 10px 0;
}
pre, code {
white-space: pre-wrap !important;
word-break: break-all !important;
background: #f8f8f8;
padding: 15px;
margin: 15px 0;
border-radius: 4px;
}
/* Add to your existing CSS string */
img, .licensed-image, .preview-image {
max-width: 100% !important;
max-height: 400px !important; /* Limits height to roughly half a page */
object-fit: contain !important; /* Preserves aspect ratio without stretching */
display: block !important;
margin: 20px auto !important;
/* PDF Print Rules */
page-break-inside: avoid !important;
break-inside: avoid !important;
}
@media print {
@page { margin: 1.5cm; }
}

`;
// 5. RE-BUILD BODY
const printRoot = document.createElement('div');
printRoot.id = 'print-root';
printRoot.appendChild(clone);
document.head.appendChild(style);
document.body.replaceChildren(printRoot);
console.log("Ready. If print dialog doesn't open, hit Cmd+P.");
setTimeout(() => { window.print(); }, 3000);
}, 1000);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment