Last active
March 18, 2026 15:42
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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