Last active
September 3, 2026 13:36
-
-
Save warrenseine/8eae46b6cabfa327225bd93efa99f852 to your computer and use it in GitHub Desktop.
Open the overview panel in GitHub code review
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name Open the overview panel in GitHub code review | |
| // @namespace http://tampermonkey.net/ | |
| // @version 2026-09-03 | |
| // @description Open the overview panel in GitHub code review | |
| // @author Warren Seine | |
| // @match https://github.com/*/*/pull/* | |
| // @icon https://www.google.com/s2/favicons?sz=64&domain=github.com | |
| // @run-at document-idle | |
| // @grant none | |
| // ==/UserScript== | |
| (function() { | |
| 'use strict'; | |
| // The diff tab, where the overview panel lives. | |
| const DIFF_TAB = /^\/[^/]+\/[^/]+\/pull\/\d+\/(?:changes|files)\b/; | |
| // The toolbar toggle, labelled Open overview panel / Close overview panel. | |
| const TOGGLE = /overview panel/i; | |
| // Closing the side panel, either from the toolbar or from the panel header. | |
| const CLOSE = /^close (?:side|overview) panel$/i; | |
| // The panel header, present for whichever view is on screen — Overview, | |
| // Comments, Alerts, Merge status, any of them. | |
| const PANEL = '[class*="Panel-module__Header__"], [class*="PanelNavigationDropdown"]'; | |
| // The header's close button, whose label is the same signal without leaning | |
| // on a hashed class name. | |
| const CLOSE_SIDE = /^close side panel$/i; | |
| // GitHub can re-render the toolbar with the panel shut, so opening is retried | |
| // rather than done once. The cap per visit stops the retry from becoming a loop. | |
| const MAX_OPENS = 5; | |
| let tab = null; | |
| let opens = 0; | |
| let dismissed = false; | |
| function label(button) { | |
| const direct = button.getAttribute('aria-label'); | |
| if (direct) return direct; | |
| // Most of these buttons carry their text in a tooltip instead. | |
| const tooltip = document.getElementById(button.getAttribute('aria-labelledby') || ''); | |
| return tooltip ? tooltip.textContent : ''; | |
| } | |
| function buttons() { | |
| return [...document.querySelectorAll('button')]; | |
| } | |
| function panelIsOpen() { | |
| return !!document.querySelector(PANEL) || buttons().some(b => CLOSE_SIDE.test(label(b).trim())); | |
| } | |
| function openOverview() { | |
| if (!DIFF_TAB.test(document.location.pathname)) { | |
| tab = null; | |
| return; | |
| } | |
| if (tab !== document.location.pathname) { | |
| tab = document.location.pathname; | |
| opens = 0; | |
| dismissed = false; | |
| } | |
| if (dismissed || opens >= MAX_OPENS) return; | |
| // A panel showing another view still reads as an unexpanded overview | |
| // toggle. Opening it there would drag the reader back to Overview, so the | |
| // panel being shut at all is the condition, not the overview toggle alone. | |
| if (panelIsOpen()) return; | |
| const button = buttons().find(b => TOGGLE.test(label(b))); | |
| if (!button) return; | |
| opens += 1; | |
| button.click(); | |
| } | |
| // Closing the panel by hand settles it for this visit. | |
| document.addEventListener('click', event => { | |
| if (!event.isTrusted || !event.target.closest) return; | |
| const button = event.target.closest('button'); | |
| if (button && CLOSE.test(label(button).trim())) dismissed = true; | |
| }, true); | |
| // The toolbar renders late, and the diff tab can be reached by client-side | |
| // navigation, so keep watching instead of running once. | |
| let scheduled = false; | |
| const observer = new MutationObserver(() => { | |
| if (scheduled) return; | |
| scheduled = true; | |
| // A timer rather than requestAnimationFrame, which never fires while the | |
| // tab sits in the background. | |
| setTimeout(() => { | |
| scheduled = false; | |
| openOverview(); | |
| }, 50); | |
| }); | |
| observer.observe(document.body, {childList: true, subtree: true}); | |
| openOverview(); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment