| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224 |
- import Foundation
- enum RedditHomeChromeStyle {
- /// Keeps Reddit header separators flush with the in-app sidebar divider and
- /// fixes Log In / menu hit targets that get covered by borders or feed chrome.
- static let injectionScript = """
- (function() {
- const STYLE_ID = 'reddora-home-chrome-style';
- if (window.__reddoraHomeChromeInstalled) return;
- window.__reddoraHomeChromeInstalled = true;
- const HEADER_ACTION_IDS = ['login-button', 'expand-user-drawer-button'];
- const DECORATIVE_SELECTORS = [
- 'faceplate-border',
- 'hr',
- '[role="separator"]',
- 'shreddit-overlay-display'
- ].join(', ');
- const style = document.createElement('style');
- style.id = STYLE_ID;
- style.textContent = `
- html,
- body,
- shreddit-app {
- margin-top: 0 !important;
- padding-top: 0 !important;
- }
- header,
- header > * {
- margin-left: 0 !important;
- }
- header {
- position: sticky !important;
- top: 0 !important;
- z-index: 300 !important;
- align-items: center !important;
- isolation: isolate !important;
- margin-top: 0 !important;
- padding-top: 0 !important;
- background: var(--shreddit-content-background, #ffffff) !important;
- }
- header faceplate-border,
- header [role="separator"],
- header hr {
- width: 100% !important;
- max-width: none !important;
- margin-left: 0 !important;
- margin-right: 0 !important;
- pointer-events: none !important;
- z-index: 0 !important;
- }
- header faceplate-search-input,
- header #SearchDropdown,
- header [data-testid="search-input-container"] {
- flex: 1 1 auto !important;
- min-width: 0 !important;
- max-width: 100% !important;
- overflow: hidden !important;
- align-self: center !important;
- pointer-events: auto !important;
- }
- /* Wrappers around header actions often extend past the visible button. */
- faceplate-tracker:has(#login-button),
- faceplate-tracker:has(#expand-user-drawer-button),
- rpl-tooltip:has(#login-button),
- rpl-tooltip:has(#expand-user-drawer-button),
- activate-feature:has(#expand-user-drawer-button) {
- display: contents !important;
- }
- #login-button,
- #expand-user-drawer-button {
- position: relative !important;
- z-index: 301 !important;
- pointer-events: auto !important;
- flex-shrink: 0 !important;
- align-self: center !important;
- touch-action: manipulation !important;
- }
- /* Only suppress inert overlay layers that block header clicks. */
- shreddit-overlay-display:not(:has(
- button,
- a,
- [role="button"],
- [role="menu"],
- [role="menuitem"],
- [role="dialog"],
- [role="listbox"],
- faceplate-menu,
- input:not([type="hidden"]),
- textarea
- )) {
- pointer-events: none !important;
- }
- /* Feed sort row sits below the header; keep it from overlapping action buttons. */
- shreddit-sort-dropdown,
- [data-testid="sort-dropdown"],
- [data-testid="sort-posts-dropdown"],
- shreddit-feed-sort-dropdown {
- position: relative !important;
- z-index: 1 !important;
- }
- `;
- (document.head || document.documentElement).appendChild(style);
- function isDecorative(node) {
- if (!(node instanceof Element)) return false;
- return node.matches(DECORATIVE_SELECTORS)
- || (node.closest('header') && node.matches('faceplate-border, hr, [role="separator"]'));
- }
- function isInteractive(node) {
- if (!(node instanceof Element)) return false;
- return node.matches(
- 'button, a, [role="button"], input, select, textarea, label, faceplate-button'
- );
- }
- function clearBlockersForButton(button) {
- if (!(button instanceof Element)) return;
- const rect = button.getBoundingClientRect();
- if (rect.width < 1 || rect.height < 1) return;
- const sampleYs = [
- rect.top + rect.height * 0.25,
- rect.top + rect.height * 0.5,
- rect.top + rect.height * 0.75,
- rect.bottom - 1
- ];
- const x = rect.left + rect.width * 0.5;
- sampleYs.forEach(function(y) {
- const stack = document.elementsFromPoint(x, y);
- for (let i = 0; i < stack.length; i++) {
- const el = stack[i];
- if (el === button || button.contains(el)) break;
- if (isInteractive(el)) break;
- if (isDecorative(el) || !el.closest('header')) {
- el.style.setProperty('pointer-events', 'none', 'important');
- el.dataset.reddoraPointerFix = '1';
- }
- }
- });
- }
- function pinHeader() {
- document.querySelectorAll('header, reddit-header-large, faceplate-header').forEach(function(node) {
- if (!(node instanceof HTMLElement)) return;
- node.style.setProperty('position', 'sticky', 'important');
- node.style.setProperty('top', '0', 'important');
- node.style.setProperty('z-index', '300', 'important');
- node.style.setProperty(
- 'background',
- 'var(--shreddit-content-background, #ffffff)',
- 'important'
- );
- });
- }
- function patchHeaderHitTargets() {
- pinHeader();
- HEADER_ACTION_IDS.forEach(function(id) {
- const button = document.getElementById(id);
- if (!button) return;
- clearBlockersForButton(button);
- button.style.setProperty('position', 'relative', 'important');
- button.style.setProperty('z-index', '301', 'important');
- });
- document.querySelectorAll('header faceplate-border, header hr, header [role="separator"]').forEach(function(el) {
- el.style.setProperty('pointer-events', 'none', 'important');
- });
- }
- let patchTimer = null;
- function schedulePatch() {
- if (patchTimer) return;
- patchTimer = setTimeout(function() {
- patchTimer = null;
- patchHeaderHitTargets();
- }, 50);
- }
- patchHeaderHitTargets();
- [0, 100, 300, 750, 1500, 3000].forEach(function(delay) {
- setTimeout(patchHeaderHitTargets, delay);
- });
- const observer = new MutationObserver(schedulePatch);
- observer.observe(document.documentElement, { childList: true, subtree: true, attributes: true });
- window.addEventListener('resize', schedulePatch, { passive: true });
- window.addEventListener('scroll', schedulePatch, { passive: true });
- function forwardClicksWithinButtonBounds(event) {
- if (event.type !== 'pointerdown') return;
- const menuButton = document.getElementById('expand-user-drawer-button');
- if (!menuButton || menuButton.contains(event.target)) return;
- const rect = menuButton.getBoundingClientRect();
- const x = event.clientX;
- const y = event.clientY;
- if (x < rect.left || x > rect.right || y < rect.top || y > rect.bottom) return;
- event.preventDefault();
- event.stopImmediatePropagation();
- menuButton.click();
- }
- document.addEventListener('pointerdown', forwardClicksWithinButtonBounds, true);
- })();
- """
- }
|