Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save warrenseine/8eae46b6cabfa327225bd93efa99f852 to your computer and use it in GitHub Desktop.

Select an option

Save warrenseine/8eae46b6cabfa327225bd93efa99f852 to your computer and use it in GitHub Desktop.
Open the overview panel in GitHub code review
// ==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