Created
September 3, 2026 15:12
-
-
Save warrenseine/94433bd95dde9675fe7af50d3cb0a6a4 to your computer and use it in GitHub Desktop.
Open Linear reviews on the Diff tab
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 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