Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save warrenseine/94433bd95dde9675fe7af50d3cb0a6a4 to your computer and use it in GitHub Desktop.

Select an option

Save warrenseine/94433bd95dde9675fe7af50d3cb0a6a4 to your computer and use it in GitHub Desktop.
Open Linear reviews on the Diff tab
// ==UserScript==
// @name Open Linear reviews on the Diff tab
// @namespace http://tampermonkey.net/
// @version 2026-09-03
// @description Open Linear reviews on the Diff tab
// @author Warren Seine
// @match https://linear.app/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=linear.app
// @run-at document-idle
// @grant none
// ==/UserScript==
(function() {
'use strict';
// The bare review page, e.g. /hey-pearl/review/<slug> — not its sub-tabs
// (/changes is the Diff tab, /review is the Guide tab).
const REVIEW = /^\/[^/]+\/review\/[^/]+\/?$/;
const DIFF_TAB = 'changes';
// The tab renders late in the client-side app. Give up rather than move a
// review that has been sitting open.
const TIMEOUT = 20000;
let review = null;
let since = 0;
function diffPath() {
return document.location.pathname.replace(/\/$/, '') + '/' + DIFF_TAB;
}
function redirect() {
if (!REVIEW.test(document.location.pathname)) {
review = null;
return;
}
// A link to a specific comment goes where it points.
if (document.location.hash) return;
if (review !== document.location.pathname) {
review = document.location.pathname;
since = Date.now();
}
if (Date.now() - since > TIMEOUT) return;
// Once per review per tab, so clicking back to Overview sticks.
const key = 'linear-diff-redirect:' + document.location.pathname;
try {
if (sessionStorage.getItem(key)) return;
} catch (error) {
// Private windows and blocked storage: fall through and redirect once.
}
// The Diff tab's presence is what says the review has finished rendering.
const path = diffPath();
if (![...document.querySelectorAll('a')].some(a => a.getAttribute('href') === path)) return;
try {
sessionStorage.setItem(key, '1');
} catch (error) {
// Nothing to remember; the redirect below still runs.
}
// The tab's own click handler ignores scripted clicks, so navigate by URL
// and keep the query, which carries flags like ?stay. Replace rather than
// assign: the review root drops out of the history, so going back lands on
// whatever came before it instead of redirecting again.
document.location.replace(path + document.location.search);
}
// Reviews are opened by client-side navigation as often as by page load, and
// the tabs render late, so poll instead of running once.
setInterval(redirect, 300);
redirect();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment