Repository: refined-github/refined-github
Pull request #8208 (2025-01-09 20:24:54)

GitHub bug fix: remove bad spacing in notifications header
Closes  #8203




## Test URLs
https://github.com/notifications

## Screenshot
**Before**
![grafik](https://github.com/user-attachments/assets/6f363ef6-b596-462e-aded-b7d0a1ebc86a)

**After**
|   |   |
| - | - |
| ![grafik](https://github.com/user-attachments/assets/62537538-6d7b-4fe4-95c0-c00db1d57555) | ![grafik](https://github.com/user-attachments/assets/1b03aa77-c964-422e-92c7-26d8ff30ef2b) |


Patch:
diff --git a/build/__snapshots__/features-meta.json b/build/__snapshots__/features-meta.json
index 57e41ff8..625d0abe 100644
--- a/build/__snapshots__/features-meta.json
+++ b/build/__snapshots__/features-meta.json
@@ -807,7 +807,6 @@
 	{
 		"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"
 	},
 	{
diff --git a/source/features/conversation-activity-filter.tsx b/source/features/conversation-activity-filter.tsx
index 0823395a..a6835b21 100644
--- a/source/features/conversation-activity-filter.tsx
+++ b/source/features/conversation-activity-filter.tsx
@@ -161,10 +161,10 @@ async function addWidget(state: State, anchor: HTMLElement): Promise<void> {
 			className={`details-reset details-overlay d-inline-block ml-2 position-relative ${dropdownClass}`}
 			id="rgh-conversation-activity-filter-select-menu"
 		>
-			<summary>
-				<EyeIcon className="color-fg-muted" />
+			<summary className="height-full color-fg-muted">
+				<EyeIcon />
 				<EyeClosedIcon className="color-fg-danger" />
-				<span className="text-small color-fg-danger v-align-text-bottom rgh-conversation-events-label"> events</span>
+				<span className="text-small color-fg-danger v-align-text-bottom rgh-conversation-events-label ml-1">events</span>
 				<div className="dropdown-caret ml-1" />
 			</summary>
 			<details-menu
diff --git a/source/features/esc-to-deselect-line.tsx b/source/features/esc-to-deselect-line.tsx
index eaf208b2..fb912095 100644
--- a/source/features/esc-to-deselect-line.tsx
+++ b/source/features/esc-to-deselect-line.tsx
@@ -1,3 +1,4 @@
+import {$optional} from 'select-dom/strict.js';
 import * as pageDetect from 'github-url-detection';
 
 import features from '../feature-manager.js';
@@ -13,7 +14,23 @@ function isLineSelected(): boolean {
 
 function listener({key, target}: KeyboardEvent): void {
 	if (key === 'Escape' && isLineSelected() && !isEditable(target)) {
-		location.hash = '#no-line'; // Update UI, without `scroll-to-top` behavior
+		const selectedLineNumber = $optional('.react-line-number.highlighted-line');
+
+		if (selectedLineNumber) {
+			// Save and remove line number
+			const {lineNumber} = selectedLineNumber.dataset;
+			selectedLineNumber.dataset.lineNumber = '';
+			// Trigger click to deselect
+			selectedLineNumber.dispatchEvent(new MouseEvent('mousedown', {bubbles: true}));
+			// Restore line number
+			selectedLineNumber.dataset.lineNumber = lineNumber;
+			// Un-focus code block
+			(document.activeElement as HTMLElement).blur();
+		} else {
+			// TODO: Review in December 2025 if old UI is gone. Currently only applies to PRs
+			location.hash = '#no-line'; // Update UI, without `scroll-to-top` behavior
+		}
+
 		history.replaceState(undefined, document.title, location.pathname); // Drop remaining # from url
 	}
 }
diff --git a/source/features/github-bugs.css b/source/features/github-bugs.css
index 544c84b7..ceb6ad50 100644
--- a/source/features/github-bugs.css
+++ b/source/features/github-bugs.css
@@ -102,6 +102,21 @@ svg.octicon-package + a.Truncate[href*='/releases/download/'] > .Truncate-text {
 	white-space: wrap;
 }
 
+/* Notifications "Group by" button unnecessary space */
+/* Info: https://github.com/refined-github/refined-github/pull/8208 */
+/* Test: https://github.com/notifications */
+/* Matches .d-md-block */
+@media (width >= 768px) {
+	/* Pulls the alert container to the right when empty */
+	.gap-4.pl-3:not(:has(a.h6)):has(
+			> .d-md-block
+				> .js-socket-channel[data-url^='/notifications/beta/recent_notifications_alert']
+		) {
+		/* Substract 16px spacing from both .gap-3 and .ml-3 */
+		margin-right: -32px;
+	}
+}
+
 /*
 
 Test URLs:
diff --git a/source/features/linkify-user-location.tsx b/source/features/linkify-user-location.tsx
index 785167d5..129a7f88 100644
--- a/source/features/linkify-user-location.tsx
+++ b/source/features/linkify-user-location.tsx
@@ -28,7 +28,9 @@ function addLocation({nextElementSibling, nextSibling}: SVGElement): Element {
 function initOnce(): void {
 	observe([
 		'[itemprop="homeLocation"] svg.octicon-location', // `isUserProfile`
-		'[aria-label="User location"] svg.octicon-location', // Hover cards
+		'.pagehead .has-location svg.octicon-location', // `isOrganizationProfile`
+		'[aria-label="User location"] svg.octicon-location', // User hover cards
+		'[aria-label="Organization Hovercard"] svg.octicon-location', // Organization hover cards
 	], addLocation);
 }
 
@@ -41,7 +43,8 @@ void features.add(import.meta.url, {
 
 Test URLs
 
-https://github.com/docubot
-https://github.com/
+- isUserProfile: https://github.com/mysticatea
+- isOrganizationProfile: https://github.com/github
+- Hover cards: https://github.com/
 
 */
diff --git a/source/features/show-names.css b/source/features/show-names.css
deleted file mode 100644
index 5765041e..00000000
--- a/source/features/show-names.css
+++ /dev/null
@@ -1,4 +0,0 @@
-/* Prevent text reversal Unicode hacks in full names */
-.rgh-show-names .timeline-comment-header-text bdo {
-	display: inline-block;
-}
diff --git a/source/features/show-names.tsx b/source/features/show-names.tsx
index dc26d3f1..51376d4d 100644
--- a/source/features/show-names.tsx
+++ b/source/features/show-names.tsx
@@ -1,5 +1,3 @@
-import './show-names.css';
-
 import React from 'dom-chef';
 import * as pageDetect from 'github-url-detection';
 import batchedFunction from 'batched-function';
@@ -27,7 +25,8 @@ function appendName(element: HTMLAnchorElement, fullName: string): void {
 	const insertionPoint = parentElement!.tagName === 'STRONG' ? parentElement! : element;
 	const nameElement = (
 		<span className="color-fg-muted css-truncate d-inline-block ml-1">
-			(<bdo className="css-truncate-target" style={{maxWidth: '200px'}}>{fullName}</bdo>)
+			{/* .css-truncate-target sets display: inline-block and confines bidi overrides #8191 */}
+			(<span className="css-truncate-target" style={{maxWidth: '200px'}}>{fullName}</span>)
 		</span>
 	);
 
@@ -106,4 +105,10 @@ Test URLs:
 - mannequins: https://togithub.com/python/cpython/issues/67591
 - newsfeed: https://github.com
 
+Special cases:
+
+- RTL: https://github.com/refined-github/refined-github/issues/8191
+- Bidi override case 1: https://togithub.com/FortAwesome/Font-Awesome/issues/2465
+- Bidi override case 2: https://togithub.com/w3c/webdriver/issues/385#issuecomment-598407238
+
 */
diff --git a/source/github-helpers/index.ts b/source/github-helpers/index.ts
index dc38bf79..8b8e5ae3 100644
--- a/source/github-helpers/index.ts
+++ b/source/github-helpers/index.ts
@@ -49,7 +49,10 @@ export function isUsernameAlreadyFullName(username: string, realname: string): b
 		.toLowerCase();
 	realname = realname
 		.normalize('NFD')
-		.replaceAll(/\W/g, '')
+		// Remove diacritics, punctuation and spaces
+		// https://stackoverflow.com/a/37511463/288906
+		// https://www.freecodecamp.org/news/what-is-punct-in-regex-how-to-match-all-punctuation-marks-in-regular-expressions/
+		.replaceAll(/[\p{Diacritic}\p{P}\s]/gu, '')
 		.toLowerCase();
 
 	return username === realname;


Test patch:
diff --git a/source/github-helpers/index.test.ts b/source/github-helpers/index.test.ts
index e3a7e40c..091a8ce4 100644
--- a/source/github-helpers/index.test.ts
+++ b/source/github-helpers/index.test.ts
@@ -91,8 +91,11 @@ test('isUsernameAlreadyFullName', () => {
 	assert.isTrue(isUsernameAlreadyFullName('john-wdoe', 'John W. Doe'));
 	assert.isTrue(isUsernameAlreadyFullName('john-doe-jr', 'John Doe Jr.'));
 	assert.isTrue(isUsernameAlreadyFullName('nicolo', 'Nicolò'));
+
+	assert.isFalse(isUsernameAlreadyFullName('wonderful', 'wonder'));
 	assert.isFalse(isUsernameAlreadyFullName('dotconnor', 'Connor Love'));
-	assert.isFalse(isUsernameAlreadyFullName('fregante ', 'Federico Brigante'));
+	assert.isFalse(isUsernameAlreadyFullName('fregante', 'Federico Brigante'));
+	assert.isFalse(isUsernameAlreadyFullName('chipwolf', 'Chip Wolf ‮ '));
 });
 
 test('getLatestVersionTag', () => {