您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Customizes the Bluesky Home feed by creating a responsive three-column feed layout, aligning the navigation menus, and hiding unnecessary elements for a cleaner and more streamlined interface.
// ==UserScript== // @name Bluesky Enhanced Layout // @namespace https://gf.qytechs.cn/en/users/567951-stuart-saddler // @version 1.7 // @description Customizes the Bluesky Home feed by creating a responsive three-column feed layout, aligning the navigation menus, and hiding unnecessary elements for a cleaner and more streamlined interface. // @author Stuart Saddler // @icon https://i.ibb.co/Vv9LhQv/bluesky-logo-png-seeklogo-520643.png // @license MIT // @match https://bsky.app/* // @grant none // ==/UserScript== (function() { 'use strict'; /** * Checks whether the user is logged in by verifying the presence of the navigation bar. * This ensures that the layout modifications run only after login. */ function userIsLoggedIn() { return !!document.querySelector('nav[role="navigation"]'); } // Exit early on profile or notifications pages since this layout tweak is intended only for the main feed. if (window.location.pathname.startsWith('/profile/') || window.location.pathname.startsWith('/notifications')) { return; } /** * Injects custom CSS into the document head. * This CSS applies a multi‑column layout for the feed and uses break‑inside rules to keep posts stable, * preventing them from jumping or splitting between columns. */ function injectCSS() { const style = document.createElement('style'); style.textContent = ` /* Left Navigation Panel Styling */ nav[role="navigation"] { position: fixed !important; top: 0 !important; left: 0 !important; width: 200px !important; height: 100vh !important; background-color: rgb(22, 30, 39) !important; border-right: 1px solid rgb(46, 64, 82) !important; overflow-y: auto !important; z-index: 1000 !important; display: flex !important; flex-direction: column !important; padding: 20px 0 !important; box-sizing: border-box !important; transform: translateZ(0); will-change: transform; } /* Main Feed Container with Multi‑Column Layout */ [data-testid="FeedPage-feed"], [data-testid="customFeedPage-feed"], [data-testid="followingFeedPage-feed"] { column-count: 3 !important; -webkit-column-count: 3 !important; -moz-column-count: 3 !important; column-gap: 20px !important; -webkit-column-gap: 20px !important; -moz-column-gap: 20px !important; width: calc(100vw - 200px) !important; margin-left: 200px !important; padding: 20px !important; box-sizing: border-box !important; display: block !important; overflow: visible !important; } /* Responsive Layout Breakpoints */ @media (max-width: 1600px) { [data-testid="FeedPage-feed"], [data-testid="customFeedPage-feed"], [data-testid="followingFeedPage-feed"] { column-count: 3 !important; } } @media (max-width: 1200px) { [data-testid="FeedPage-feed"], [data-testid="customFeedPage-feed"], [data-testid="followingFeedPage-feed"] { column-count: 2 !important; } nav[role="navigation"] { width: 150px !important; } [data-testid="FeedPage-feed"], [data-testid="customFeedPage-feed"], [data-testid="followingFeedPage-feed"] { width: calc(100vw - 150px) !important; margin-left: 150px !important; } } @media (max-width: 768px) { [data-testid="FeedPage-feed"], [data-testid="customFeedPage-feed"], [data-testid="followingFeedPage-feed"] { column-count: 1 !important; } nav[role="navigation"] { position: absolute !important; width: 100% !important; height: auto !important; border-right: none !important; border-bottom: 1px solid rgb(46, 64, 82) !important; flex-direction: row !important; flex-wrap: wrap !important; justify-content: space-between !important; padding: 10px !important; } [data-testid="FeedPage-feed"], [data-testid="customFeedPage-feed"], [data-testid="followingFeedPage-feed"] { width: 100% !important; margin-left: 0 !important; } nav[role="navigation"] > .css-175oi2r.r-1ipicw7.r-1xcajam.r-1rnoaur.r-pm9dpa.r-196lrry.css-175oi2r > .css-175oi2r { width: auto !important; margin-top: 0 !important; } } /* Individual Post Card Styling for Stability */ .css-175oi2r.r-1habvwh { display: block !important; width: 100% !important; margin: 0 0 20px !important; background: rgb(22, 30, 39) !important; border: 1px solid rgb(46, 64, 82) !important; border-radius: 8px !important; overflow: hidden !important; break-inside: avoid-column !important; page-break-inside: avoid !important; -webkit-column-break-inside: avoid !important; break-after: avoid-column !important; break-before: avoid-column !important; box-sizing: border-box !important; transition: all 0.2s ease-in-out !important; max-width: 100% !important; min-height: 150px !important; max-height: 500px !important; overflow-y: auto !important; } /* Hide Unnecessary Elements */ .r-2llsf.css-175oi2r > div.css-175oi2r:nth-of-type(1) > .css-175oi2r, .css-175oi2r[style*="position: fixed"][style*="inset: 0px 0px 0px 50%"], .css-175oi2r.r-18u37iz.r-1niwhzg.r-1e084wi { display: none !important; } `; document.head.appendChild(style); } /** * Adds event listeners to improve interactions. * For example, it prevents duplicate handling of like buttons. */ function handleInteractions() { document.body.addEventListener('click', (e) => { const likeButton = e.target.closest('[data-testid*="like-button"]'); if (likeButton && !likeButton.dataset.handled) { // Mark the like button to ensure the event isn't re-applied likeButton.dataset.handled = 'true'; } }, { capture: true, passive: true }); } /** * Ensures that individual posts do not break or jump between columns. * Re-applies CSS break-avoidance rules to every post card to maintain stability. */ function stabilizePosts() { const feed = document.querySelector( '[data-testid="FeedPage-feed"], [data-testid="customFeedPage-feed"], [data-testid="followingFeedPage-feed"]' ); if (!feed) return; feed.querySelectorAll('.css-175oi2r.r-1habvwh').forEach(post => { if (!post.dataset.stabilized) { post.style.display = 'block'; post.style.breakInside = 'avoid-column'; post.style.pageBreakInside = 'avoid'; post.style.webkitColumnBreakInside = 'avoid'; post.style.breakAfter = 'avoid-column'; post.style.breakBefore = 'avoid-column'; post.dataset.stabilized = 'true'; } }); } /** * Reorganizes the right-side menu into the left-hand navigation panel. * This creates a more uniform and accessible layout. */ function moveRightMenuIntoNav() { const rightMenu = document.querySelector( 'div[style*="padding: 20px 0px 20px 28px"][style*="position: fixed"][style*="left: 50%"][style*="width: 328px"]' ); const leftNav = document.querySelector('nav[role="navigation"]'); if (!rightMenu || !leftNav) return; // Reset styles for the right menu so it blends seamlessly into the left nav rightMenu.style.position = 'static'; rightMenu.style.left = 'auto'; rightMenu.style.top = 'auto'; rightMenu.style.transform = 'none'; rightMenu.style.width = 'auto'; rightMenu.style.margin = '0'; rightMenu.style.padding = '35px 15px 18px 15px'; rightMenu.style.gap = '16px'; rightMenu.style.maxHeight = '100%'; rightMenu.style.overflowY = 'auto'; leftNav.appendChild(rightMenu); } /** * Fine-tunes the top padding of a target container for visual consistency. */ function adjustTopPadding() { const targetDiv = document.querySelector( 'div.css-175oi2r[style*="gap: 10px;"][style*="padding-bottom: 2px;"][style*="overflow-y: auto"]' ); if (targetDiv) { targetDiv.style.paddingTop = '11px'; } } /** * Sets up a MutationObserver to monitor DOM changes. * When new posts or elements are injected, it re-applies the stabilization and layout tweaks. */ function setupMutationObserver() { let debounceTimeout; const observer = new MutationObserver(() => { clearTimeout(debounceTimeout); debounceTimeout = setTimeout(() => { stabilizePosts(); moveRightMenuIntoNav(); adjustTopPadding(); }, 200); }); observer.observe(document.body, { childList: true, subtree: true }); return observer; } /** * Disconnects the MutationObserver on page unload to clean up resources. */ function setupCleanup(observer) { window.addEventListener('unload', () => observer.disconnect()); } /** * Runs all layout modifications: * - Injects new CSS for a multi‑column, stable post layout. * - Reorganizes navigation elements. * - Handles interactive elements. * - Starts monitoring the DOM for dynamically added content. */ function runLayoutScript() { injectCSS(); moveRightMenuIntoNav(); adjustTopPadding(); handleInteractions(); setupCleanup(setupMutationObserver()); } /** * Waits until the user is logged in before running the layout modifications. * Uses an interval check to ensure the necessary UI elements are present. */ function waitForLoginAndRun() { if (userIsLoggedIn()) { runLayoutScript(); return; } const checkLoginInterval = setInterval(() => { if (userIsLoggedIn()) { clearInterval(checkLoginInterval); runLayoutScript(); } }, 1000); } // Initialize the script when the document is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', waitForLoginAndRun); } else { waitForLoginAndRun(); } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址