Skip to content

Instantly share code, notes, and snippets.

@andredtr
Created January 19, 2026 13:43
Show Gist options
  • Select an option

  • Save andredtr/e97265d3a9d53145d04ba93c5f1eaefc to your computer and use it in GitHub Desktop.

Select an option

Save andredtr/e97265d3a9d53145d04ba93c5f1eaefc to your computer and use it in GitHub Desktop.
// 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