On X Quote Posts pages, show inner Quote content once at top, then all Quote Posts as Replies. Waits for image loads, ensures posts remain clickable.
// ==UserScript==
// @name X Quotes Cleaner
// @namespace https://x.com/quote-cleaner
// @version 5.7
// @description On X Quote Posts pages, show inner Quote content once at top, then all Quote Posts as Replies. Waits for image loads, ensures posts remain clickable.
// @author Grok (complained at by nanimonull)
// @match https://x.com/*/status/*/quotes
// @match https://twitter.com/*/status/*/quotes
// @grant none
// @run-at document-end
// @license MIT
// ==/UserScript==
(function () {
'use strict';
let originalMoved = false;
console.log('✅ X Quotes Cleaner v5.6 - Perfect final version');
function logImages(innerBlock) {
console.log('[Quotes Cleaner Image Debug] === Images in inner quoted block ===');
const pfp = innerBlock.querySelector('img[src*="profile_images"]');
console.log('[Quotes Cleaner Image Debug] PFP:', pfp ? pfp.src : 'NONE');
const photos = innerBlock.querySelectorAll('img[src*="media/"]');
photos.forEach((img, i) => {
console.log(`[Quotes Cleaner Image Debug] Photo ${i+1}:`, img.src || 'NONE');
});
return !!(pfp && pfp.src) || photos.length > 0;
}
function processQuotes() {
if (originalMoved) return;
const timeline = document.querySelector('div[aria-label="Timeline: Search timeline"]');
if (!timeline) return;
const articles = Array.from(timeline.querySelectorAll('article[data-testid="tweet"]'));
if (articles.length < 2) return;
console.log(`[Quotes Cleaner Debug] Found ${articles.length} articles`);
const firstArticle = articles[0];
let innerBlock = firstArticle.querySelector('div.r-adacv.r-1udh08x.r-1kqtdi0.r-1867qdf') ||
firstArticle.querySelector('div[role="link"]');
if (innerBlock) {
const hasImages = logImages(innerBlock);
if (hasImages) {
const linkEl = innerBlock.querySelector('a[href*="/status/"]');
const originalUrl = linkEl
? 'https://x.com' + linkEl.getAttribute('href').split('?')[0]
: '#';
const clonedInner = innerBlock.cloneNode(true);
const wrapper = document.createElement('div');
wrapper.dataset.testid = 'cellInnerDiv';
const a = document.createElement('a');
a.href = originalUrl;
a.style.cssText = 'text-decoration:none;color:inherit;display:block;';
a.appendChild(clonedInner);
wrapper.appendChild(a);
timeline.prepend(wrapper);
// Remove inner block from original first post
innerBlock.remove();
// Remove hanging "Quote" label safely after move
setTimeout(() => {
const hanging = firstArticle.querySelector('div.r-9aw3ui.r-1s2bzr4');
if (hanging) hanging.remove();
}, 500);
originalMoved = true;
console.log('[Quotes Cleaner] ✅ SUCCESS: Inner quoted content moved to top - images preserved');
} else {
console.log('[Quotes Cleaner Debug] Inner block found but images not ready yet - waiting...');
}
}
}
// Keep polling until success
setInterval(processQuotes, 100);
// Clean other posts
function cleanOthers() {
const articles = document.querySelectorAll('article[data-testid="tweet"]');
let cleaned = 0;
articles.forEach((article, i) => {
if (i === 0) return;
const blocks = article.querySelectorAll('div.r-adacv, div[role="link"], div.r-1s2bzr4, div.r-9aw3ui');
blocks.forEach(block => {
if (block.textContent && block.textContent.length > 20) {
block.remove();
cleaned++;
}
});
});
if (cleaned > 0) console.log(`[Quotes Cleaner] Cleaned ${cleaned} inner blocks`);
}
setInterval(cleanOthers, 250);
// Back button
window.addEventListener('popstate', () => {
originalMoved = false;
});
console.log('v5.6 ready');
})();