Repository navigation
Pending-review leave warning and new commits since last look - #4
Merged
Merged
Conversation
…t look Pending-review guard: reads the pending comment count from GitHub's review button (React ButtonCounter / classic .js-pending-review-comment-count), seeded from the React payload's viewerPendingReview on a fresh load. beforeunload covers unloads; same-origin links that leave the PR get a confirm(). In-PR links, form submits, F5/Ctrl+R and the open review dialog never prompt. Since last look: records the PR head SHA when a file is marked Viewed and, once the head moves, links to /pull/<n>/files/<old>..<new> (or /compare/<old>..<new> after a force-push). The head comes from the commits route's JSON, cross-checked against its head-SHA channel; .patch is not used because it omits merge commits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two review-state features. Both are on by default and have toggles in settings. No new
@connecthost is needed.1. Warn before leaving with an unsubmitted pending review
What it does: If your pending review has comments, leaving the PR asks first. Same-origin links that go off the PR (another PR, issues, a blob view) get a
confirm(); cancelling it stops the navigation. Anything that unloads the page (closing the tab, cross-origin links, typed URLs) gets the browser's ownbeforeunloadprompt. These never prompt:/<o>/<r>/pull/<n>(any tab, hash links,/files/a..b)How it detects the pending review (
pendingReviewCount). It checks these in order:button[class*="ReviewMenuButton__"]) is a PrimerButtonwithcount={pendingReviewIDs.length || undefined}. Primer renders that as[data-component="ButtonCounter"]. I confirmed both in GitHub's shipped bundles (theReviewMenuButtoncomponent in the75-*.jschunk, andButtonCounterinprimer-react-*.js). The same bundle mounts this button in the PR's sticky header on every tab..js-pending-review-comment-count. GitHub'sdiffs-*.jskeeps it in sync withpendingReviewCommentsCountand togglesis-review-pending.viewerPendingReviewfrom the React payload (react-app[initial-path]→script[data-target="react-app.embeddedData"]). I confirmed it in a real PR page ({"id":null,"comments":[]}when signed out). It's only used wheninitial-pathis this PR, so a stale payload from another page after in-app navigation is ignored.False-positive guards:
clearPendingReview(); window.location.href = redirectUrl(seen in the bundle), so the counter is likely still rendered whenbeforeunloadfires.beforeunloadtherefore stays quiet while the review overlay's#anchored-review-titleis on the page.2. New commits since your last look
What it does: The panel shows "N new commits since your last look", with a View changes chip and a Dismiss button. The chip opens GitHub's own range view
/<o>/<r>/pull/<n>/files/<old>..<new>.Getting the head SHA without a token.
loadCommitOidsfetches/<o>/<r>/pull/<n>/commitswithAccept: application/json(the request GitHub's own React router makes). That returnspayload.pullRequestsCommitsRoute.commitGroups[].commits[].oid, about 2 KB per commit. If the response isn't JSON, it parses the same payload out of the HTML page. The last oid is cross-checked against the head SHA embedded inmetadata.commitHeadShaChannel. If the route istruncatedor the two disagree, the feature shows nothing.Rejected alternative:
.patch. Each commit starts withFrom <sha>, but.patchleaves out merge commits. On microsoft/vscode#342850, whose head is "Merge branch 'main' into …", the lastFromwas2d80450while the real head isad2fe67. A head-only push of a merge from main would also go unnoticed. It also grows with the diff size times the commit count.What counts as the "last look": the head SHA when you last tick a file's Viewed box. It hooks into the existing Viewed-control binding. I chose this over review submission because:
Un-ticking Viewed doesn't move the last look. Dismiss sets it to the current head.
Force-pushes: when the old SHA is no longer in the PR's commit list, the PR range view 404s ("Commit range not found"; checked on cli/cli#14655 with its real before/after force-push SHAs). The link switches to
/<o>/<r>/compare/<old>..<new>, which returns 200 and is the same link GitHub uses for force-push events. The panel then says "Force-pushed since your last look" and notes that a rebase also brings in base-branch changes.Storage is
v3:lastLook:<digest>, whichpruneStalePrsprunes.Verified
node --checkpasses./pull/<n>/commitsJSON and HTML give the right head and count on Refreshable tokens: build-gated debug overrides for testing token expiry cli/cli#14752, markdown: restore the scroll position of the preview microsoft/vscode#342850 (merge-commit head), Rollup of 14 pull requests rust-lang/rust#164640 (48 commits over several days) and Fix Turbopack WASM route over-tracing package dependencies vercel/next.js#100391. Each was checked againstgh pr view --json headRefOid,commits.Accept: application/jsonis required./files/<a>..<b>,/changes/<a>..<b>(redirects to/files) and short SHAs all return 200 for in-PR ranges.GM_*stubs (scratch harness, not committed). All 49 checks pass. They cover:/pull/147vs/pull/14752, outbound links with cancel and accept, external,_blankand Ctrl-click links, the allowance window expiring, the open review dialog, counter removed, the sticky count, form submit, classic counters, payload on and off, a payload for another PR, the live button overriding a stale payload, the feature-off case, and a real signed-out PR pageUnverified (no signed-in browser available)
viewerPendingReview.commentslists the pending comments, and the shape of the classic count element. These are inferred from GitHub's current JS bundles, not from a rendered page.#anchored-review-titleis still present whenbeforeunloadfires during a React submit. If it isn't, submitting a review from the Files tab could prompt once.beforeunloadprompt behaves in Tampermonkey's sandbox (it usespreventDefaultplusreturnValue).confirm()in a capture-phase window click listener reliably beats GitHub's Turbo and React Router handlers. It should, because they checkdefaultPrevented.truncatedor a channel mismatch.Manual test
/files/<old>..<new>with only those commits./compare/.... Dismiss hides the row.🤖 Generated with Claude Code