Created
September 9, 2025 14:15
-
-
Save levibostian/b8b398ab3bcd7fd461ad6a38d1fdb68e to your computer and use it in GitHub Desktop.
tampermonkey github pull request mark file as Viewed keyboard shortcut. Found it in https://github.com/orgs/community/discussions/10197, but modified it over time because I found some bugs.
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 GitHub PR review keyboard shortcut | |
// @version 0.3 | |
// @description Mark file as "viewed" on GitHub PR UI when hovering and pressing 'Escape' key | |
// @match https://github.com/* | |
// @author dvdvdmt, nbolton | |
// @source https://github.com/orgs/community/discussions/10197 | |
// ==/UserScript== | |
(function() { | |
'use strict'; | |
if (window.disposeMarkAsViewedByEscape) { | |
window.disposeMarkAsViewedByEscape(); | |
} | |
window.disposeMarkAsViewedByEscape = start(); | |
function start() { | |
window.addEventListener('keydown', handleKeyDown); | |
return () => window.removeEventListener('keydown', handleKeyDown); | |
} | |
function isInEditableArea(el) { | |
if (!el) return false; | |
// If the target or any ancestor is input/textarea/select | |
if (el.closest && el.closest('input, textarea, select')) return true; | |
// If the target or ancestor has contentEditable=true (or is contentEditable) | |
const editable = el.closest && el.closest('[contenteditable]'); | |
if (editable) { | |
const ce = editable.getAttribute('contenteditable'); | |
// treat empty string or "true" as editable; also fallback to isContentEditable | |
if (ce === '' || ce === 'true' || editable.isContentEditable) return true; | |
} | |
// Some GitHub editors use role="textbox" | |
if (el.closest && el.closest('[role="textbox"]')) return true; | |
return false; | |
} | |
function markFileAsViewed() { | |
console.debug("Marking file as viewed"); | |
const fileElement = document.querySelector(`[id^="diff-"]:hover`); | |
if (!fileElement){ | |
console.debug("No file element under cursor"); | |
return; | |
} | |
const buttons = [...fileElement.querySelectorAll('button')]; | |
if (buttons.length === 0) { | |
console.debug("No buttons found in file element"); | |
return; | |
} | |
// GitHub sometimes localizes the label; we attempt to match the visible label | |
const checkbox = buttons.find(btn => btn.textContent && btn.textContent.trim() === 'Viewed'); | |
if (!checkbox) { | |
console.debug("No 'Viewed' checkbox/button in file element"); | |
return; | |
} | |
checkbox.click(); | |
} | |
function handleKeyDown(event) { | |
// Only act on plain Escape without modifiers | |
if (event.key !== 'Escape' || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) { | |
return; | |
} | |
// If focus is inside any editable input/textarea/contentEditable, do nothing | |
if (isInEditableArea(event.target)) { | |
return; | |
} | |
markFileAsViewed(); | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment