Repository: refined-github/refined-github
Pull request #7715 (2024-08-21 07:30:52)

`extend-conversation-status-filters` - Improve reliability
Related with: https://github.com/refined-github/refined-github/issues/5871 

before:

https://github.com/user-attachments/assets/12c51cd9-aadd-4e13-bfa0-c7fd1e1a1e1a

after:


https://github.com/user-attachments/assets/83c43900-51d7-4f5a-af6d-2b781f4bf5a8



fregante: Also thanks for linking to https://github.com/refined-github/refined-github/issues/5871, I forgot this was an issue. I’ve had a problem with buttons not working recently but I didn’t understand why. Maybe this was the reason. 
fregante: I'd rather not make the UI too cluttered, I've only been asked that question once before so I assume not many need it anyway.
kovsu: PR list page:

https://github.com/user-attachments/assets/27301728-1dee-4826-8028-587bbd4b468a

Issue list page:

https://github.com/user-attachments/assets/3d455a24-bf2c-4ffe-8db8-6057ad7ae060



Patch:
diff --git a/build/__snapshots__/features-meta.json b/build/__snapshots__/features-meta.json
index b4a69d20..4b5b778e 100644
--- a/build/__snapshots__/features-meta.json
+++ b/build/__snapshots__/features-meta.json
@@ -17,6 +17,7 @@
 	{
 		"id": "align-issue-labels",
 		"description": "In issue/PR lists, aligns the labels to the left, below each title.",
+		"css": true,
 		"screenshot": "https://github-production-user-asset-6210df.s3.amazonaws.com/83146190/261160640-28ae4f12-0e95-4db5-a79c-e89ae523a4d0.png"
 	},
 	{
@@ -42,6 +43,7 @@
 	{
 		"id": "ci-link",
 		"description": "Adds a build/CI status icon next to the repo’s name.",
+		"css": true,
 		"screenshot": "https://github-production-user-asset-6210df.s3.amazonaws.com/1402241/237923995-5e14a272-0bf2-4fe4-b409-8c05378aa4fd.png"
 	},
 	{
@@ -52,16 +54,19 @@
 	{
 		"id": "clean-conversation-headers",
 		"description": "Removes duplicate information in the header of issues and PRs (\"User wants to merge X commits from Y into Z\")",
+		"css": true,
 		"screenshot": "https://user-images.githubusercontent.com/44045911/112314137-a34b0680-8ce3-11eb-9e0e-8afd6c8235c2.png"
 	},
 	{
 		"id": "clean-conversation-sidebar",
 		"description": "Hides empty sections (or just their \"empty\" label) in the issue/PR sidebar.",
+		"css": true,
 		"screenshot": "https://github-production-user-asset-6210df.s3.amazonaws.com/83146190/253054419-48c38c01-b1dc-42ca-9ff6-fd63392b5921.png"
 	},
 	{
 		"id": "clean-pinned-issues",
 		"description": "Changes the layout of pinned issues from side-by-side to a standard list.",
+		"css": true,
 		"screenshot": "https://github-production-user-asset-6210df.s3.amazonaws.com/83146190/258224321-e8ee8c70-6952-4a42-8626-6b5f31d167a3.png"
 	},
 	{
@@ -77,6 +82,7 @@
 	{
 		"id": "clean-repo-sidebar",
 		"description": "Removes unnecessary or redundant information from the repository sidebar.",
+		"css": true,
 		"screenshot": "https://user-images.githubusercontent.com/46634000/107955448-18694480-6f9e-11eb-8bc6-80cc90d910bc.png"
 	},
 	{
@@ -87,6 +93,7 @@
 	{
 		"id": "clean-rich-text-editor",
 		"description": "Hides unnecessary comment field tooltips and toolbar items (each one has a keyboard shortcut.)",
+		"css": true,
 		"screenshot": "https://user-images.githubusercontent.com/46634000/158201651-7364aba7-f9d0-4a51-89c4-2ced0cc34e48.png"
 	},
 	{
@@ -137,6 +144,7 @@
 	{
 		"id": "conflict-marker",
 		"description": "Shows which PRs have conflicts in PR lists.",
+		"css": true,
 		"screenshot": "https://github-production-user-asset-6210df.s3.amazonaws.com/83146190/253128438-d67c8f49-44f1-4e15-9363-a717109fef39.png"
 	},
 	{
@@ -172,11 +180,13 @@
 	{
 		"id": "cross-deleted-pr-branches",
 		"description": "Adds a line-through to the deleted branches in PRs.",
+		"css": true,
 		"screenshot": "https://github-production-user-asset-6210df.s3.amazonaws.com/140871606/256963526-646ac7d0-3e7f-40c6-ba39-014b49bc0063.png"
 	},
 	{
 		"id": "deep-reblame",
 		"description": "When exploring blames, <code>Alt</code>-clicking the “Reblame” buttons will extract the associated PR’s commits first, instead of treating the commit as a single change.",
+		"css": true,
 		"screenshot": "https://github-production-user-asset-6210df.s3.amazonaws.com/140871606/257035884-732ee7ff-22c5-4049-af7d-f11117d2bbe4.png"
 	},
 	{
@@ -187,6 +197,7 @@
 	{
 		"id": "dim-bots",
 		"description": "Dims commits and PRs by bots to reduce noise.",
+		"css": true,
 		"screenshot": "https://user-images.githubusercontent.com/1402241/220607557-f8ea0863-f05b-48c8-a447-1fec42af0afd.gif"
 	},
 	{
@@ -217,6 +228,7 @@
 	{
 		"id": "emphasize-draft-pr-label",
 		"description": "Makes it easier to distinguish draft PR in lists.",
+		"css": true,
 		"screenshot": "https://user-images.githubusercontent.com/1402241/218252438-062a1ab3-4437-436d-9140-87bee23aaefb.png"
 	},
 	{
@@ -242,6 +254,7 @@
 	{
 		"id": "extend-diff-expander",
 		"description": "Widens the <code>Expand diff</code> button to be clickable across the screen.",
+		"css": true,
 		"screenshot": "https://user-images.githubusercontent.com/1402241/152118201-f25034c7-6fae-4be0-bb3f-c217647e32b7.gif"
 	},
 	{
@@ -252,6 +265,7 @@
 	{
 		"id": "fit-textareas",
 		"description": "Auto-resizes comment fields to fit their content and no longer show scroll bars.",
+		"css": true,
 		"screenshot": "https://user-images.githubusercontent.com/1402241/54336211-66fd5e00-4666-11e9-9c5e-111fccab004d.gif"
 	},
 	{
@@ -267,16 +281,19 @@
 	{
 		"id": "global-conversation-list-filters",
 		"description": "Adds filters for issues/PRs <em>in your repos</em> and <em>commented on by you</em> in the global issue/PR search.",
+		"css": true,
 		"screenshot": "https://github-production-user-asset-6210df.s3.amazonaws.com/83146190/253048449-2f7cc331-c379-4ec0-a542-441e8b4f8d79.png"
 	},
 	{
 		"id": "hidden-review-comments-indicator",
 		"description": "Adds comment indicators when comments are hidden in PR review.",
+		"css": true,
 		"screenshot": "https://github-production-user-asset-6210df.s3.amazonaws.com/83146190/253128043-a10eaf9e-ff81-48db-b67c-ee823804c859.gif"
 	},
 	{
 		"id": "hide-diff-signs",
 		"description": "Hides diff signs since diffs are color coded already.",
+		"css": true,
 		"screenshot": "https://user-images.githubusercontent.com/1402241/54807718-149cec80-4cb9-11e9-869c-e265863211e3.png"
 	},
 	{
@@ -292,16 +309,19 @@
 	{
 		"id": "hide-low-quality-comments",
 		"description": "Hides reaction comments (\"+1\", \"👍\", …) (except the maintainers’) but they can still be shown.",
+		"css": true,
 		"screenshot": "https://github-production-user-asset-6210df.s3.amazonaws.com/83146190/258220965-4743b9b9-2aef-41b3-a905-ccf8d7beb74e.png"
 	},
 	{
 		"id": "hide-navigation-hover-highlight",
 		"description": "Removes the file hover effect in the repo file browser.",
+		"css": true,
 		"screenshot": null
 	},
 	{
 		"id": "hide-newsfeed-noise",
 		"description": "Hides other inutile newsfeed events (commits, forks, new followers).",
+		"css": true,
 		"screenshot": null
 	},
 	{
@@ -312,11 +332,13 @@
 	{
 		"id": "highest-rated-comment",
 		"description": "Highlights the most useful comment in issues.",
+		"css": true,
 		"screenshot": "https://github-production-user-asset-6210df.s3.amazonaws.com/83146190/252763905-a0c3b074-b032-4d97-946e-328e8a6fb2da.png"
 	},
 	{
 		"id": "highlight-collaborators-and-own-conversations",
 		"description": "Highlights issues/PRs opened by you or the current repo’s collaborators.",
+		"css": true,
 		"screenshot": "https://github-production-user-asset-6210df.s3.amazonaws.com/83146190/252804821-a412e05c-fb76-400b-85b5-5acbda538ab2.png"
 	},
 	{
@@ -367,6 +389,7 @@
 	{
 		"id": "link-to-compare-diff",
 		"description": "Linkifies the \"X files changed\" text on compare pages to allow jumping to the diff.",
+		"css": true,
 		"screenshot": "https://user-images.githubusercontent.com/46634000/157072587-0335357a-18c7-44c4-ae6e-237080fb36b4.png"
 	},
 	{
@@ -437,16 +460,19 @@
 	{
 		"id": "mark-merge-commits-in-list",
 		"description": "Marks merge commits in commit lists.",
+		"css": true,
 		"screenshot": "https://github-production-user-asset-6210df.s3.amazonaws.com/1402241/285106996-9bdbc938-69c4-4692-8d47-11e30676de62.png"
 	},
 	{
 		"id": "mark-private-orgs",
 		"description": "Marks private organizations on your own profile.",
+		"css": true,
 		"screenshot": "https://user-images.githubusercontent.com/6775216/44633467-d5dcc900-a959-11e8-9116-e6b0ffef66af.png"
 	},
 	{
 		"id": "mobile-tabs",
 		"description": "Makes the tabs more compact on mobile so more of them can be seen.",
+		"css": true,
 		"screenshot": "https://user-images.githubusercontent.com/1402241/245446231-28f44b59-0151-4986-8cb9-05b5645592d8.png"
 	},
 	{
@@ -457,6 +483,7 @@
 	{
 		"id": "more-dropdown-links",
 		"description": "Adds useful links to the repository navigation dropdown",
+		"css": true,
 		"screenshot": "https://user-images.githubusercontent.com/16872793/124681432-856e6f80-de96-11eb-89c9-6d78e8ae4329.png"
 	},
 	{
@@ -487,6 +514,7 @@
 	{
 		"id": "no-unnecessary-split-diff-view",
 		"description": "Always uses unified diffs on files where split diffs aren’t useful.",
+		"css": true,
 		"screenshot": "https://user-images.githubusercontent.com/46634000/121495005-89af8600-c9d9-11eb-822d-77e0b987e3b1.png"
 	},
 	{
@@ -497,6 +525,7 @@
 	{
 		"id": "one-click-pr-or-gist",
 		"description": "Lets you create draft PRs and public gists in one click.",
+		"css": true,
 		"screenshot": "https://user-images.githubusercontent.com/34235681/152473201-868ad7c1-e06f-4826-b808-d90bca7f08b3.png"
 	},
 	{
@@ -517,6 +546,7 @@
 	{
 		"id": "open-all-notifications",
 		"description": "Adds a button to open all your unread notifications at once.",
+		"css": true,
 		"screenshot": "https://github-production-user-asset-6210df.s3.amazonaws.com/140871606/257085496-17e5c6fa-6bad-443d-96d2-d97e73cd1a5e.png"
 	},
 	{
@@ -532,6 +562,7 @@
 	{
 		"id": "parse-backticks",
 		"description": "GitHub renders `<code> </code>text in backticks<code> </code>` in some places but not others; this features fills in where forgotten.",
+		"css": true,
 		"screenshot": "https://github-production-user-asset-6210df.s3.amazonaws.com/83146190/262555091-df31aa17-e7a2-4c16-91ca-fb077ba6134a.png"
 	},
 	{
@@ -547,6 +578,7 @@
 	{
 		"id": "pr-approvals-count",
 		"description": "Shows color-coded review counts in PR lists.",
+		"css": true,
 		"screenshot": "https://github-production-user-asset-6210df.s3.amazonaws.com/83146190/253125143-d10d95df-4a89-4692-b218-5eba5cd79906.png"
 	},
 	{
@@ -607,6 +639,7 @@
 	{
 		"id": "preview-hidden-comments",
 		"description": "Previews hidden comments inline.",
+		"css": true,
 		"screenshot": "https://github-production-user-asset-6210df.s3.amazonaws.com/83146190/262556553-ca04b870-9adb-4a8c-a6d0-6238863948be.png"
 	},
 	{
@@ -647,16 +680,19 @@
 	{
 		"id": "quick-file-edit",
 		"description": "Adds a button to edit files from the repo file list.",
+		"css": true,
 		"screenshot": "https://github-production-user-asset-6210df.s3.amazonaws.com/83146190/252182890-081975f4-f041-4ba5-ae48-d52cb0796543.png"
 	},
 	{
 		"id": "quick-label-removal",
 		"description": "Adds one-click buttons to remove labels in issues/PRs.",
+		"css": true,
 		"screenshot": "https://user-images.githubusercontent.com/36174850/89980178-0bc80480-dc7a-11ea-8ded-9e25f5f13d1a.gif"
 	},
 	{
 		"id": "quick-mention",
 		"description": "Adds a button to <code>@mention</code> a user in issues and PRs.",
+		"css": true,
 		"screenshot": "https://github-production-user-asset-6210df.s3.amazonaws.com/83146190/261158402-5a79cc3e-4331-475f-8063-5ed81fefcf10.gif"
 	},
 	{
@@ -667,6 +703,7 @@
 	{
 		"id": "quick-repo-deletion",
 		"description": "Lets you delete your repos in a click, if they have no stars, issues, or PRs.",
+		"css": true,
 		"screenshot": "https://user-images.githubusercontent.com/1402241/99716945-54a80a00-2a6e-11eb-9107-f3517a6ab1bc.gif"
 	},
 	{
@@ -682,6 +719,7 @@
 	{
 		"id": "reactions-avatars",
 		"description": "Adds reaction avatars showing <em>who</em> reacted to a comment.",
+		"css": true,
 		"screenshot": "https://user-images.githubusercontent.com/1402241/236628453-8b646178-b838-44a3-9541-0a9b5f54a84a.png"
 	},
 	{
@@ -692,6 +730,7 @@
 	{
 		"id": "release-download-count",
 		"description": "Adds a download count next to release assets.",
+		"css": true,
 		"screenshot": "https://user-images.githubusercontent.com/1402241/197958719-1577bc1b-1f4d-44a8-98c2-2645b7b14d31.png"
 	},
 	{
@@ -747,6 +786,7 @@
 	{
 		"id": "scrollable-areas",
 		"description": "Limits the height of tall code blocks and quotes.",
+		"css": true,
 		"screenshot": null
 	},
 	{
@@ -772,11 +812,13 @@
 	{
 		"id": "show-associated-branch-prs-on-fork",
 		"description": "Shows the associated PRs on branches for forked repositories.",
+		"css": true,
 		"screenshot": "https://github-production-user-asset-6210df.s3.amazonaws.com/83146190/260873542-2a7fc7a2-231f-4f2e-9c7e-272d894de4c6.png"
 	},
 	{
 		"id": "show-names",
 		"description": "Adds the real name of users by their usernames.",
+		"css": true,
 		"screenshot": "https://github-production-user-asset-6210df.s3.amazonaws.com/83146190/252756294-94785dc2-423e-498c-939a-359a012036e0.png"
 	},
 	{
@@ -812,16 +854,19 @@
 	{
 		"id": "sticky-conversation-list-toolbar",
 		"description": "Makes the issue/PR list’s filters toolbar sticky.",
+		"css": true,
 		"screenshot": "https://github-production-user-asset-6210df.s3.amazonaws.com/83146190/261164103-875b70f7-5adc-4bb2-b158-8d5231d47da2.gif"
 	},
 	{
 		"id": "sticky-notifications-actions",
 		"description": "Make the notifications action bar sticky.",
+		"css": true,
 		"screenshot": "https://github.com/refined-github/refined-github/assets/1402241/5b370430-2319-4c78-88e7-c2c06cd1c30f"
 	},
 	{
 		"id": "sticky-sidebar",
 		"description": "Makes sidebars sticky in repositories, issues, and PRs, if they fit the viewport.",
+		"css": true,
 		"screenshot": "https://github-production-user-asset-6210df.s3.amazonaws.com/83146190/252179311-ea6d42dc-1525-401a-bc4d-404cf8fa1785.gif"
 	},
 	{
@@ -837,6 +882,7 @@
 	{
 		"id": "suggest-commit-title-limit",
 		"description": "Suggests limiting commit and PR titles to 72 characters.",
+		"css": true,
 		"screenshot": "https://user-images.githubusercontent.com/37769974/60379478-106b3280-9a51-11e9-88b9-0e3607f214cd.gif"
 	},
 	{
@@ -857,11 +903,13 @@
 	{
 		"id": "table-input",
 		"description": "Adds a button in the text editor to quickly insert a simplified HTML table.",
+		"css": true,
 		"screenshot": "https://user-images.githubusercontent.com/46634000/94559114-09892c00-0261-11eb-8fb0-c5a85ea76b6f.gif"
 	},
 	{
 		"id": "tag-changes-link",
 		"description": "Adds a link to changes since last tag/release for each tag/release.",
+		"css": true,
 		"screenshot": "https://github-production-user-asset-6210df.s3.amazonaws.com/140871606/257036739-bebafb94-cb94-4053-9768-ff97306ab862.png"
 	},
 	{
@@ -874,11 +922,6 @@
 		"description": "Adds a shortcut to toggle all similar items (minimized comments, deferred diffs, etc) at once: <kbd>alt</kbd> <kbd>click</kbd> on each button or checkbox.",
 		"screenshot": "https://github-production-user-asset-6210df.s3.amazonaws.com/83146190/253063446-6f556e7d-2ac5-439d-92f0-0c6d719fc86f.gif"
 	},
-	{
-		"id": "toggle-files-button",
-		"description": "Adds a button to toggle the repo file list.",
-		"screenshot": "https://github.com/refined-github/refined-github/assets/1402241/69465a92-5548-40d2-bf90-9f7834d26ef2"
-	},
 	{
 		"id": "unfinished-comments",
 		"description": "Notifies the user of unfinished comments in hidden tabs.",
@@ -907,6 +950,7 @@
 	{
 		"id": "user-local-time",
 		"description": "Shows the user local time in their hovercard (based on their last commit).",
+		"css": true,
 		"screenshot": "https://github-production-user-asset-6210df.s3.amazonaws.com/140871606/257039621-132bd789-e213-4a89-83ff-e1266215c60d.png"
 	},
 	{
@@ -917,6 +961,7 @@
 	{
 		"id": "vertical-front-matter",
 		"description": "Shows Markdown front matter as vertical table.",
+		"css": true,
 		"screenshot": "https://user-images.githubusercontent.com/44045911/87251695-26069b00-c4a0-11ea-9077-53ce366490ed.png"
 	},
 	{
@@ -937,6 +982,7 @@
 	{
 		"id": "warning-for-disallow-edits",
 		"description": "Warns you when unchecking <code>Allow edits from maintainers</code>, as it’s maintainer-hostile.",
+		"css": true,
 		"screenshot": "https://user-images.githubusercontent.com/1402241/53151888-24101380-35ef-11e9-8d30-d6315ad97325.gif"
 	}
 ]
diff --git a/build/__snapshots__/imported-features.json b/build/__snapshots__/imported-features.json
index d9294eed..70e2ae3d 100644
--- a/build/__snapshots__/imported-features.json
+++ b/build/__snapshots__/imported-features.json
@@ -178,7 +178,6 @@
 	"tag-changes-link",
 	"tags-on-commits-list",
 	"toggle-everything-with-alt",
-	"toggle-files-button",
 	"unfinished-comments",
 	"unreleased-commits",
 	"unwrap-unnecessary-dropdowns",
diff --git a/build/prepare-safari-release.sh b/build/prepare-safari-release.sh
index 44b210e8..dec9ec57 100755
--- a/build/prepare-safari-release.sh
+++ b/build/prepare-safari-release.sh
@@ -29,6 +29,7 @@ npx dot-json distribution/manifest.json version "$TAG"
 # For https://github.com/refined-github/refined-github/issues/7629
 # TODO: Drop after https://bugs.webkit.org/show_bug.cgi?id=277588
 npx dot-json distribution/manifest.json background.service_worker --delete
+npx dot-json distribution/manifest.json background.persistent false --json-value
 
 sed -i '' '/MARKETING_VERSION/d' $CONFIG_FILE
 sed -i '' '/CURRENT_PROJECT_VERSION/d' $CONFIG_FILE
diff --git a/build/readme-parser.ts b/build/readme-parser.ts
index 45db894f..6d9244fc 100644
--- a/build/readme-parser.ts
+++ b/build/readme-parser.ts
@@ -1,7 +1,7 @@
 /// <reference types="../source/globals.js" />
 
 import regexJoin from 'regex-join';
-import {readFileSync} from 'node:fs';
+import {existsSync, readFileSync} from 'node:fs';
 import parseMarkdown from 'snarkdown';
 
 // Group names must be unique because they will be merged
@@ -38,6 +38,8 @@ function extractDataFromMatch(match: RegExpMatchArray): FeatureMeta {
 	return {
 		id: simpleId as FeatureID,
 		description: parseMarkdown(linkLessMarkdownDescription),
+		// `undefined` hides the key when CSS is missing
+		css: existsSync(`source/features/${simpleId}.css`) || undefined,
 		// `null` makes the keys visible in the JSON file
 		screenshot: urls.find(url => screenshotRegex.test(url)) ?? null,
 	};
diff --git a/package-lock.json b/package-lock.json
index e08f18eb..bbed6075 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -20,7 +20,7 @@
 				"filter-altered-clicks": "^2.1.1",
 				"fit-textarea": "^2.0.0",
 				"flat-zip": "^1.0.1",
-				"github-url-detection": "^9.0.0",
+				"github-url-detection": "^9.0.1",
 				"indent-textarea": "^4.0.0",
 				"js-abbreviation-number": "^1.4.0",
 				"linkify-issues": "^3.0.1",
@@ -5653,18 +5653,18 @@
 			}
 		},
 		"node_modules/github-reserved-names": {
-			"version": "2.0.6",
-			"resolved": "https://registry.npmjs.org/github-reserved-names/-/github-reserved-names-2.0.6.tgz",
-			"integrity": "sha512-1/vFxGEnhdlU9P3euICRRypN3932B0P1p0+ZRABGTbEtfxNVhUtqr6xyAJK2p5BSDebAZkW4QhT1M2ljTl8MSQ==",
+			"version": "2.0.7",
+			"resolved": "https://registry.npmjs.org/github-reserved-names/-/github-reserved-names-2.0.7.tgz",
+			"integrity": "sha512-0ejh3IuBnGmUxiZFEsIfgRMFOmUe6v9lDyZS8+/8R3/ob8eqC06OJjaNmADy2RspY4X03Dg3LFy42iin3YdUIQ==",
 			"license": "MIT"
 		},
 		"node_modules/github-url-detection": {
-			"version": "9.0.0",
-			"resolved": "https://registry.npmjs.org/github-url-detection/-/github-url-detection-9.0.0.tgz",
-			"integrity": "sha512-NY4Jrysr1R2Z0uJ2LI+Lk+wZ6kUACkZfeQluPGICVk8xAdVhz9iowyY3jDfM75a6Go93laH02eS3FdQxtGDHPw==",
+			"version": "9.0.1",
+			"resolved": "https://registry.npmjs.org/github-url-detection/-/github-url-detection-9.0.1.tgz",
+			"integrity": "sha512-eSgezbnq3HmYOhEHxGc22MJfH64KfgnsoJFuZ741rZR0BWYuBVLmGuj6KUa1nEMVwoevCfHtBEmS4DrJ0figyg==",
 			"license": "MIT",
 			"dependencies": {
-				"github-reserved-names": "^2.0.5"
+				"github-reserved-names": "^2.0.7"
 			},
 			"engines": {
 				"node": ">=20.10"
diff --git a/package.json b/package.json
index d73dcc5c..b62f1715 100644
--- a/package.json
+++ b/package.json
@@ -58,7 +58,7 @@
 		"filter-altered-clicks": "^2.1.1",
 		"fit-textarea": "^2.0.0",
 		"flat-zip": "^1.0.1",
-		"github-url-detection": "^9.0.0",
+		"github-url-detection": "^9.0.1",
 		"indent-textarea": "^4.0.0",
 		"js-abbreviation-number": "^1.4.0",
 		"linkify-issues": "^3.0.1",
diff --git a/readme.md b/readme.md
index 2bf80a64..e20c7c21 100644
--- a/readme.md
+++ b/readme.md
@@ -150,7 +150,6 @@ Thanks for contributing! 🦋🙌
 ### File management
 
 - [](# "download-folder-button") [Adds a button to download entire folders](https://user-images.githubusercontent.com/46634000/158347358-49234bb8-b9e6-41be-92ed-c0c0233cbad2.png), via https://download-directory.github.io.
-- [](# "toggle-files-button") [Adds a button to toggle the repo file list.](https://github.com/refined-github/refined-github/assets/1402241/69465a92-5548-40d2-bf90-9f7834d26ef2)
 - [](# "quick-file-edit") [Adds a button to edit files from the repo file list.](https://github-production-user-asset-6210df.s3.amazonaws.com/83146190/252182890-081975f4-f041-4ba5-ae48-d52cb0796543.png)
 - [](# "repo-wide-file-finder") Enables the File Finder keyboard shortcut (<kbd>t</kbd>) on entire repository.
 - [](# "show-associated-branch-prs-on-fork") [Shows the associated PRs on branches for forked repositories.](https://github-production-user-asset-6210df.s3.amazonaws.com/83146190/260873542-2a7fc7a2-231f-4f2e-9c7e-272d894de4c6.png)
diff --git a/source/features/action-used-by-link.tsx b/source/features/action-used-by-link.tsx
index 79fba5b6..e97eaab1 100644
--- a/source/features/action-used-by-link.tsx
+++ b/source/features/action-used-by-link.tsx
@@ -4,8 +4,9 @@ import SearchIcon from 'octicons-plain-react/Search';
 import * as pageDetect from 'github-url-detection';
 
 import features from '../feature-manager.js';
+import observe from '../helpers/selector-observer.js';
 
-function init(): void {
+function getActionURL(): URL {
 	const actionRepo = $('aside a:has(.octicon-repo)')!
 		.pathname
 		.slice(1);
@@ -18,19 +19,27 @@ function init(): void {
 		o: 'desc',
 	}).toString();
 
-	$('.d-block.mb-2[href^="/contact"]')!.after(
+	return actionURL;
+}
+
+function addUsageLink(side: HTMLElement): void {
+	const actionURL = getActionURL();
+
+	side.after(
 		<a href={actionURL.href} className="d-block mb-2">
 			<SearchIcon width={14} className="color-fg-default mr-2"/>Usage examples
 		</a>,
 	);
 }
 
+function init(signal: AbortSignal): void {
+	observe('.d-block.mb-2[href^="/contact"]', addUsageLink, {signal});
+}
+
 void features.add(import.meta.url, {
 	include: [
 		pageDetect.isMarketplaceAction,
 	],
-	awaitDomReady: true,
-	deduplicate: 'has-rgh',
 	init,
 });
 
diff --git a/source/features/clean-repo-sidebar.css b/source/features/clean-repo-sidebar.css
index c047d61f..33956e39 100644
--- a/source/features/clean-repo-sidebar.css
+++ b/source/features/clean-repo-sidebar.css
@@ -14,11 +14,6 @@
 	margin-top: 0 !important;
 }
 
-/* Hide "Readme" link made unnecessary by `toggle-files-button` #3580 */
-[rgh-clean-repo-sidebar] .Layout-sidebar [href$='#readme'] {
-	display: none;
-}
-
 @media (min-width: 768px) {
 	/* Hide "Releases" header */
 	[rgh-clean-repo-sidebar] .Layout-sidebar h2 [href$='/releases'] {
diff --git a/source/features/comment-excess.tsx b/source/features/comment-excess.tsx
index a8250af4..9c58cc2a 100644
--- a/source/features/comment-excess.tsx
+++ b/source/features/comment-excess.tsx
@@ -28,9 +28,9 @@ function scrollOnSearch(event: KeyboardEvent): void {
 
 function addIndicator(headerCommentCount: HTMLSpanElement): void {
 	const loadMoreButton = expectElement(paginationButtonSelector);
-	assertNodeContent(headerCommentCount, /^\d+ comments$/);
+	assertNodeContent(headerCommentCount, /^\d+ comment(s)?$/);
 	assertNodeContent(loadMoreButton, /^\d+ hidden items$/);
-	const spacer = new Text(' • ');
+	const spacer = new Text(' · ');
 	const link = (
 		<a
 			className="Link--muted"
diff --git a/source/features/default-branch-button.css b/source/features/default-branch-button.css
deleted file mode 100644
index 3a3f4bed..00000000
--- a/source/features/default-branch-button.css
+++ /dev/null
@@ -1,4 +0,0 @@
-/* Reduce excessive width on long branch names, this is also a GitHub bug */
-.rgh-default-branch-button-group [data-component='text'] {
-	max-width: 80px;
-}
diff --git a/source/features/default-branch-button.tsx b/source/features/default-branch-button.tsx
index d9f0673b..6e7ce67e 100644
--- a/source/features/default-branch-button.tsx
+++ b/source/features/default-branch-button.tsx
@@ -1,4 +1,3 @@
-import './default-branch-button.css';
 import React from 'dom-chef';
 import * as pageDetect from 'github-url-detection';
 import ChevronLeftIcon from 'octicons-plain-react/ChevronLeft';
@@ -101,5 +100,6 @@ Test URLs:
 - isRepoCommitList: https://github.com/refined-github/refined-github/commits/07ecc75/
 - isRepoCommitList (already on default branch): https://github.com/typed-ember/ember-cli-typescript/commits/master
 - isRepoCommitListRoot (no branch selector): https://github.com/refined-github/refined-github/commits/07ecc75/extension
+- `isRepoHome`, long branch name: https://github.com/refined-github/sandbox/tree/very-very-long-long-long-long-branch-name
 
 */
diff --git a/source/features/extend-conversation-status-filters.tsx b/source/features/extend-conversation-status-filters.tsx
index daca9cbf..cb715400 100644
--- a/source/features/extend-conversation-status-filters.tsx
+++ b/source/features/extend-conversation-status-filters.tsx
@@ -1,80 +1,74 @@
 import React from 'dom-chef';
-import {$, $$} from 'select-dom';
+import {$} from 'select-dom';
 import CheckIcon from 'octicons-plain-react/Check';
-import elementReady from 'element-ready';
 import * as pageDetect from 'github-url-detection';
 
 import features from '../feature-manager.js';
 import SearchQuery from '../github-helpers/search-query.js';
+import observe from '../helpers/selector-observer.js';
 
-function addMergeLink(): void {
+function addMergeLink(lastLink: HTMLAnchorElement): void {
+	// It's shouldn't be added in issues list
 	if (!pageDetect.isPRList()) {
 		return;
 	}
 
+	const locationQuery = SearchQuery.from(location);
+	const isMerged = locationQuery.includes('is:merged');
+	const isUnmerged = locationQuery.includes('is:unmerged');
+
 	// The links in `.table-list-header-toggle` are either:
 	//   1 Open | 1 Closed
 	//   1 Total            // Apparently appears with is:merged/is:unmerged
-	for (const lastLink of $$('.table-list-header-toggle.states a:last-child')) {
-		const lastLinkQuery = SearchQuery.from(lastLink);
+	if (isMerged) {
+		// It's a "Total" link for "is:merged"
+		lastLink.lastChild!.textContent = lastLink.lastChild!.textContent.replace('Total', 'Merged');
+		return;
+	}
 
-		if (lastLinkQuery.includes('is:merged')) {
-			// It's a "Total" link for "is:merged"
-			lastLink.lastChild!.textContent = lastLink.lastChild!.textContent.replace('Total', 'Merged');
-			continue;
-		}
+	if (isUnmerged) {
+		// It's a "Total" link for "is:unmerged"
+		lastLink.lastChild!.textContent = lastLink.lastChild!.textContent.replace('Total', 'Unmerged');
+		return;
+	}
 
-		if (lastLinkQuery.includes('is:unmerged')) {
-			// It's a "Total" link for "is:unmerged"
-			lastLink.lastChild!.textContent = lastLink.lastChild!.textContent.replace('Total', 'Unmerged');
-			continue;
-		}
+	// In this case, `lastLink` is expected to be a "Closed" link
+	const mergeLink = lastLink.cloneNode(true);
+	mergeLink.textContent = 'Merged';
+	mergeLink.classList.toggle('selected', isMerged);
+	mergeLink.href = SearchQuery.from(mergeLink).replace('is:closed', 'is:merged').href;
+	lastLink.after(' ', mergeLink);
+}
 
-		// In this case, `lastLink` is expected to be a "Closed" link
-		const mergeLink = lastLink.cloneNode(true);
-		mergeLink.textContent = 'Merged';
-		mergeLink.classList.toggle('selected', SearchQuery.from(location).includes('is:merged'));
-		mergeLink.href = SearchQuery.from(mergeLink).replace('is:closed', 'is:merged').href;
-		lastLink.after(' ', mergeLink);
+function removeAllFilters(link: HTMLAnchorElement): void {
+	if (link === link.parentElement!.lastElementChild) {
+		addMergeLink(link);
 	}
-}
 
-function togglableFilters(): void {
-	for (const link of $$('.table-list-header-toggle.states a')) {
-		$('.octicon', link)?.remove();
-		if (link.classList.contains('selected')) {
-			link.prepend(<CheckIcon/>);
-			link.href = SearchQuery
-				.from(link)
-				.remove(
-					'is:open',
-					'is:closed',
-					'is:merged',
-					'is:unmerged',
-				)
-				.href;
-		}
+	$('.octicon', link)?.remove();
+	if (link.classList.contains('selected')) {
+		link.prepend(<CheckIcon/>);
+		link.href = SearchQuery
+			.from(link)
+			.remove(
+				'is:open',
+				'is:closed',
+				'is:merged',
+				'is:unmerged',
+			)
+			.href;
 	}
 }
 
-async function init(): Promise<void | false> {
-	await elementReady('.table-list-filters');
-
-	addMergeLink();
-	togglableFilters();
+function init(signal: AbortSignal): void {
+	observe('.table-list-header-toggle.states a', removeAllFilters, {signal});
 }
 
 void features.add(import.meta.url, {
 	include: [
 		pageDetect.isRepoIssueOrPRList,
-	],
-	deduplicate: 'has-rgh-inner',
-	init,
-}, {
-	include: [
 		pageDetect.isGlobalIssueOrPRList,
 	],
-	deduplicate: 'has-rgh',
 	init,
 });
 
diff --git a/source/features/quick-review.tsx b/source/features/quick-review.tsx
index 0711915d..8944a8f9 100644
--- a/source/features/quick-review.tsx
+++ b/source/features/quick-review.tsx
@@ -13,7 +13,7 @@ import {
 	getConversationNumber, getUsername, scrollIntoViewIfNeeded, triggerConversationUpdate,
 } from '../github-helpers/index.js';
 import {randomArrayItem} from '../helpers/math.js';
-import {getToken} from '../github-helpers/github-token.js';
+import {getToken} from '../options-storage.js';
 
 const emojis = [...'🚀🐿️⚡️🤌🥳🥰🤩🥸😎🤯🚢🛫🏳️🏁'];
 
diff --git a/source/features/rgh-feature-descriptions.tsx b/source/features/rgh-feature-descriptions.tsx
index 67becdda..5ae64889 100644
--- a/source/features/rgh-feature-descriptions.tsx
+++ b/source/features/rgh-feature-descriptions.tsx
@@ -63,7 +63,9 @@ function addDescription(infoBanner: HTMLElement, id: string, meta: FeatureMeta |
 						{
 							meta && isCss
 								? <> • <a data-turbo-frame="repo-content-turbo-frame" href={location.pathname.replace('.css', '.tsx')}>See .tsx file</a></>
-								: undefined
+								: meta?.css
+									? <> • <a data-turbo-frame="repo-content-turbo-frame" href={location.pathname.replace('.tsx', '.css')}>See .css file</a></>
+									: undefined
 						}
 					</div>
 				</div>
diff --git a/source/features/rgh-improve-new-issue-form.tsx b/source/features/rgh-improve-new-issue-form.tsx
index d9ab723d..af8983d8 100644
--- a/source/features/rgh-improve-new-issue-form.tsx
+++ b/source/features/rgh-improve-new-issue-form.tsx
@@ -1,13 +1,18 @@
 import React from 'dom-chef';
 import {$} from 'select-dom';
+import delegate, {DelegateEvent} from 'delegate-it';
 import * as pageDetect from 'github-url-detection';
 
 import features from '../feature-manager.js';
 import openOptions from '../helpers/open-options.js';
 import clearCacheHandler from '../helpers/clear-cache-handler.js';
-import {expectTokenScope, getToken} from '../github-helpers/github-token.js';
+import {expectTokenScope} from '../github-helpers/github-token.js';
+import {getToken} from '../options-storage.js';
 import {isRefinedGitHubRepo} from '../github-helpers/index.js';
 
+const isSetTheTokenSelector = 'input[name^="issue_form[token]"]';
+const liesGif = 'https://github.com/user-attachments/assets/f417264f-f230-4156-b020-16e4390562bd';
+
 function addNotice(adjective: JSX.Element | string): void {
 	$('#issue_body_template_name')!.before(
 		<div className="flash flash-error h3 my-9" style={{animation: 'pulse-in 0.3s 2'}}>
@@ -34,7 +39,7 @@ async function checkToken(): Promise<void> {
 	}
 
 	// Thank you for following the instructions. I'll save you a click.
-	$('input[name^="issue_form[token]')!.checked = true;
+	$(isSetTheTokenSelector)!.checked = true;
 }
 
 async function setVersion(): Promise<void> {
@@ -60,6 +65,33 @@ async function linkifyCacheRefresh(): Promise<void> {
 	);
 }
 
+function Lies(): JSX.Element {
+	return (
+		<a href="https://www.youtube.com/watch?v=YWdD206eSv0">
+			<img src={liesGif} alt="Just go on the internet and tell lies?" className="d-inline-block"/>
+		</a>
+	);
+}
+
+async function lieDetector({delegateTarget}: DelegateEvent<MouseEvent, HTMLInputElement>): Promise<void> {
+	if (delegateTarget.checked) {
+		delegateTarget.closest('fieldset')!.append(<Lies/>);
+	}
+}
+
+async function validateTokenCheckbox(): Promise<void> {
+	if (await getToken()) {
+		return;
+	}
+
+	// eslint-disable-next-line new-cap -- Preload image
+	Lies();
+
+	delegate(isSetTheTokenSelector, 'click', lieDetector, {
+		once: true,
+	});
+}
+
 void features.add(import.meta.url, {
 	asLongAs: [
 		isRefinedGitHubRepo,
@@ -71,6 +103,7 @@ void features.add(import.meta.url, {
 	init: [
 		linkifyCacheRefresh,
 		checkToken,
+		validateTokenCheckbox,
 		setVersion,
 	],
 });
diff --git a/source/features/select-notifications.tsx b/source/features/select-notifications.tsx
index 41903421..ce557b92 100644
--- a/source/features/select-notifications.tsx
+++ b/source/features/select-notifications.tsx
@@ -131,9 +131,10 @@ const createDropdown = onetime(() => (
 		onToggle={resetFilters}
 	>
 		<summary
-			className="h6" // Match "Select all" style
-			data-hotkey="S"
+			className="h6 tooltipped tooltipped-s" // `h6` matches "Select all" style
+			data-hotkey="Shift+S"
 			aria-haspopup="menu"
+			aria-label="Hotkey: Shift+S"
 			role="button"
 		>
 			Select by <span className="dropdown-caret ml-1"/>
@@ -172,7 +173,7 @@ function init(signal: AbortSignal): void {
 
 void features.add(import.meta.url, {
 	shortcuts: {
-		S: 'Open the "Select by" dropdown',
+		'shift s': 'Open the "Select by" dropdown',
 	},
 	include: [
 		pageDetect.isNotifications,
diff --git a/source/features/suggest-commit-title-limit.tsx b/source/features/suggest-commit-title-limit.tsx
index 7887eafc..d163464e 100644
--- a/source/features/suggest-commit-title-limit.tsx
+++ b/source/features/suggest-commit-title-limit.tsx
@@ -4,6 +4,8 @@ import * as pageDetect from 'github-url-detection';
 
 import features from '../feature-manager.js';
 import onCommitTitleUpdate from '../github-events/on-commit-title-update.js';
+import getNextConversationNumber from '../github-helpers/get-next-conversation-number.js';
+import {getConversationNumber} from '../github-helpers/index.js';
 import {formatPrCommitTitle} from './sync-pr-commit-title.js';
 
 // https://github.com/refined-github/refined-github/issues/2178#issuecomment-505940703
@@ -13,9 +15,13 @@ function validateCommitTitle({delegateTarget: field}: DelegateEvent<Event, HTMLI
 	field.classList.toggle('rgh-title-over-limit', field.value.length > limit);
 }
 
-function validatePrTitle({delegateTarget: field}: DelegateEvent<Event, HTMLInputElement>): void {
+async function validatePrTitle({delegateTarget: field}: DelegateEvent<Event, HTMLInputElement>): Promise<void> {
 	// Include the PR number in the title length calculation because it will be added to the commit title
-	field.classList.toggle('rgh-title-over-limit', formatPrCommitTitle(field.value).length > limit);
+	const prTitle = formatPrCommitTitle(
+		field.value,
+		getConversationNumber() ?? await getNextConversationNumber(),
+	);
+	field.classList.toggle('rgh-title-over-limit', prTitle.length > limit);
 }
 
 function init(signal: AbortSignal): void {
diff --git a/source/features/sync-pr-commit-title.tsx b/source/features/sync-pr-commit-title.tsx
index bfc32f0b..3877a4c6 100644
--- a/source/features/sync-pr-commit-title.tsx
+++ b/source/features/sync-pr-commit-title.tsx
@@ -21,8 +21,8 @@ function getCurrentCommitTitle(): string | undefined {
 	return getCurrentCommitTitleField()?.value.trim();
 }
 
-export function formatPrCommitTitle(title: string): string {
-	return `${title} (#${getConversationNumber()!})`;
+export function formatPrCommitTitle(title: string, prNumber = getConversationNumber()!): string {
+	return `${title} (#${prNumber})`;
 }
 
 function createCommitTitle(): string {
diff --git a/source/features/tag-changes-link.tsx b/source/features/tag-changes-link.tsx
index bbb71b4c..2d0e2b1f 100644
--- a/source/features/tag-changes-link.tsx
+++ b/source/features/tag-changes-link.tsx
@@ -1,6 +1,8 @@
 import './tag-changes-link.css';
 import React from 'dom-chef';
-import {$, $$, elementExists} from 'select-dom';
+import {
+	$, $$, elementExists, expectElement,
+} from 'select-dom';
 import domLoaded from 'dom-loaded';
 import DiffIcon from 'octicons-plain-react/Diff';
 import * as pageDetect from 'github-url-detection';
@@ -33,8 +35,9 @@ async function getNextPage(): Promise<DocumentFragment> {
 }
 
 function parseTags(element: HTMLElement): TagDetails {
-	// Safari doesn't correctly parse links if they're loaded via AJAX #3899
-	const {pathname: tagUrl} = new URL($(['a[href*="/tree/"]', 'a[href*="/tag/"]'], element)!.href);
+	// DO NOT change this to `pathname` because it's empty when element is from `getNextPage` function
+	// https://github.com/refined-github/refined-github/pull/7726#discussion_r1727135015
+	const tagUrl = expectElement(['a[href*="/tree/"]', 'a[href*="/tag/"]'], element).href;
 	const tag = /\/(?:releases\/tag|tree)\/(.*)/.exec(tagUrl)![1];
 
 	return {
@@ -84,9 +87,9 @@ async function init(): Promise<void> {
 		'.Box-body .border-md-bottom',
 	];
 
+	await domLoaded;
 	// Look for tags in the current page and the next page
 	const pages = [document, await getNextPage()];
-	await domLoaded;
 	const allTags = $$(tagsSelector, pages).map(tag => parseTags(tag));
 
 	for (const [index, container] of allTags.entries()) {
diff --git a/source/features/toggle-files-button.css b/source/features/toggle-files-button.css
deleted file mode 100644
index a27181b5..00000000
--- a/source/features/toggle-files-button.css
+++ /dev/null
@@ -1,17 +0,0 @@
-:root .rgh-toggle-files {
-	margin: -10px;
-	padding: 10px 13px;
-}
-
-body:not(.rgh-files-hidden) .rgh-toggle-files .octicon-chevron-up,
-body.rgh-files-hidden .rgh-toggle-files .octicon-chevron-down {
-	display: none !important;
-}
-
-.rgh-files-hidden .react-directory-row {
-	display: none;
-}
-
-.rgh-files-hidden .bgColor-muted.p-1.rounded-top-2 {
-	border-radius: var(--borderRadius-medium, 6px);
-}
diff --git a/source/features/toggle-files-button.tsx b/source/features/toggle-files-button.tsx
deleted file mode 100644
index 97bef366..00000000
--- a/source/features/toggle-files-button.tsx
+++ /dev/null
@@ -1,103 +0,0 @@
-import './toggle-files-button.css';
-import React from 'dom-chef';
-import {$, expectElement} from 'select-dom';
-import delegate from 'delegate-it';
-import {CachedFunction} from 'webext-storage-cache';
-import * as pageDetect from 'github-url-detection';
-import ChevronDownIcon from 'octicons-plain-react/ChevronDown';
-import ChevronUpIcon from 'octicons-plain-react/ChevronUp';
-import ArrowUpIcon from 'octicons-plain-react/ArrowUp';
-
-import features from '../feature-manager.js';
-import attachElement from '../helpers/attach-element.js';
-import observe from '../helpers/selector-observer.js';
-import {cacheByRepo} from '../github-helpers/index.js';
-
-const wasHidden = new CachedFunction('toggle-files-button', {
-	async updater(): Promise<boolean> {
-		return false;
-	},
-	cacheKey: cacheByRepo,
-});
-const hiddenFilesClass = 'rgh-files-hidden';
-const toggleButtonClass = 'rgh-toggle-files';
-const noticeClass = 'rgh-files-hidden-notice';
-
-// 19px align this icon with the <ChevronUpIcon/> above it
-const noticeStyle = {paddingRight: '16px'};
-
-function addButton(commitsLink: HTMLElement): void {
-	// It won't work with :has(), too many nested boxes
-	commitsLink.closest('[class^="Box"]')!.append(
-		<button
-			type="button"
-			className={`btn-octicon ${toggleButtonClass}`}
-			aria-label="Toggle files section"
-		>
-			<ChevronDownIcon/>
-			<ChevronUpIcon/>
-		</button>,
-	);
-}
-
-function addFilesHiddenNotice(): void {
-	// Add notice so the user knows that the list was collapsed #5524
-	attachElement(expectElement('[aria-labelledby="folders-and-files"]'), {
-		className: noticeClass,
-		after: () => (
-			<div
-				className="py-1 text-right text-small color-fg-subtle"
-				style={noticeStyle}
-			>
-				The file list was collapsed via Refined GitHub <ArrowUpIcon className="v-align-middle"/>
-			</div>
-		),
-	});
-}
-
-function toggle(toggle?: boolean): boolean {
-	return document.body.classList.toggle(hiddenFilesClass, toggle);
-}
-
-async function toggleHandler(): Promise<void> {
-	// Remove notice after the first click
-	$(`.${noticeClass}`)?.remove();
-
-	// eslint-disable-next-line unicorn/prefer-ternary -- No.
-	if (toggle()) {
-		await wasHidden.applyOverride([], true);
-	} else {
-		await wasHidden.delete();
-	}
-}
-
-async function updateView(button: HTMLElement): Promise<void> {
-	addButton(button);
-	if (await wasHidden.getCached()) {
-		toggle(true);
-		addFilesHiddenNotice();
-	}
-}
-
-async function init(signal: AbortSignal): Promise<void> {
-	// Ensure that commit details are loaded
-	observe('[data-testid="latest-commit-details"] span', updateView, {signal});
-	delegate(`.${toggleButtonClass}, .${noticeClass}`, 'click', toggleHandler, {signal});
-}
-
-void features.add(import.meta.url, {
-	include: [
-		pageDetect.isRepoTree,
-	],
-	init,
-});
-
-/*
-
-Test URLs
-
-https://github.com/refined-github/refined-github
-
-https://github.com/refined-github/sandbox/tree/other-branch
-
-*/
diff --git a/source/features/warn-pr-from-master.tsx b/source/features/warn-pr-from-master.tsx
index dd650260..25b0f804 100644
--- a/source/features/warn-pr-from-master.tsx
+++ b/source/features/warn-pr-from-master.tsx
@@ -1,41 +1,32 @@
 import React from 'dom-chef';
-import {$, elementExists} from 'select-dom';
 import * as pageDetect from 'github-url-detection';
+import elementReady from 'element-ready';
 
 import features from '../feature-manager.js';
-import getDefaultBranch, {defaultBranchOfRepo} from '../github-helpers/get-default-branch.js';
-import {getRepo} from '../github-helpers/index.js';
-
-async function init(): Promise<false | void> {
-	let defaultBranch;
-	if (elementExists('.is-cross-repo')) {
-		const forkedRepository = getRepo($('[title^="head: "]')!.textContent)!;
-		defaultBranch = await defaultBranchOfRepo.get(forkedRepository);
-	} else {
-		defaultBranch = await getDefaultBranch();
-	}
-
-	// Expected: /user/repo/compare/master...user:master
-	if (!location.pathname.endsWith(':' + defaultBranch)) {
-		return false;
-	}
-
-	$('.js-compare-pr')!.before(
+import parseCompareUrl from '../github-helpers/parse-compare-url.js';
+import {defaultBranchOfRepo} from '../github-helpers/get-default-branch.js';
+
+async function init(): Promise<void> {
+	const anchor = await elementReady('.js-compare-pr');
+	anchor?.before(
 		<div className="flash flash-error my-3">
 			<strong>Note:</strong> Creating a PR from the default branch is an <a href="https://blog.jasonmeridth.com/posts/do-not-issue-pull-requests-from-your-master-branch/" target="_blank" rel="noopener noreferrer">anti-pattern</a>.
 		</div>,
 	);
 }
 
+async function isCrossRepoCompareFromMaster(): Promise<boolean> {
+	const c = parseCompareUrl(location.pathname);
+
+	// eslint-disable-next-line no-implicit-coercion -- TS preference
+	return !!c && c.isCrossRepo && c.head.branch === await defaultBranchOfRepo.get(c.head.repo);
+}
+
 void features.add(import.meta.url, {
-	include: [
+	asLongAs: [
 		pageDetect.isCompare,
+		isCrossRepoCompareFromMaster,
 	],
-	exclude: [
-		pageDetect.isBlank,
-	],
-	awaitDomReady: true,
-	deduplicate: 'has-rgh',
 	init,
 });
 
diff --git a/source/github-helpers/api.ts b/source/github-helpers/api.ts
index 2c233e08..1fa16331 100644
--- a/source/github-helpers/api.ts
+++ b/source/github-helpers/api.ts
@@ -31,7 +31,7 @@ import {JsonObject, AsyncReturnType} from 'type-fest';
 
 import features from '../feature-manager.js';
 import {getRepo} from './index.js';
-import {getToken} from './github-token.js';
+import {getToken} from '../options-storage.js';
 
 type JsonError = {
 	message: string;
diff --git a/source/github-helpers/get-default-branch.ts b/source/github-helpers/get-default-branch.ts
index 64cfe29d..6a5ef341 100644
--- a/source/github-helpers/get-default-branch.ts
+++ b/source/github-helpers/get-default-branch.ts
@@ -1,13 +1,13 @@
 import {CachedFunction} from 'webext-storage-cache';
 import elementReady from 'element-ready';
-import {type RepositoryInfo} from 'github-url-detection';
+import type {NameWithOwner} from 'github-url-detection';
 
 import api from './api.js';
 import {extractCurrentBranchFromBranchPicker, getRepo} from './index.js';
 import {branchSelector} from './selectors.js';
 import GetDefaultBranch from './get-default-branch.gql';
 
-const isCurrentRepo = ({nameWithOwner}: RepositoryInfo): boolean => Boolean(getRepo()?.nameWithOwner === nameWithOwner);
+const isCurrentRepo = (nameWithOwner: NameWithOwner): boolean => Boolean(getRepo()?.nameWithOwner === nameWithOwner);
 
 // Do not make this function complicated. We're only optimizing for the repo root.
 async function fromDOM(): Promise<string | undefined> {
@@ -27,11 +27,12 @@ async function fromDOM(): Promise<string | undefined> {
 	return extractCurrentBranchFromBranchPicker(element);
 }
 
-async function fromAPI(repository: RepositoryInfo): Promise<string> {
+async function fromAPI(repository: NameWithOwner): Promise<string> {
+	const [owner, name] = repository.split('/');
 	const response = await api.v4(GetDefaultBranch, {
 		variables: {
-			owner: repository.owner,
-			name: repository.name,
+			owner,
+			name,
 		},
 	});
 
@@ -41,7 +42,7 @@ async function fromAPI(repository: RepositoryInfo): Promise<string> {
 // DO NOT use optional arguments/defaults in "cached functions" because they can't be memoized effectively
 // https://github.com/sindresorhus/eslint-plugin-unicorn/issues/1864
 export const defaultBranchOfRepo = new CachedFunction('default-branch', {
-	async updater(repository: RepositoryInfo): Promise<string> {
+	async updater(repository: NameWithOwner): Promise<string> {
 		if (!repository) {
 			throw new Error('getDefaultBranch was called on a non-repository page');
 		}
@@ -52,10 +53,9 @@ export const defaultBranchOfRepo = new CachedFunction('default-branch', {
 
 	maxAge: {hours: 1},
 	staleWhileRevalidate: {days: 20},
-	cacheKey: ([repository]) => repository.nameWithOwner,
 },
 );
 
 export default async function getDefaultBranch(): Promise<string> {
-	return defaultBranchOfRepo.get(getRepo()!);
+	return defaultBranchOfRepo.get(getRepo()!.nameWithOwner);
 }
diff --git a/source/github-helpers/get-next-conversation-number.ts b/source/github-helpers/get-next-conversation-number.ts
new file mode 100644
index 00000000..fb4adebe
--- /dev/null
+++ b/source/github-helpers/get-next-conversation-number.ts
@@ -0,0 +1,6 @@
+import api from './api.js';
+
+export default async function getNextConversationNumber(): Promise<number> {
+	const issues = await api.v3('issues?per_page=1');
+	return (issues[0].number as number) + 1;
+}
diff --git a/source/github-helpers/github-token.ts b/source/github-helpers/github-token.ts
index ece6a3e8..d77c75e0 100644
--- a/source/github-helpers/github-token.ts
+++ b/source/github-helpers/github-token.ts
@@ -1,12 +1,5 @@
 import {v3} from './api.js';
-import optionsStorage from '../options-storage.js';
-
-const settings = optionsStorage.getAll();
-
-export async function getToken(): Promise<string | undefined> {
-	const {personalToken} = await settings;
-	return personalToken;
-}
+import {getToken} from '../options-storage.js';
 
 export async function expectToken(): Promise<string> {
 	const personalToken = await getToken();
diff --git a/source/github-helpers/parse-compare-url.ts b/source/github-helpers/parse-compare-url.ts
new file mode 100644
index 00000000..a31b13e5
--- /dev/null
+++ b/source/github-helpers/parse-compare-url.ts
@@ -0,0 +1,49 @@
+import type {NameWithOwner} from 'github-url-detection';
+
+import {getRepo} from './index.js';
+
+type Comparison = {
+	head: {
+		repo: NameWithOwner;
+		branch: string;
+	};
+	base: {
+		repo: NameWithOwner;
+		branch: string;
+	};
+	isCrossRepo: boolean;
+};
+
+const compareRegex = /compare[/]([^.]+)(?:[.][.][.]?)(.+)/;
+export default function parseCompareUrl(pathname: string): Comparison | undefined {
+	const base = getRepo(pathname)!;
+
+	const [, baseBranch, heads] = compareRegex.exec(base.path) ?? [];
+	if (!baseBranch) {
+		return;
+	}
+
+	const headParts = heads.split(':');
+	const headBranch = headParts.pop()!; // Branch is always last, or the only one
+	const headOwner = headParts.shift() ?? base.owner; // The owner is first, or it's the same as the base
+	const headName = headParts.pop() ?? base.name; // The repo is first or middle, or it's the same as the base
+
+	if (headParts.length > 0) {
+		throw new Error('Invalid compare URL format');
+	}
+
+	const headRepo: NameWithOwner = `${headOwner}/${headName}`;
+
+	return {
+		base: {
+			repo: base.nameWithOwner,
+			branch: baseBranch,
+		},
+		head: {
+			repo: headRepo,
+			branch: headBranch,
+		},
+		isCrossRepo: headRepo !== base.nameWithOwner,
+	};
+}
+
diff --git a/source/globals.d.ts b/source/globals.d.ts
index de7c4ee6..780ee8cf 100644
--- a/source/globals.d.ts
+++ b/source/globals.d.ts
@@ -11,6 +11,7 @@ interface FeatureMeta {
 	id: FeatureID;
 	description: string;
 	screenshot: string | null; // eslint-disable-line @typescript-eslint/ban-types -- We use `null` in the JSON file
+	css?: true;
 }
 
 // These types are unnecessarily loose
diff --git a/source/manifest.json b/source/manifest.json
index b487a80d..3c293cdf 100644
--- a/source/manifest.json
+++ b/source/manifest.json
@@ -9,11 +9,11 @@
 	"browser_specific_settings": {
 		"gecko": {
 			"id": "{a4c4eda4-fb84-4a84-b4a1-f7c1cbf2a1ad}",
-			"strict_min_version": "124.0"
+			"strict_min_version": "126.0"
 		},
 		"gecko_android": {
 			"id": "{a4c4eda4-fb84-4a84-b4a1-f7c1cbf2a1ad}",
-			"strict_min_version": "124.0"
+			"strict_min_version": "126.0"
 		}
 	},
 	"permissions": [
diff --git a/source/options-storage.ts b/source/options-storage.ts
index eb5e7a2d..30ba6c24 100644
--- a/source/options-storage.ts
+++ b/source/options-storage.ts
@@ -32,4 +32,12 @@ const migrations = [
 ];
 
 export const perDomainOptions = new OptionsSyncPerDomain({defaults, migrations});
-export default perDomainOptions.getOptionsForOrigin();
+const optionsStorage = perDomainOptions.getOptionsForOrigin();
+export default optionsStorage;
+
+const cachedSettings = optionsStorage.getAll();
+
+export async function getToken(): Promise<string | undefined> {
+	const {personalToken} = await cachedSettings;
+	return personalToken;
+}
diff --git a/source/refined-github.ts b/source/refined-github.ts
index 689ed051..b156d884 100644
--- a/source/refined-github.ts
+++ b/source/refined-github.ts
@@ -59,7 +59,6 @@ import './features/pinned-issues-update-time.js';
 import './features/default-branch-button.js';
 import './features/one-click-diff-options.js';
 import './features/ci-link.js';
-import './features/toggle-files-button.js';
 import './features/sync-pr-commit-title.js';
 import './features/hide-inactive-deployments.js';
 import './features/pull-request-hotkeys.js';


Test patch:
diff --git a/source/github-helpers/parse-compare-url.test.ts b/source/github-helpers/parse-compare-url.test.ts
new file mode 100644
index 00000000..0c5ee60f
--- /dev/null
+++ b/source/github-helpers/parse-compare-url.test.ts
@@ -0,0 +1,37 @@
+import {expect, test} from 'vitest';
+
+import parseCompareUrl from './parse-compare-url.js';
+
+const base = {
+	branch: 'main',
+	repo: 'john/recipes',
+};
+
+test('parseCompareUrl', () => {
+	expect(parseCompareUrl('/john/recipes/compare')).toBe(undefined);
+	expect(parseCompareUrl('/john/recipes/compare/main')).toBe(undefined);
+	expect(parseCompareUrl('/john/recipes/compare/main...patch-1')).toMatchObject({
+		base,
+		head: {
+			branch: 'patch-1',
+			repo: 'john/recipes',
+		},
+		isCrossRepo: false,
+	});
+	expect(parseCompareUrl('/john/recipes/compare/main...patty:patch-1')).toMatchObject({
+		base,
+		head: {
+			branch: 'patch-1',
+			repo: 'patty/recipes',
+		},
+		isCrossRepo: true,
+	});
+	expect(parseCompareUrl('/john/recipes/compare/main...maria:ricette:pizza')).toMatchObject({
+		base,
+		head: {
+			branch: 'pizza',
+			repo: 'maria/ricette',
+		},
+		isCrossRepo: true,
+	});
+});