Makes Blue Sky's view significantly larger for ease of use, as well as larger icons while in threaded view.
// ==UserScript==
// @name Accessibility Blue Sky
// @namespace http://tampermonkey.net/
// @version 2025-10-26.12
// @description Makes Blue Sky's view significantly larger for ease of use, as well as larger icons while in threaded view.
// @license MIT
// @author You
// @match https://bsky.app/*
// @include http://bsky.app/*
// @include https://bsky.app/*
// @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant window.onurlchange
// @run-at document-start
// ==/UserScript==
(function() {
'use strict';
const ICON_SIZE = 42; // In pixels
const THREAD_SCALE = 1.5; // Percentage of the thread size
var BASE_WIDTH;
var actualScale;
var totalWidth;
var targetSize;
var url;
function ready() {
actualScale = THREAD_SCALE;
// Window's width
totalWidth = document.body.offsetWidth;
// Home View
handleHomeView();
// Post View
handlePostView();
// Profile View
handleProfileView();
}
function handleProfileView() {
var profileView = document.querySelectorAll('[data-testid="profileView"]')
if (!profileView.length) {
return;
}
const feedElem = document.querySelectorAll('[data-testid="profileScreen"]');
var frame = feedElem[feedElem.length - 1].previousElementSibling;
setBaseWidthOnce(frame.offsetWidth);
// Sidebars
var [nav, searchElem] = findNavAndSearch();
var shouldContinue = adjustSize(nav, searchElem);
if (!shouldContinue) {
return;
}
enlargeProfileView(targetSize, frame);
positionNavAndSearch(frame, nav, searchElem);
}
function enlargeProfileView(size, frame) {
frame.style.width = (size + 2) + "px"; // Add a small offset to look nice
var profileView = document.querySelectorAll('[data-testid="profileView"]');
var header = profileView[profileView.length - 1].querySelector('[style]');
header.style.maxWidth = size + "px";
var tablist = document.querySelectorAll('[role="tablist"');
tablist = tablist[tablist.length - 1];
tablist.parentElement.style.maxWidth = size + "px";
tablist.style.width = size + "px";
var threads = document.querySelectorAll('[data-testid="postsFeed-flatlist"]');
for (var i = 0; i < threads.length; i++) {
var thread = threads[i].querySelector('[style]');
thread.style.maxWidth = size + "px";
}
}
function handleHomeView() {
const feedElem = document.querySelectorAll('[data-testid="followingFeedPage-feed-flatlist"], [data-testid="customFeedPage-feed-flatlist"]');
if (!feedElem.length) {
return;
}
var mainElem;
// There's two feeds, Discovery and Following. We apply the same to each.
for (var i = 0; i < feedElem.length; i++) {
mainElem = feedElem[i].querySelector('[style]');
setBaseWidthOnce(mainElem.offsetWidth);
// Sidebars
var [nav, searchElem] = findNavAndSearch();
var shouldContinue = adjustSize(nav, searchElem);
if (!shouldContinue) {
return;
}
enlargeHomeView(targetSize, mainElem);
positionNavAndSearch(mainElem, nav, searchElem);
}
}
function enlargeHomeView(size, mainElem) {
mainElem.style.maxWidth = size + "px";
var frame = document.querySelectorAll('[class="css-g5y9jx r-1xcajam"]');
frame = frame[frame.length - 1];
frame.style.width = (size + 2) + "px"; // Add a small offset to look nice
var tabs = document.querySelectorAll('[data-testid="homeScreenFeedTabs"]');
tabs = tabs[tabs.length - 1];
tabs.style.width = size + "px";
}
function handlePostView() {
// Thread exists
const feedElem = document.querySelectorAll('[data-testid="postThreadScreen"]');
if (!feedElem.length) {
return;
}
var mainElem = feedElem[0].children[1].querySelector('[style]');
if (!mainElem) {
return;
}
setBaseWidthOnce(mainElem.offsetWidth);
// Sidebars
var [nav, searchElem] = findNavAndSearch();
var shouldContinue = adjustSize(nav, searchElem);
if (!shouldContinue) {
return;
}
enlargeIcons();
enlargeThread(targetSize);
positionNavAndSearch(mainElem, nav, searchElem);
}
function positionNavAndSearch(mainElem, nav, searchElem) {
// Get position of main thread element. It's not always centered.
var rect = mainElem.getBoundingClientRect();
// If the main element isn't visible, don't do anything.
if (!rect.left) {
return;
}
if (nav && nav.style) {
nav.style.transform = "";
nav.style.left = (100 * (rect.left - nav.offsetWidth) / totalWidth) + "%";
}
if (searchElem && searchElem.style) {
searchElem.style.transform = "";
searchElem.style.left = (100 * (rect.right / totalWidth)) + "%";
}
}
function adjustSize(nav, searchElem) {
var navWidth = nav.offsetWidth;
var searchWidth = searchElem.offsetWidth;
var baseTotalWidth = BASE_WIDTH + navWidth + searchWidth;
var requiredTotalWidth = targetSize + navWidth + searchWidth;
if (totalWidth >= requiredTotalWidth) {
// There's plenty of space, no need to adjust anything
} else if (totalWidth > baseTotalWidth) {
// There's more width than the default view, but not enough for the full scale. Partial scale it is.
targetSize = totalWidth - (navWidth + searchWidth);
actualScale = targetSize/BASE_WIDTH;
} else {
// Too small to adjust. Return.
return false;
}
return true;
}
function findNavAndSearch() {
// This class indicates a collapsed nav. If the class exists, it means the window is too small to be expanded. Reset the size to base.
var nav = document.getElementsByClassName("r-1e50gmw");
if (nav.length) {
actualScale = 1;
targetSize = BASE_WIDTH * actualScale;
}
// Otherwise, it means the nav is expanded, and we have more work to do.
nav = document.querySelectorAll("nav")[0];
// Search bar
var searchElem = nav.nextElementSibling;
return [nav, searchElem];
}
function setBaseWidthOnce(width) {
if (!BASE_WIDTH) {
BASE_WIDTH = width;
}
targetSize = BASE_WIDTH * actualScale;
}
function enlargeThread(size) {
const mainElem = document.querySelectorAll('[data-testid="postThreadScreen"]');
const threadScreen = mainElem[mainElem.length - 1];
const frame = threadScreen.previousElementSibling;
frame.style.width = (size + 2) + "px"; // Add a small offset to look nice
const header = threadScreen.children[0];
header.style.maxWidth = size + "px";
const thread = threadScreen.children[1].querySelector('[style]');
thread.style.maxWidth = size + "px";
}
/**
* Resizes the users' profile icons, and all nearby elements that need it.
*/
function enlargeIcons() {
var pfps = getIconsOtherThanNav()
// Resize
for (var i = 0; i < pfps.length; i++) {
var iconDiv = pfps[i]
resizeElem(iconDiv, ICON_SIZE);
resizeElem(iconDiv.parentElement, ICON_SIZE);
resizeElem(iconDiv.nextElementSibling, ICON_SIZE);
}
setTimeout(function() {
if (!validate()) {
enlargeIcons();
}
}, 600);
}
function getIconsOtherThanNav() {
const rawPfps = document.querySelectorAll('[data-testid="userAvatarImage"]');
var pfps = Array.from(rawPfps);
// Filter out the nav icon
for (var i = 0; i < pfps.length; i++) {
var nav = pfps[i].closest("nav");
if (nav) {
pfps.splice(i, 1);
break;
}
}
return pfps;
}
/**
* Changes the size of round elements such as profile pictures
* @param Node elem
* @param int size The pixel value to set the width/height
*/
function resizeElem(elem, size) {
if (!elem) { return; }
elem.style.width = size + "px";
elem.style.height = size + "px";
elem.style.borderRadius = size/2 + "px";
}
/**
* Confirms the changes were applied, and if not, retries.
* @returns True if the changes are ready.
*/
function validate() {
var pfps = getIconsOtherThanNav();
for (var i = 0; i < pfps.length; i++) {
if (parseInt(pfps[i].style.width) != ICON_SIZE) {
console.log("Failed to apply changes")
console.log(pfps[i]);
console.log(parseInt(pfps[i].style.width) + ":" + ICON_SIZE);
return false;
}
}
return true;
}
/**
* Waits for the page to be ready to interact with
* @param int timeout The amount of time to wait
*/
function initiate(timeout) {
// Test
var elem = getIconsOtherThanNav();
if (elem.length > 0) {
ready();
return;
}
setTimeout(function() {
initiate(2*timeout);
}, timeout);
}
/**
* Maintains new DOM elements in line with the changes.
*/
function driftCheck() {
url = window.location.href;
window.addEventListener('urlchange', () => {
ready();
});
window.addEventListener('resize', function() {
ready();
});
setInterval(function() {
if(url != window.location.href) {
url != window.location.href;
ready();
}
}, 400);
}
initiate(20);
driftCheck();
})();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址