// ==UserScript== // @name X/Twitter - xGrid Media Timeline // @namespace https://github.com/BD9Max/userscripts // @version 2.5.1.2 // @description Profile media timeline grid filters: Display images, videos or both. Grid size (1-5) changer. Media deduplication - removes duplicate media from grid. Blocks GIFs. Uncrops thumbnails. Control Panel for settings/save. // @icon https://raw.githubusercontent.com/BD9Max/userscripts/refs/heads/main/media/icons/X_Twitter_xGrid_Media_Timeline_Icon_64.png // @author krbd9max // @include https://x.com/* // @include https://twitter.com/* // @grant GM_addStyle // @grant GM_xmlhttpRequest // @connect raw.githubusercontent.com // @license MIT // ==/UserScript== (function() { 'use strict'; // Configuration const HAMMING_THRESHOLD = 3; // Bit difference tolerance for image similarities // --- BK-TREE DATA STRUCTURE FOR FAST HAMMING DISTANCE LOOKUPS --- class BKNode { constructor(hash, cellId) { this.hash = hash; this.cellIds = [cellId]; this.children = {}; // distance -> BKNode } } class BKTree { constructor() { this.root = null; } static hammingDistance(h1, h2) { let xor = h1 ^ h2; let count = 0; while (xor > 0n) { if (xor & 1n) count++; xor >>= 1n; } return count; } add(hash, cellId) { if (!this.root) { this.root = new BKNode(hash, cellId); return null; } let curr = this.root; while (true) { const dist = BKTree.hammingDistance(curr.hash, hash); if (dist === 0) { curr.cellIds.push(cellId); return curr.cellIds[0]; } if (curr.children[dist]) { curr = curr.children[dist]; } else { curr.children[dist] = new BKNode(hash, cellId); return null; } } } search(hash, maxDist, node = this.root, results = []) { if (!node) return results; const dist = BKTree.hammingDistance(node.hash, hash); if (dist <= maxDist) results.push({ node, dist }); const minDist = dist - maxDist; const highDist = dist + maxDist; for (let d in node.children) { const numericD = parseInt(d, 10); if (numericD >= minDist && numericD <= highDist) { this.search(hash, maxDist, node.children[numericD], results); } } return results; } } // --- DHASH ALGORITHM --- function computeDHash(imgSrc) { return new Promise((resolve) => { const img = new Image(); img.crossOrigin = 'anonymous'; img.src = imgSrc; img.onload = () => { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = 9; canvas.height = 8; ctx.drawImage(img, 0, 0, 9, 8); let imgData; try { imgData = ctx.getImageData(0, 0, 9, 8).data; } catch (e) { resolve(null); return; } const gray = new Uint8Array(72); for (let i = 0; i < 72; i++) { gray[i] = 0.299 * imgData[i * 4] + 0.587 * imgData[i * 4 + 1] + 0.114 * imgData[i * 4 + 2]; } let hash = 0n; let bitPosition = 0n; for (let y = 0; y < 8; y++) { for (let x = 0; x < 8; x++) { if (gray[y * 9 + x] > gray[y * 9 + (x + 1)]) hash |= (1n << bitPosition); bitPosition++; } } resolve(hash); }; img.onerror = () => resolve(null); }); } // --- TRACKING GLOBALS --- const imageTree = new BKTree(); const cellRegistry = new Map(); let deduplicatorMode = "SCAN"; const knownDuplicates = new Set(); const knownUniques = new Set(); // --- ROUTE & CONTEXT CHECKER WITH STATE RETENTION --- function updatePageContext() { const isMediaTab = /\/[^/]+\/media\/?$/.test(window.location.pathname); if (isMediaTab) { document.body.setAttribute('data-xgrid-active', 'true'); } else { if (!document.querySelector('[role="dialog"]') && !document.querySelector('[data-testid="layerViews"]')) { document.body.removeAttribute('data-xgrid-active'); document.body.removeAttribute('filter-by'); } } } // --- GLOBAL PERSISTENCE STORAGE CONTROLLER ENGINE --- function saveAllSettings() { if (document.body.getAttribute('data-save-settings') === "ON") { const filterBy = document.body.getAttribute('filter-by'); if (filterBy) localStorage.setItem('xgrid-filter-by', filterBy); else localStorage.removeItem('xgrid-filter-by'); localStorage.setItem('xgrid-column-count', document.body.getAttribute('column-count') || "3"); localStorage.setItem('xgrid-dedup-mode', document.body.getAttribute('dedup-mode') || "SCAN"); localStorage.setItem('xgrid-crop-mode', document.body.getAttribute('data-crop-mode') || "ON"); } else { localStorage.removeItem('xgrid-filter-by'); localStorage.removeItem('xgrid-column-count'); localStorage.removeItem('xgrid-dedup-mode'); localStorage.removeItem('xgrid-crop-mode'); } } function loadAllSettings() { const globalSaveState = localStorage.getItem('xgrid-save-settings') || "OFF"; document.body.setAttribute('data-save-settings', globalSaveState); if (globalSaveState === "ON") { const filterBy = localStorage.getItem('xgrid-filter-by'); if (filterBy) document.body.setAttribute('filter-by', filterBy); else document.body.removeAttribute('filter-by'); const cols = localStorage.getItem('xgrid-column-count'); if (cols) document.body.setAttribute('column-count', cols); const dedup = localStorage.getItem('xgrid-dedup-mode'); if (dedup) { document.body.setAttribute('dedup-mode', dedup); deduplicatorMode = dedup; } const crop = localStorage.getItem('xgrid-crop-mode'); if (crop) document.body.setAttribute('data-crop-mode', crop); } } // Control panel UI / logo let cachedToggleIconDataUri = null; function loadToggleIcon(imgEl) { if (!imgEl) return; if (cachedToggleIconDataUri) { imgEl.src = cachedToggleIconDataUri; return; } if (typeof GM_xmlhttpRequest === 'undefined') return; GM_xmlhttpRequest({ method: 'GET', url: 'https://raw.githubusercontent.com/BD9Max/userscripts/refs/heads/main/media/icons/X_Twitter_xGrid_Media_Timeline_Icon_64.png', responseType: 'arraybuffer', onload: (response) => { const bytes = new Uint8Array(response.response); let binary = ''; for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]); cachedToggleIconDataUri = 'data:image/png;base64,' + btoa(binary); imgEl.src = cachedToggleIconDataUri; } }); } function createFilterButtons() { if (!document.body.hasAttribute('data-xgrid-active')) { const existing = document.getElementById("media-filter-controls"); if (existing) existing.style.display = "none"; return; } if (document.getElementById("media-filter-controls")) { document.getElementById("media-filter-controls").style.display = "flex"; return; } // --- Main Floating Container Panel const xgridCP = document.createElement("div"); xgridCP.id = "media-filter-controls"; xgridCP.style.cssText = ` display: flex; position: fixed; top: 130px; right: 40px; z-index: 9999; border-radius: 15px; padding: 8px; gap: 10px; flex-direction: column; align-items: stretch; background: rgba(0, 0, 0, 0.85); border: 1px solid #333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; backdrop-filter: blur(30px); width: 140px; transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), right 0.3s cubic-bezier(0.4, 0, 0.2, 1); `; // --- Minimize / Expand Toggle Button const toggleBtn = document.createElement("button"); toggleBtn.id = "media-filter-toggle-btn"; toggleBtn.innerHTML = ` `; loadToggleIcon(toggleBtn.querySelector("#media-filter-toggle-icon")); toggleBtn.title = "xGrid Media Timeline"; toggleBtn.style.cssText = ` position: absolute; left: -32px; top: 10px; width: 34px; height: 42px; border-radius: 6px 0 0 6px; border: 1px solid #333; border-right: none; background: rgba(0, 0, 0, 0.85); color: #e7e9ea; cursor: pointer; display: flex; align-items: center; justify-content: center; backdrop-filter: blur(30px); transition: transform 0.3s ease, color 0.15s ease, background-color 0.15s ease; padding: 0; box-shadow: -2px 0 8px rgba(0,0,0,0.3); `; // --- Vertical Label const closedTextLabel = document.createElement("div"); closedTextLabel.id = "media-filter-closed-label"; closedTextLabel.textContent = "xGrid Media Timeline"; closedTextLabel.style.cssText = ` position: absolute; left: -26px; top: 45px; color: #71767b; background: rgba(0, 0, 0, 0.85); font-size: 9px; font-weight: 800; letter-spacing: 1px; writing-mode: vertical-rl; text-orientation: mixed; transform: rotate(180deg); pointer-events: none; opacity: 0; transition: opacity 0.2s ease, color 0.15s ease; `; xgridCP.appendChild(closedTextLabel); // Toggle button click logic with persistence let isOpen = localStorage.getItem('xgrid-cp-open') !== 'false'; // Initial state logic if (isOpen) { xgridCP.style.right = "40px"; toggleBtn.style.transform = "rotate(0deg)"; closedTextLabel.style.opacity = "0"; } else { xgridCP.style.right = "-153px"; toggleBtn.style.transform = "rotate(180deg)"; closedTextLabel.style.opacity = "1"; } toggleBtn.onclick = (e) => { e.stopPropagation(); isOpen = !isOpen; localStorage.setItem('xgrid-cp-open', isOpen); if (isOpen) { xgridCP.style.right = "40px"; toggleBtn.style.transform = "rotate(0deg)"; closedTextLabel.style.opacity = "0"; } else { xgridCP.style.right = "-153px"; toggleBtn.style.transform = "rotate(180deg)"; closedTextLabel.style.opacity = "1"; } }; xgridCP.appendChild(toggleBtn); // --- Filter Section Header const titleFilters = document.createElement("div"); titleFilters.textContent = "xGrid MEDIA FILTERS:"; titleFilters.style.cssText = `color: #e7e9ea; font-size: 12px; font-weight: 750; letter-spacing: 0.5px; padding: 0 2px;`; xgridCP.appendChild(titleFilters); // Images / Videos / All filter buttons const filterGroup = document.createElement("div"); filterGroup.style.cssText = `display: flex; flex-direction: row; gap: 4px; width: 100%; justify-content: space-between;`; const allBtn = document.createElement("button"); allBtn.id = "all"; allBtn.textContent = "OFF"; allBtn.onclick = () => { document.body.removeAttribute("filter-by"); saveAllSettings(); }; const imagesBtn = document.createElement("button"); imagesBtn.id = "images"; imagesBtn.textContent = "IMG"; imagesBtn.onclick = () => { document.body.setAttribute("filter-by", "images"); saveAllSettings(); }; const videosBtn = document.createElement("button"); videosBtn.id = "videos"; videosBtn.textContent = "VID"; videosBtn.onclick = () => { document.body.setAttribute("filter-by", "videos"); saveAllSettings(); }; filterGroup.appendChild(allBtn); filterGroup.appendChild(imagesBtn); filterGroup.appendChild(videosBtn); xgridCP.appendChild(filterGroup); // --- Grid Scaler Header const titleColumns = document.createElement("div"); titleColumns.textContent = "GRID SCALE:"; titleColumns.style.cssText = `color: #e7e9ea; font-size: 11px; font-weight: 700; letter-spacing: 0.5px; padding: 0 2px; margin-top: 6px;`; xgridCP.appendChild(titleColumns); // Grid width adjustments const scalerGroup = document.createElement("div"); scalerGroup.style.cssText = `display: flex; gap: 4px; align-items: center; justify-content: space-between; background: #000; border: 1px solid #3d4144; border-radius: 20px; padding: 2px 4px;`; if (!document.body.hasAttribute("column-count")) { document.body.setAttribute("column-count", "3"); } const valueDisplay = document.createElement("span"); valueDisplay.style.cssText = `background-color: rgb(26, 77, 216); color: #fff; font-size: 11px; font-weight: bold; width: 22px; height: 22px; border-radius: 50%; display: flex; align-items: center; justify-content: center; text-align: center;`; valueDisplay.textContent = document.body.getAttribute("column-count"); const updateCols = (delta) => { let current = parseInt(document.body.getAttribute("column-count") || "3", 10); let next = current + delta; if (next >= 1 && next <= 5) { document.body.setAttribute("column-count", next.toString()); valueDisplay.textContent = next.toString(); saveAllSettings(); } }; const minusBtn = document.createElement("button"); minusBtn.textContent = "−"; minusBtn.style.cssText = `background: none; border: none; color: #71767b; cursor: pointer; font-size: 14px; font-weight: bold; width: 24px; height: 24px; padding: 0; transition: color 0.1s ease;`; minusBtn.onclick = () => updateCols(-1); minusBtn.onmouseover = () => minusBtn.style.color = "rgb(26, 77, 216)"; minusBtn.onmouseout = () => minusBtn.style.color = "#71767b"; const plusBtn = document.createElement("button"); plusBtn.textContent = "+"; plusBtn.style.cssText = `background: none; border: none; color: #71767b; cursor: pointer; font-size: 14px; font-weight: bold; width: 24px; height: 24px; padding: 0; transition: color 0.1s ease;`; plusBtn.onclick = () => updateCols(1); plusBtn.onmouseover = () => plusBtn.style.color = "rgb(26, 77, 216)"; plusBtn.onmouseout = () => plusBtn.style.color = "#71767b"; scalerGroup.appendChild(minusBtn); scalerGroup.appendChild(valueDisplay); scalerGroup.appendChild(plusBtn); xgridCP.appendChild(scalerGroup); // DEDUPLICATOR ENGINE CONTROLS const titleDedup = document.createElement("div"); titleDedup.textContent = "DEDUPLICATE:"; titleDedup.style.cssText = `color: #e7e9ea; font-size: 11px; font-weight: 700; letter-spacing: 0.5px; padding: 0 2px; margin-top: 6px;`; xgridCP.appendChild(titleDedup); const dedupRow = document.createElement("div"); dedupRow.style.cssText = `display: flex; gap: 4px; align-items: center; width: 100%; justify-content: space-between;`; const dedupGroup = document.createElement("div"); dedupGroup.style.cssText = `display: flex; gap: 2px; background: #000; border: 1px solid #3d4144; border-radius: 20px; padding: 2px; flex: 1; justify-content: space-between;`; if (!document.body.hasAttribute("dedup-mode")) document.body.setAttribute("dedup-mode", "SCAN"); ["OFF", "SCAN", "ON"].forEach(mode => { const btn = document.createElement("button"); btn.className = "dedup-btn"; btn.textContent = mode; btn.style.cssText = ` background: none; border: none; color: ${deduplicatorMode === mode ? '#fff' : '#71767b'}; cursor: pointer; font-size: 9px; font-weight: bold; padding: 4px 4px; border-radius: 12px; transition: all 0.1s ease; flex: 1; text-align: center; background-color: ${deduplicatorMode === mode ? 'rgb(26, 77, 216)' : 'transparent'}; `; btn.onclick = () => { deduplicatorMode = mode; document.body.setAttribute("dedup-mode", mode); dedupGroup.querySelectorAll('.dedup-btn').forEach(b => { b.style.color = '#71767b'; b.style.backgroundColor = 'transparent'; }); btn.style.color = '#fff'; btn.style.backgroundColor = 'rgb(26, 77, 216)'; saveAllSettings(); }; dedupGroup.appendChild(btn); }); const dedupIndicator = document.createElement("span"); dedupIndicator.id = "xgrid-dedup-indicator"; dedupIndicator.textContent = "D"; dedupIndicator.style.cssText = ` font-size: 8px; font-weight: 700; color: #71767b; background: transparent; border: 1px solid #3d4144; border-radius: 50%; width: 14px; height: 14px; display: flex; align-items: center; justify-content: center; text-align: center; transition: all 0.2s ease; box-sizing: border-box; margin-right: 2px; `; dedupRow.appendChild(dedupGroup); dedupRow.appendChild(dedupIndicator); xgridCP.appendChild(dedupRow); // --- CROP THUMBS CONTROLS const titleCrop = document.createElement("div"); titleCrop.textContent = "THUMBNAIL CROP:"; titleCrop.style.cssText = `color: #e7e9ea; font-size: 11px; font-weight: 700; letter-spacing: 0.5px; padding: 0 2px; margin-top: 6px;`; xgridCP.appendChild(titleCrop); const cropGroup = document.createElement("div"); cropGroup.style.cssText = `display: flex; gap: 2px; background: #000; border: 1px solid #3d4144; border-radius: 20px; padding: 2px; justify-content: space-between;`; let cropMode = document.body.getAttribute("data-crop-mode") || "ON"; ["ON", "OFF"].forEach(mode => { const btn = document.createElement("button"); btn.className = "crop-btn"; btn.textContent = mode; btn.style.cssText = ` background: none; border: none; color: ${cropMode === mode ? '#fff' : '#71767b'}; cursor: pointer; font-size: 9px; font-weight: bold; padding: 4px 6px; border-radius: 12px; transition: all 0.1s ease; flex: 1; background-color: ${cropMode === mode ? 'rgb(26, 77, 216)' : 'transparent'}; `; btn.onclick = () => { cropMode = mode; document.body.setAttribute("data-crop-mode", mode); cropGroup.querySelectorAll('.crop-btn').forEach(b => { b.style.color = '#71767b'; b.style.backgroundColor = 'transparent'; }); btn.style.color = '#fff'; btn.style.backgroundColor = 'rgb(26, 77, 216)'; saveAllSettings(); }; cropGroup.appendChild(btn); }); xgridCP.appendChild(cropGroup); // --- GIF Block Status Text Indicator const titleGifs = document.createElement("div"); titleGifs.innerHTML = 'GIF BLOCK: ACTIVE'; titleGifs.style.cssText = `color: #e7e9ea; font-size: 10px; padding: 0 2px; margin-top: 4px;`; xgridCP.appendChild(titleGifs); // --- GLOBAL SAVE CONFIGURATION SECTION const titleSave = document.createElement("div"); titleSave.textContent = "SAVE SETTINGS:"; titleSave.style.cssText = `color: #e7e9ea; font-size: 11px; font-weight: 700; letter-spacing: 0.5px; padding: 0 2px; margin-top: 6px;`; xgridCP.appendChild(titleSave); const saveGroup = document.createElement("div"); saveGroup.style.cssText = `display: flex; gap: 2px; background: #000; border: 1px solid #3d4144; border-radius: 20px; padding: 2px; justify-content: space-between;`; let currentSaveMode = document.body.getAttribute('data-save-settings') || "OFF"; ["ON", "OFF"].forEach(mode => { const btn = document.createElement("button"); btn.className = "save-settings-btn"; btn.textContent = mode; const activeColor = mode === "ON" ? "#198532" : "rgb(26, 77, 216)"; btn.style.cssText = ` background: none; border: none; color: ${currentSaveMode === mode ? '#fff' : '#71767b'}; cursor: pointer; font-size: 9px; font-weight: bold; padding: 4px 6px; border-radius: 12px; transition: all 0.1s ease; flex: 1; background-color: ${currentSaveMode === mode ? activeColor : 'transparent'}; `; btn.onclick = () => { document.body.setAttribute("data-save-settings", mode); localStorage.setItem('xgrid-save-settings', mode); saveGroup.querySelectorAll('.save-settings-btn').forEach(b => { b.style.color = '#71767b'; b.style.backgroundColor = 'transparent'; }); btn.style.color = '#fff'; btn.style.backgroundColor = activeColor; saveAllSettings(); }; saveGroup.appendChild(btn); }); xgridCP.appendChild(saveGroup); document.body.appendChild(xgridCP); } GM_addStyle(` /* Uniform Filter Tab Formatting rules */ #media-filter-controls #all, #media-filter-controls #images, #media-filter-controls #videos { flex: 1 !important; padding: 6px 2px !important; border: 1px solid #3d4144 !important; border-radius: 20px !important; background: #000000 !important; color: #71767b !important; cursor: pointer !important; font-size: 10px !important; font-weight: 600 !important; transition: all 0.15s ease !important; text-align: center !important; box-sizing: border-box !important; } #media-filter-controls #all:hover, #media-filter-controls #images:hover, #media-filter-controls #videos:hover { background: #15181c !important; } /* Highlight Rules for Selected Active State */ body:not([filter-by]) #media-filter-controls #all, [filter-by="images"] #media-filter-controls #images, [filter-by="videos"] #media-filter-controls #videos { background: rgb(26, 77, 216) !important; scale: 1.02 !important; color: white !important; border-color: transparent !important; } #media-filter-toggle-btn:hover { background: #111 !important; } #media-filter-controls:hover #media-filter-closed-label { color: #e7e9ea !important; } /* Control Panel Tab Visibility Logic Rules */ #media-filter-controls { display: none !important; } body:has([data-testid="primaryColumn"] [role="navigation"] [href$="/media"][aria-selected="true"]):not(:has([role="dialog"]), :has([data-testid="layerViews"])) #media-filter-controls { display: flex !important; } /* Rebuild grid after filtering */ [filter-by="videos"] [data-testid="primaryColumn"]:has([role="navigation"] [href$="/media"][aria-selected="true"]) [role="region"] [role="listitem"]:has(a[href*="/photo/"]) { display: none !important; } [filter-by="images"] [data-testid="primaryColumn"]:has([role="navigation"] [href$="/media"][aria-selected="true"]) [role="region"] [role="listitem"]:has(a[href*="/video/"]) { display: none !important; } /* Dynamic GIF Blocker Rule */ [data-testid="primaryColumn"]:has([role="navigation"] [href$="/media"][aria-selected="true"]) [role="listitem"]:has(div[style*="tweet_video_thumb"]), [data-testid="primaryColumn"]:has([role="navigation"] [href$="/media"][aria-selected="true"]) [role="listitem"]:has(img[src*="tweet_video_thumb"]) { display: none !important; } /* Grid Framework Rebuild Engine */ [data-testid="primaryColumn"]:has([role="navigation"] [href$="/media"][aria-selected="true"]) section[role="region"]:has(> h1[role="heading"] + div[aria-label^="Timeline:"] > div > div[data-testid="cellInnerDiv"]) > div { display: flex !important; flex-direction: row !important; flex-wrap: wrap !important; gap: 4px !important; margin: 4px !important; } /* Virtualization Row Eraser */ [data-testid="primaryColumn"]:has([role="navigation"] [href$="/media"][aria-selected="true"]) section[role="region"]:has(> h1[role="heading"] + div[aria-label^="Timeline:"] > div > div[data-testid="cellInnerDiv"]) > div *:not([role="listitem"], [role="listitem"] *) { display: contents !important; } /* Item Core Formatting rules */ [data-testid="primaryColumn"]:has([role="navigation"] [href$="/media"][aria-selected="true"]) [role="listitem"] { box-sizing: border-box !important; will-change: width !important; position: relative !important; } /* Secure Visual Image Setup Engine */ [data-testid="primaryColumn"]:has([role="navigation"] [href$="/media"][aria-selected="true"]) [role="listitem"] a { display: block !important; width: 100% !important; aspect-ratio: 1 / 1 !important; } [data-testid="primaryColumn"]:has([role="navigation"] [href$="/media"][aria-selected="true"]) [role="listitem"] img { aspect-ratio: 1 / 1 !important; object-fit: cover !important; width: 100% !important; height: 100% !important; opacity: 1 !important; visibility: visible !important; } /* Grid Column Assignment Width Rules */ body:not([column-count]) [data-testid="primaryColumn"] [role="listitem"] { width: calc(33.333% - 2.66px) !important; } body[column-count="1"] [data-testid="primaryColumn"] [role="listitem"] { width: 100% !important; } body[column-count="2"] [data-testid="primaryColumn"] [role="listitem"] { width: calc(50% - 2px) !important; } body[column-count="3"] [data-testid="primaryColumn"] [role="listitem"] { width: calc(33.333% - 2.66px) !important; } body[column-count="4"] [data-testid="primaryColumn"] [role="listitem"] { width: calc(25% - 3px) !important; } body[column-count="5"] [data-testid="primaryColumn"] [role="listitem"] { width: calc(20% - 3.2px) !important; } /* --- UNCROP THUMBS CSS --- */ body[data-crop-mode="OFF"] [data-testid="primaryColumn"]:has([role="navigation"] [href$="/media"][aria-selected="true"]) a[href*="/photo/"] *, body[data-crop-mode="OFF"] [data-testid="primaryColumn"]:has([role="navigation"] [href$="/media"][aria-selected="true"]) a[href*="/video/"] * { object-fit: contain !important; background-size: contain !important; background-repeat: no-repeat !important; background-position: center !important; } body[data-crop-mode="OFF"] [data-testid="primaryColumn"]:has([role="navigation"] [href$="/media"][aria-selected="true"]) a[href*="/photo/"] img, body[data-crop-mode="OFF"] [data-testid="primaryColumn"]:has([role="navigation"] [href$="/media"][aria-selected="true"]) a[href*="/video/"] img { width: 100% !important; height: 100% !important; max-width: 100% !important; max-height: 100% !important; } body[data-crop-mode="OFF"] [data-testid="primaryColumn"]:has([role="navigation"] [href$="/media"][aria-selected="true"]) a[href*="/photo/"], body[data-crop-mode="OFF"] [data-testid="primaryColumn"]:has([role="navigation"] [href$="/media"][aria-selected="true"]) a[href*="/video/"] { background-color: rgba(0, 0, 0, 0) !important; } /* DEDUPLICATOR VISUAL OVERLAY MARKER */ body[dedup-mode="SCAN"] [data-is-duplicate="true"]::after { content: "D" !important; position: absolute !important; bottom: 6px !important; left: 55% !important; transform: translateX(-50%) !important; background: rgba(0, 0, 0, 0.75) !important; color: #ff3b30 !important; font-size: 10px !important; font-weight: 700 !important; padding: 1px 4px !important; border-radius: 10px !important; z-index: 10 !important; pointer-events: none !important; font-family: sans-serif !important; } body[dedup-mode="ON"] [data-is-duplicate="true"] { display: none !important; } /* VIDEO DURATION AND MULTI-MEDIA ICON COLOR OVERRIDES */ [data-testid="primaryColumn"]:has([role="navigation"] [href$="/media"][aria-selected="true"]) a[href*="/video/"] span { color: rgb(60, 162, 240) !important; } [data-testid="primaryColumn"]:has([role="navigation"] [href$="/media"][aria-selected="true"]) [role="listitem"] svg:has(path[d^="M2 8.5"]) { fill: rgb(60, 162, 240) !important; color: rgb(60, 162, 240) !important; } [data-testid="primaryColumn"]:has([role="navigation"] [href$="/media"][aria-selected="true"]) [role="listitem"] svg:has(path[d^="M2 8.5"]) { fill: rgb(60, 162, 240) !important; color: rgb(60, 162, 240) !important; background-color: rgba(15, 15, 20, 0.75) !important; padding: 1px !important; border-radius: 35% !important; display: inline-block !important; } `); // --- DEDUPLICATION LOGIC (DHASH/BK-TREE) --- function processDeduplication() { if (deduplicatorMode === "OFF") { const indicatorEl = document.getElementById('xgrid-dedup-indicator'); if (indicatorEl) { indicatorEl.style.color = "#71767b"; indicatorEl.style.borderColor = "#3d4144"; } return; } const cells = document.querySelectorAll('[data-testid="primaryColumn"]:has([role="navigation"] [href$="/media"][aria-selected="true"]) [role="listitem"]'); if (!cells || cells.length === 0) return; cells.forEach(cell => { if (!cell.hasAttribute('data-media-type')) { const anchor = cell.querySelector('a[href*="/status/"]'); if (anchor) { const match = anchor.getAttribute('href').match(/\/(photo|video)\//); if (match) cell.setAttribute('data-media-type', match[1]); } } const imgEl = cell.querySelector('img'); if (!imgEl || !imgEl.src) return; const imgSrc = imgEl.src; if (knownDuplicates.has(imgSrc)) { if (!cell.hasAttribute('data-dedup-id')) cell.setAttribute('data-dedup-id', 'cached_' + Math.random().toString(36).substr(2, 9)); cell.setAttribute('data-is-duplicate', 'true'); return; } if (knownUniques.has(imgSrc)) { if (!cell.hasAttribute('data-dedup-id')) cell.setAttribute('data-dedup-id', 'cached_' + Math.random().toString(36).substr(2, 9)); return; } if (cell.hasAttribute('data-dedup-id')) return; const cellId = 'cell_' + Math.random().toString(36).substr(2, 9); cell.setAttribute('data-dedup-id', cellId); cellRegistry.set(cellId, cell); computeDHash(imgSrc).then(hash => { if (hash === null) return; const matches = imageTree.search(hash, HAMMING_THRESHOLD); if (matches.length > 0) { let dupCellId = cellId; let keepCellId = matches[0].node.cellIds[0]; const keepCell = cellRegistry.get(keepCellId); const currCell = cell; if (keepCell && currCell) { const keepHasMulti = keepCell.querySelector('svg:has(path[d^=\"M2 8.5\"])') !== null; const currHasMulti = currCell.querySelector('svg:has(path[d^=\"M2 8.5\"])') !== null; // Prefer to keep the post with multiple media (deduplicate/remove the one without) if (currHasMulti && !keepHasMulti) { dupCellId = keepCellId; keepCellId = cellId; matches[0].node.cellIds[0] = cellId; currCell.removeAttribute('data-is-duplicate'); } } const dupNode = cellRegistry.get(dupCellId); if (dupNode) { dupNode.setAttribute('data-is-duplicate', 'true'); const dupImg = dupNode.querySelector('img'); if (dupImg && dupImg.src) knownDuplicates.add(dupImg.src); } } else { imageTree.add(hash, cellId); knownUniques.add(imgSrc); } }); }); // Update real-time UI duplicates panel indicator badge setTimeout(() => { const hasDuplicates = document.querySelector('[data-is-duplicate="true"]') !== null; const indicatorEl = document.getElementById('xgrid-dedup-indicator'); if (indicatorEl) { if (hasDuplicates) { indicatorEl.style.color = "#ff6b6b"; indicatorEl.style.borderColor = "#ff6b6b"; } else { indicatorEl.style.color = "#71767b"; indicatorEl.style.borderColor = "#3d4144"; } } }, 250); } // --- INITIALIZATION CORE --- loadAllSettings(); updatePageContext(); createFilterButtons(); const spaObserver = new MutationObserver(() => { updatePageContext(); createFilterButtons(); if (document.body.hasAttribute('data-xgrid-active')) { processDeduplication(); } }); spaObserver.observe(document.documentElement, { childList: true, subtree: true }); // Saves & restore scroll position let savedGridScrollY = null; document.addEventListener('click', (e) => { const link = e.target.closest('[role="listitem"] a[href*="/status/"]'); if (link && document.body.hasAttribute('data-xgrid-active')) { savedGridScrollY = window.scrollY; } }, true); function restoreGridScroll(targetY, durationMs = 2000) { const start = performance.now(); const reassert = () => { if (Math.abs(window.scrollY - targetY) > 1) { window.scrollTo(0, targetY); } if (performance.now() - start < durationMs) { setTimeout(reassert, 40); } }; reassert(); } let dialogWasOpen = false; const scrollRestoreObserver = new MutationObserver(() => { const dialogOpen = !!(document.querySelector('[role="dialog"]') || document.querySelector('[data-testid="layerViews"]')); if (dialogWasOpen && !dialogOpen && savedGridScrollY !== null) { restoreGridScroll(savedGridScrollY); savedGridScrollY = null; } dialogWasOpen = dialogOpen; }); scrollRestoreObserver.observe(document.documentElement, { childList: true, subtree: true }); window.addEventListener('popstate', () => { const isMediaTab = /\/[^/]+\/media\/?$/.test(window.location.pathname); if (isMediaTab && savedGridScrollY !== null) { restoreGridScroll(savedGridScrollY); savedGridScrollY = null; } }); })();