// ==UserScript==
// @name X/Twitter - xTweaks UI Enhance
// @namespace https://github.com/BD9Max/userscripts
// @version 1.1.1
// @description UI enhance tweaks for X / Twitter. Wider central feed area. Sticky (stays on top) profile navigation while scrolling down. Truncate posts texts to 1-line - expand for full text portion. Smaller posts font size. Hide right sidebar. Collapsible control panel. More functionality added later.
// @icon https://raw.githubusercontent.com/BD9Max/userscripts/refs/heads/main/media/icons/xtweaks_icon_64.png
// @author krbd9max
// @license MIT
// @match https://*.x.com/*
// @match https://*.twitter.com/*
// @grant none
// @run-at document-idle
// ==/UserScript==
(function() {
'use strict';
// --- 1. Configuration State (Saves to Browser Storage) ---
const config = {
truncate: localStorage.getItem('x-mod-truncate') !== 'false',
stickyNav: localStorage.getItem('x-mod-sticky') !== 'false',
widerFeed: localStorage.getItem('x-mod-wider') !== 'false',
smallText: localStorage.getItem('x-mod-smalltext') !== 'false',
focusMode: localStorage.getItem('x-mod-focus') === 'true'
};
function updateConfig(key, value, styleNode) {
localStorage.setItem(key, value);
if (value) {
document.head.appendChild(styleNode);
} else {
if (styleNode && styleNode.parentNode) {
styleNode.parentNode.removeChild(styleNode);
}
}
}
// --- 2. Feature: 12px Global Tweet Text ---
const smallTextStyle = document.createElement('style');
smallTextStyle.textContent = `
[data-testid="tweetText"] {
font-size: 12px !important;
line-height: 18px !important;
}
`;
if (config.smallText) {
document.head.appendChild(smallTextStyle);
}
// --- 3. Feature: 800px Wide Central Feed (default 600px) ---
const widerStyle = document.createElement('style');
widerStyle.textContent = `
[data-testid="primaryColumn"] {
max-width: 800px !important;
width: 800px !important;
}
[data-testid="primaryColumn"] > div > div > div,
[data-testid="cellInnerDiv"] {
max-width: 800px !important;
width: 100% !important;
}
[data-testid="primaryColumn"] .r-1ye8kvj {
max-width: 800px !important;
}
`;
if (config.widerFeed) {
document.head.appendChild(widerStyle);
}
// --- 4. Feature: Sticky Profile Navigation ---
const stickyStyle = document.createElement('style');
stickyStyle.textContent = `
[data-testid="primaryColumn"] > div > div > div > div > div:has(> nav[role="navigation"]) {
position: sticky !important;
top: 36px !important;
z-index: 10 !important;
background-color: rgba(0, 0, 0, 0.7) !important;
backdrop-filter: blur(12px) !important;
border-bottom: 1px solid rgb(47, 51, 54) !important;
}
`;
if (config.stickyNav) {
document.head.appendChild(stickyStyle);
}
// --- 5. Feature: Right Sidebar Focus Mode (Hide Widgets & Narrow Search) ---
const focusStyle = document.createElement('style');
focusStyle.textContent = `
[data-testid="sidebarColumn"] {
width: 260px !important;
}
[data-testid="sidebarColumn"] section,
[data-testid="sidebarColumn"] aside,
[data-testid="sidebarColumn"] [aria-label="Timeline: Trending now"],
[data-testid="sidebarColumn"] [aria-label="Timeline: Explore"] {
display: none !important;
}
[data-testid="sidebarColumn"] form[role="search"] {
max-width: 220px !important;
margin: 0 auto !important;
}
`;
if (config.focusMode) {
document.head.appendChild(focusStyle);
}
// --- 6. Feature: 1-Line Truncation ---
const truncateStyle = document.createElement('style');
truncateStyle.textContent = `
/* Container setup */
[data-testid="tweetText"].is-truncated:not(.expanded-tweet) {
display: block !important;
white-space: nowrap !important;
overflow: hidden !important;
position: relative !important;
cursor: pointer !important;
padding-right: 60px !important;
box-sizing: border-box !important;
}
/* The bold "...more" text with an opaque background to block overlap */
[data-testid="tweetText"].is-truncated:not(.expanded-tweet)::after {
content: "...more";
position: absolute !important;
right: 0 !important;
top: 0 !important;
bottom: 0 !important;
display: flex !important;
align-items: center !important;
color: #00ba7c !important; /* X Retweet Green */
font-weight: 700 !important;
font-size: 12px !important;
/* This blocks the text underneath */
background-color: black !important;
padding-left: 8px !important;
padding-right: 4px !important;
/* Creates a slight fade effect on the left edge */
box-shadow: -10px 0 10px black !important;
}
`;
if (config.truncate) {
document.head.appendChild(truncateStyle);
}
const evaluateTruncation = () => {
const tweets = document.querySelectorAll('[data-testid="tweetText"]:not(.truncation-checked)');
tweets.forEach(tweet => {
if (tweet.clientWidth === 0) return; // Wait until fully rendered
tweet.classList.add('truncation-checked');
const originalWhiteSpace = tweet.style.whiteSpace;
tweet.style.whiteSpace = 'nowrap';
const overflowsWidth = tweet.scrollWidth > tweet.clientWidth;
tweet.style.whiteSpace = originalWhiteSpace;
const lineHeight = parseFloat(window.getComputedStyle(tweet).lineHeight) || 24;
const overflowsHeight = tweet.offsetHeight > (lineHeight * 1.5);
if (overflowsWidth || overflowsHeight) {
tweet.classList.add('is-truncated');
}
});
};
const observer = new MutationObserver(evaluateTruncation);
observer.observe(document.body, { childList: true, subtree: true });
evaluateTruncation();
document.addEventListener('click', function(e) {
const tweetText = e.target.closest('[data-testid="tweetText"].is-truncated');
if (tweetText && !tweetText.classList.contains('expanded-tweet')) {
e.preventDefault();
e.stopPropagation();
tweetText.classList.add('expanded-tweet');
}
}, true);
// --- 7. The Control Panel ---
function createControlPanel() {
if (document.getElementById('customizer-panel')) return;
const switchStyle = document.createElement('style');
switchStyle.textContent = `
.xt-switch { position: relative; display: inline-block; width: 34px; height: 20px; flex-shrink: 0; }
.xt-switch input { opacity: 0; width: 0; height: 0; }
.xt-slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: rgb(83, 100, 113); transition: .2s; border-radius: 20px; }
.xt-slider:before { position: absolute; content: ""; height: 14px; width: 14px; left: 3px; bottom: 3px; background-color: white; transition: .2s; border-radius: 50%; }
.xt-switch input:checked + .xt-slider { background-color: #00ba7c; }
.xt-switch input:checked + .xt-slider:before { transform: translateX(14px); }
`;
document.head.appendChild(switchStyle);
const isMinimized = localStorage.getItem('x-mod-panel-min') === 'true';
const panel = document.createElement('div');
panel.id = 'customizer-panel';
panel.style.cssText = `
position: fixed;
bottom: 70px;
left: 15px;
background-color: black;
border: 1px solid rgb(47, 51, 54);
border-radius: 12px;
z-index: 9999;
color: #eff3f4;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
font-size: 13px;
display: flex;
flex-direction: column;
box-shadow: 0 4px 12px rgba(255,255,255,0.05);
width: 250px;
overflow: hidden;
transition: transform 0.3s ease;
transform: ${isMinimized ? 'translateX(-125px)' : 'translateX(0)'};
`;
const header = document.createElement('div');
header.style.cssText = `
display: flex;
justify-content: ${isMinimized ? 'flex-end' : 'space-between'};
align-items: center;
padding: 8px 14px;
font-weight: 650;
font-size: 13px;
cursor: pointer;
background-color: rgba(255, 255, 255, 0.05);
border-bottom: ${isMinimized ? 'none' : '1px solid rgb(47, 51, 54)'};
transition: background-color 0.2s;
`;
header.innerHTML = `+xTweaks−`;
header.addEventListener('mouseover', () => header.style.backgroundColor = 'rgba(255, 255, 255, 0.1)');
header.addEventListener('mouseout', () => header.style.backgroundColor = 'rgba(255, 255, 255, 0.05)');
const content = document.createElement('div');
content.id = 'customizer-content';
content.style.cssText = `
display: ${isMinimized ? 'none' : 'flex'};
flex-direction: column;
gap: 12px;
padding: 12px 14px;
`;
content.innerHTML = `
`;
header.addEventListener('click', () => {
const currentlyMin = content.style.display === 'none';
if (currentlyMin) {
content.style.display = 'flex';
header.style.borderBottom = '1px solid rgb(47, 51, 54)';
header.style.justifyContent = 'space-between';
document.getElementById('panel-extra-text').style.display = 'inline';
document.getElementById('panel-toggle-icon').style.display = 'block';
document.getElementById('panel-toggle-icon-plus').style.display = 'none';
panel.style.transform = 'translateX(0)';
localStorage.setItem('x-mod-panel-min', 'false');
} else {
content.style.display = 'none';
header.style.borderBottom = 'none';
header.style.justifyContent = 'flex-end';
document.getElementById('panel-extra-text').style.display = 'none';
document.getElementById('panel-toggle-icon').style.display = 'none';
document.getElementById('panel-toggle-icon-plus').style.display = 'block';
panel.style.transform = 'translateX(-125px)';
localStorage.setItem('x-mod-panel-min', 'true');
}
});
panel.appendChild(header);
panel.appendChild(content);
document.body.appendChild(panel);
document.getElementById('toggle-truncate').addEventListener('change', (e) => updateConfig('x-mod-truncate', e.target.checked, truncateStyle));
document.getElementById('toggle-sticky').addEventListener('change', (e) => updateConfig('x-mod-sticky', e.target.checked, stickyStyle));
document.getElementById('toggle-wider').addEventListener('change', (e) => updateConfig('x-mod-wider', e.target.checked, widerStyle));
document.getElementById('toggle-smalltext').addEventListener('change', (e) => updateConfig('x-mod-smalltext', e.target.checked, smallTextStyle));
document.getElementById('toggle-focus').addEventListener('change', (e) => updateConfig('x-mod-focus', e.target.checked, focusStyle));
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', createControlPanel);
} else {
createControlPanel();
}
})();