Repository: refined-github/refined-github (ts)

Issue:
Issue with display user Arabic name
### Description

Arabic names in parentheses are not displayed correctly. Instead, the order of the Arabic text appears reversed

![image](https://github.com/user-attachments/assets/5c377a33-9711-45e5-a253-e84a688894b7)

The correct shape is `محمد الصوالحي`

as here

![image](https://github.com/user-attachments/assets/3d1f07fc-351c-42b8-ae21-1838b5b00e8f)



### How to replicate the issue + URL

1. Follow any user that have an Arabic name like me https://github.com/AhmedElTabarani
2. Go to the home page and see my activities, my Arabic name will display in the reversed order

### Extension version

 24.12.20

### Browser(s) used

Google Chrome

### Token ⚠️

- [X] I set a token in the options page. I understand that some features require a token to work.

Pull request:
`show-names` - Add support for RTL names
Fix #8191


## Test URLs

- 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


## Screenshot

| | Before | After |
|--------|--------|--------|
| RTL | ![CleanShot 2025-01-07 at 18 48 22@2x](https://github.com/user-attachments/assets/2476cb3d-d231-461b-be4e-1e6ed24b81ec) | ![CleanShot 2025-01-07 at 18 48 40@2x](https://github.com/user-attachments/assets/37604174-ba6c-4b20-aa72-580c03886383) |
| Bidi override case 1 | ![CleanShot 2025-01-07 at 19 26 04@2x](https://github.com/user-attachments/assets/5d9c0c72-8efe-447f-876a-ef2e7530b605) | ![CleanShot 2025-01-07 at 19 26 28@2x](https://github.com/user-attachments/assets/a89198cc-19fb-4eb4-ac77-9f2473d2b630) |
| Bidi override case 2 | ![CleanShot 2025-01-07 at 19 26 12@2x](https://github.com/user-attachments/assets/686f9186-d9ec-465e-bbf3-d9b086644656) | ![CleanShot 2025-01-07 at 19 26 36@2x](https://github.com/user-attachments/assets/e61a3b2a-4f26-445e-9e3c-e6174dd0e1fc) | 

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/package-lock.json b/package-lock.json
index 32110c80..c0e46288 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -45,7 +45,7 @@
 				"webext-dynamic-content-scripts": "^10.0.4",
 				"webext-msg": "^1.0.0",
 				"webext-options-sync": "^4.3.0",
-				"webext-options-sync-per-domain": "^4.2.3",
+				"webext-options-sync-per-domain": "^4.3.1",
 				"webext-permission-toggle": "^5.1.0",
 				"webext-storage": "^1.2.2",
 				"webext-storage-cache": "^6.0.3",
@@ -11797,9 +11797,9 @@
 			}
 		},
 		"node_modules/webext-detect": {
-			"version": "5.3.1",
-			"resolved": "https://registry.npmjs.org/webext-detect/-/webext-detect-5.3.1.tgz",
-			"integrity": "sha512-vmVd7haZ+CW/n037KUCi7t5Xn+oMxxScsgTPXirnHxXhHLJsR+4je9glh6QrsptgUfkZN4dGcFTrBrZUah1RAg==",
+			"version": "5.3.2",
+			"resolved": "https://registry.npmjs.org/webext-detect/-/webext-detect-5.3.2.tgz",
+			"integrity": "sha512-RU/e8OLiPwU0efWs/cHp3sRCY+vakaTnbOS/x8VM/FkTtv6cfW/Ofed0i7WnkAKw2jbp4gki3WcpAdii+2QVMw==",
 			"license": "MIT",
 			"engines": {
 				"node": ">=18"
@@ -11878,14 +11878,14 @@
 			}
 		},
 		"node_modules/webext-options-sync-per-domain": {
-			"version": "4.2.3",
-			"resolved": "https://registry.npmjs.org/webext-options-sync-per-domain/-/webext-options-sync-per-domain-4.2.3.tgz",
-			"integrity": "sha512-WebwQ+ah8oqccLrzN965Bw+BzuKGSjiYbBpGrFAkRaD3WkWeyqPth9alw3Wlq1UEaC5Ul5TkB2qxQbCuoYHuTQ==",
+			"version": "4.3.1",
+			"resolved": "https://registry.npmjs.org/webext-options-sync-per-domain/-/webext-options-sync-per-domain-4.3.1.tgz",
+			"integrity": "sha512-7X7hr5opY4o3l3GWRawHrsQQv3NDreRzbqAYEo29OTFKe8ihlRydaD2nfZj3RDB0QBjf1Fb1H7OTgM1HQFSvuA==",
 			"license": "MIT",
 			"dependencies": {
 				"memoize": "^10.0.0",
-				"webext-detect": "^5.0.2",
-				"webext-options-sync": "^4.2.3",
+				"webext-detect": "^5.3.2",
+				"webext-options-sync": "^4.3.0",
 				"webext-patterns": "^1.5.0",
 				"webext-permissions": "^3.1.3"
 			},
diff --git a/package.json b/package.json
index 071513b9..8e899087 100644
--- a/package.json
+++ b/package.json
@@ -109,7 +109,7 @@
 		"webext-dynamic-content-scripts": "^10.0.4",
 		"webext-msg": "^1.0.0",
 		"webext-options-sync": "^4.3.0",
-		"webext-options-sync-per-domain": "^4.2.3",
+		"webext-options-sync-per-domain": "^4.3.1",
 		"webext-permission-toggle": "^5.1.0",
 		"webext-storage": "^1.2.2",
 		"webext-storage-cache": "^6.0.3",
diff --git a/safari/app-store-description.txt b/safari/app-store-description.txt
index ddd1c364..be8c62b8 100644
--- a/safari/app-store-description.txt
+++ b/safari/app-store-description.txt
@@ -18,7 +18,7 @@ Refined GitHub is a Safari extension that simplifies the GitHub.com interface an
 - Adds possible related pages and alternatives on 404 pages.
 - Displays the age of the repository in the sidebar.
 
-And so much more! 200+ features. See the website for a full list of features.
+And so much more! 200+ features. See the Developer Website for a full list of features.
 
 
 The GITHUB and REFINED GITHUB trademarks are owned by GitHub, Inc. and used under license.
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/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/features/sticky-sidebar.css b/source/features/sticky-sidebar.css
index abcbf4d2..74f4d135 100644
--- a/source/features/sticky-sidebar.css
+++ b/source/features/sticky-sidebar.css
@@ -45,6 +45,10 @@ Exclusively use simple sums and use `0px` instead of `0`
 	z-index: 1;
 }
 
+#discussion_bucket #partial-discussion-sidebar.rgh-sticky-sidebar-container {
+	height: min-content;
+}
+
 /* Hides the last divider (on pull requests) to avoid https://user-images.githubusercontent.com/10238474/62282128-af6fb980-b457-11e9-8b18-c29687b88da1.gif */
 .rgh-sticky-sidebar + .border-top {
 	display: none;
diff --git a/source/features/sticky-sidebar.tsx b/source/features/sticky-sidebar.tsx
index af22fd0b..14c1fc1d 100644
--- a/source/features/sticky-sidebar.tsx
+++ b/source/features/sticky-sidebar.tsx
@@ -14,6 +14,7 @@ const sidebarSelector = [
 	'.Layout-sidebar .BorderGrid', // `isRepoRoot`
 	'.Layout-sidebar #partial-discussion-sidebar', // Old `isConversation`
 	'div[data-testid="issue-viewer-metadata-pane"]', // `isConversation`
+	'#discussion_bucket #partial-discussion-sidebar', // `isDiscussion`
 ];
 
 let sidebar: HTMLElement | undefined;
@@ -39,7 +40,8 @@ function trackSidebar(signal: AbortSignal, foundSidebar: HTMLElement): void {
 		sidebar = undefined;
 	});
 
-	sidebar.parentElement?.classList.add('rgh-sticky-sidebar-container');
+	const container = sidebar.parentElement!.id === 'discussion_bucket' ? sidebar : sidebar.parentElement!;
+	container.classList.add('rgh-sticky-sidebar-container');
 
 	sidebar.addEventListener('mouseenter', toggleHoverState, {signal});
 	sidebar.addEventListener('mouseleave', toggleHoverState, {signal});
@@ -73,6 +75,7 @@ void features.add(import.meta.url, {
 	include: [
 		pageDetect.isRepoRoot,
 		pageDetect.isConversation,
+		pageDetect.isDiscussion,
 	],
 	exclude: [
 		() => screen.availWidth < minimumViewportWidthForSidebar,
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;
diff --git a/source/options.tsx b/source/options.tsx
index 46eb600e..599e5aaa 100644
--- a/source/options.tsx
+++ b/source/options.tsx
@@ -147,8 +147,7 @@ function addEventListeners(): void {
 			element.setAttribute('item', domain === 'default' ? 'options' : 'options:' + domain);
 		}
 
-		// Delay to let options load first
-		setTimeout(updateListDom, 100);
+		updateListDom();
 	});
 
 	// Refresh page when permissions are changed (because the dropdown selector needs to be regenerated)
diff --git a/source/options/token-validation.tsx b/source/options/token-validation.tsx
index aea26f1c..140e9bd0 100644
--- a/source/options/token-validation.tsx
+++ b/source/options/token-validation.tsx
@@ -85,8 +85,5 @@ export default async function initTokenValidation(syncedForm: SyncedForm | undef
 	});
 
 	// Update domain-dependent page content when the domain is changed
-	syncedForm?.onChange(async () => {
-		// Delay to let options load first
-		setTimeout(validateToken, 100);
-	});
+	syncedForm?.onChange(validateToken);
 }


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', () => {