Widen FB floating windows by 1.4x, center them, perfectly nudge menus, auto-select "All Comments", and fix keyboard scrolling.
// ==UserScript==
// @name FB Floating Window Enhancer
// @namespace http://tampermonkey.net/
// @version V1.7.0
// @description Widen FB floating windows by 1.4x, center them, perfectly nudge menus, auto-select "All Comments", and fix keyboard scrolling.
// @author Gemini
// @match *://*.facebook.com/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
// 1. Resize Floating Window (1.4x width, keep text size, and force centering)
const resizeObserver = new MutationObserver(() => {
const dialogs = document.querySelectorAll('div[role="dialog"]');
dialogs.forEach(dialog => {
if (dialog.dataset.resized) return;
const innerDivs = dialog.querySelectorAll('div');
for (let div of innerDivs) {
const style = window.getComputedStyle(div);
const maxWidth = parseInt(style.maxWidth);
const width = parseInt(style.width);
if ((maxWidth > 300 && maxWidth < 1200) || (width > 300 && width < 1200 && style.maxWidth === 'none')) {
const targetWidth = maxWidth || width;
div.style.maxWidth = `${targetWidth * 1.4}px`;
div.style.width = '100%';
div.style.marginLeft = 'auto';
div.style.marginRight = 'auto';
if (div.parentElement) {
div.parentElement.style.display = 'flex';
div.parentElement.style.justifyContent = 'center';
div.parentElement.style.width = '100%';
}
dialog.dataset.resized = "true";
break;
}
}
});
});
resizeObserver.observe(document.body, { childList: true, subtree: true });
// 2. Automation Loop (Menu Boundary Nudge + Auto-Select "All Comments")
setInterval(() => {
// --- A. Deep Menu Bounds Fixer (From V1.4.0) ---
const menuItems = document.querySelectorAll('[role="menuitem"]');
const containersToFix = new Set();
menuItems.forEach(item => {
let el = item.parentElement;
while (el && el !== document.body) {
const style = window.getComputedStyle(el);
if (style.position === 'absolute' || style.position === 'fixed') {
containersToFix.add(el);
break;
}
el = el.parentElement;
}
});
containersToFix.forEach(container => {
const rect = container.getBoundingClientRect();
if (rect.width === 0 || rect.height === 0) return;
if (!container.dataset.posFixed) {
const currentMarginLeft = parseFloat(window.getComputedStyle(container).marginLeft) || 0;
// If menu touches or bleeds past the left screen edge (< 20px)
if (rect.left < 20) {
const shift = 30 - rect.left;
container.style.setProperty('margin-left', `${currentMarginLeft + shift}px`, 'important');
container.dataset.posFixed = "true";
}
}
});
// --- B. Auto-select "所有留言" (All Comments) ---
const buttons = document.querySelectorAll('div[role="button"]');
for (let btn of buttons) {
const text = btn.textContent.trim();
// Check if the button says "最相關" and hasn't been clicked by us yet
if ((text.startsWith("最相關") || text.startsWith("相關留言")) && !btn.dataset.autoSorted) {
btn.dataset.autoSorted = "processing";
// Click the dropdown to open it
btn.click();
let attempts = 0;
// Rapidly check for the opened menu items
const findMenu = setInterval(() => {
attempts++;
const dropdownItems = document.querySelectorAll('div[role="menuitem"]');
for (let item of dropdownItems) {
if (item.textContent.trim().startsWith("所有留言")) {
clearInterval(findMenu);
item.click(); // Select "所有留言"
btn.dataset.autoSorted = "done";
return;
}
}
// Timeout after ~2 seconds to prevent infinite loops
if (attempts > 20) {
clearInterval(findMenu);
btn.removeAttribute('data-autoSorted'); // Allow retry
}
}, 100);
}
}
}, 200);
// 3. Enable Keyboard Navigation (Forced Capture Phase)
window.addEventListener('keydown', (e) => {
const dialogs = Array.from(document.querySelectorAll('div[role="dialog"]')).filter(d => d.offsetWidth > 0 && d.offsetHeight > 0);
if (dialogs.length === 0) return;
const activeDialog = dialogs[dialogs.length - 1];
const tag = e.target.tagName.toLowerCase();
if (tag === 'input' || tag === 'textarea' || e.target.isContentEditable) return;
const scrollKeys = ['ArrowUp', 'ArrowDown', 'PageUp', 'PageDown', 'Home', 'End'];
if (!scrollKeys.includes(e.key)) return;
let targetScroller = null;
let maxScrollHeight = 0;
const candidates = [activeDialog, ...Array.from(activeDialog.querySelectorAll('*'))];
for (let el of candidates) {
if (el.scrollHeight > el.clientHeight + 10) {
const style = window.getComputedStyle(el);
if (['auto', 'scroll', 'overlay'].includes(style.overflowY)) {
if (el.scrollHeight > maxScrollHeight) {
maxScrollHeight = el.scrollHeight;
targetScroller = el;
}
}
}
}
if (!targetScroller) return;
const scrollAmount = 80;
const pageScrollAmount = targetScroller.clientHeight * 0.8;
switch (e.key) {
case 'ArrowUp':
targetScroller.scrollBy({ top: -scrollAmount, behavior: 'auto' });
break;
case 'ArrowDown':
targetScroller.scrollBy({ top: scrollAmount, behavior: 'auto' });
break;
case 'PageUp':
targetScroller.scrollBy({ top: -pageScrollAmount, behavior: 'auto' });
break;
case 'PageDown':
targetScroller.scrollBy({ top: pageScrollAmount, behavior: 'auto' });
break;
case 'Home':
targetScroller.scrollTo({ top: 0, behavior: 'auto' });
break;
case 'End':
targetScroller.scrollTo({ top: targetScroller.scrollHeight, behavior: 'auto' });
break;
}
e.preventDefault();
e.stopImmediatePropagation();
e.stopPropagation();
}, true);
})();