Created
January 19, 2026 13:43
-
-
Save andredtr/e97265d3a9d53145d04ba93c5f1eaefc to your computer and use it in GitHub Desktop.
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
| // Open the article and run this into your developer console | |
| (function() { | |
| // --- 1. STYLING --- | |
| const pdfStyles = ` | |
| body { | |
| font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; | |
| line-height: 1.6; | |
| color: #111; | |
| max-width: 800px; | |
| margin: 0 auto; | |
| padding: 40px; | |
| } | |
| /* HIDE THE BUTTON IN THE ACTUAL PDF */ | |
| @media print { | |
| .no-print { display: none !important; } | |
| body { padding: 0; } | |
| } | |
| /* BUTTON STYLING */ | |
| .control-bar { | |
| background: #f0f2f5; | |
| padding: 15px; | |
| border-radius: 8px; | |
| margin-bottom: 30px; | |
| text-align: center; | |
| border: 1px solid #ddd; | |
| } | |
| .print-btn { | |
| background-color: #000; | |
| color: #fff; | |
| border: none; | |
| padding: 10px 20px; | |
| font-size: 16px; | |
| font-weight: bold; | |
| border-radius: 20px; | |
| cursor: pointer; | |
| transition: opacity 0.2s; | |
| } | |
| .print-btn:hover { opacity: 0.8; } | |
| .help-text { display: block; margin-top: 8px; font-size: 12px; color: #666; } | |
| /* CONTENT STYLING */ | |
| h1 { font-size: 32px; margin-bottom: 10px; line-height: 1.2; } | |
| .meta { color: #666; font-size: 14px; margin-bottom: 30px; border-bottom: 1px solid #eee; padding-bottom: 20px; } | |
| img.cover-image { | |
| width: 100%; | |
| max-height: 500px; | |
| object-fit: cover; | |
| border-radius: 12px; | |
| margin-bottom: 30px; | |
| } | |
| img.body-image { | |
| max-width: 100%; | |
| height: auto; | |
| border-radius: 8px; | |
| margin: 20px 0; | |
| display: block; | |
| } | |
| p { margin-bottom: 18px; font-size: 17px; } | |
| h2 { margin-top: 30px; font-size: 24px; border-bottom: 1px solid #eee; padding-bottom: 8px; } | |
| a { color: #1d9bf0; text-decoration: none; } | |
| ul, ol { margin-bottom: 20px; padding-left: 20px; } | |
| li { margin-bottom: 8px; } | |
| `; | |
| // --- 2. HELPERS --- | |
| function getArticleTitle() { | |
| const titleEl = document.querySelector('[data-testid="twitter-article-title"]'); | |
| return titleEl ? titleEl.innerText.trim() : document.title.replace(' / X', ''); | |
| } | |
| function getCoverImageHtml() { | |
| const readView = document.querySelector('[data-testid="twitterArticleReadView"]'); | |
| if (!readView) return ""; | |
| const photoContainer = readView.querySelector('[data-testid="tweetPhoto"] img'); | |
| if (photoContainer) { | |
| return `<img src="${photoContainer.src}" class="cover-image" alt="Article Cover">`; | |
| } | |
| return ""; | |
| } | |
| function parseNodeToHtml(node) { | |
| if (node.nodeType === Node.TEXT_NODE) return node.textContent; | |
| if (node.nodeType !== Node.ELEMENT_NODE) return ""; | |
| let content = ""; | |
| Array.from(node.childNodes).forEach(child => { | |
| content += parseNodeToHtml(child); | |
| }); | |
| const tag = node.tagName.toLowerCase(); | |
| const style = window.getComputedStyle(node); | |
| if (tag === 'h1') return `<h2>${content}</h2>`; | |
| if (tag === 'h2') return `<h3>${content}</h3>`; | |
| if (tag === 'b' || tag === 'strong' || parseInt(style.fontWeight) > 500) { | |
| return content.trim() ? `<strong>${content}</strong>` : content; | |
| } | |
| if (tag === 'i' || tag === 'em' || style.fontStyle === 'italic') { | |
| return content.trim() ? `<em>${content}</em>` : content; | |
| } | |
| if (tag === 'a') { | |
| const href = node.getAttribute('href'); | |
| const fullHref = href && href.startsWith('/') ? `https://x.com${href}` : href; | |
| return `<a href="${fullHref}">${content}</a>`; | |
| } | |
| if (tag === 'img') { | |
| const src = node.getAttribute('src'); | |
| if (node.closest('[data-testid="tweetPhoto"]')) { | |
| return `<img src="${src}" class="body-image" />`; | |
| } | |
| return ""; | |
| } | |
| if (tag === 'li') return `<li>${content}</li>`; | |
| if (tag === 'ul') return `<ul>${content}</ul>`; | |
| if (tag === 'ol') return `<ol>${content}</ol>`; | |
| if (node.getAttribute('data-block') === 'true') return `<p>${content}</p>`; | |
| return content; | |
| } | |
| // --- 3. GENERATE --- | |
| console.log("📸 Generating PDF View..."); | |
| const articleBody = document.querySelector('[data-testid="twitterArticleRichTextView"]'); | |
| if (!articleBody) { | |
| alert("❌ Error: Open the article first."); | |
| return; | |
| } | |
| const title = getArticleTitle(); | |
| const coverImageHtml = getCoverImageHtml(); | |
| const bodyHtml = parseNodeToHtml(articleBody); | |
| const fullHtml = ` | |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <title>${title}</title> | |
| <style>${pdfStyles}</style> | |
| </head> | |
| <body> | |
| <div class="control-bar no-print"> | |
| <button class="print-btn" onclick="window.print()">🖨️ Download / Save as PDF</button> | |
| <span class="help-text">Click the button above or press Ctrl+P (Cmd+P on Mac)</span> | |
| </div> | |
| ${coverImageHtml} | |
| <h1>${title}</h1> | |
| <div class="meta"> | |
| <strong>Source:</strong> <a href="${window.location.href}">X.com Article</a> | |
| </div> | |
| ${bodyHtml} | |
| <script> | |
| // Try to trigger print automatically after a short delay | |
| window.onload = function() { | |
| setTimeout(() => { | |
| window.print(); | |
| }, 1000); | |
| }; | |
| </script> | |
| </body> | |
| </html> | |
| `; | |
| const printWindow = window.open('', '_blank'); | |
| if (printWindow) { | |
| printWindow.document.write(fullHtml); | |
| printWindow.document.close(); // IMPORTANT: This tells the browser loading is done | |
| } else { | |
| alert("⚠️ Please allow pop-ups for this site."); | |
| } | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment