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

Issue:
`fit-textareas` cuts off the description area for new pull requests
### Description

The description area on the "Open a pull request" page may not fit all the text when the `fit-textareas` is enabled. The text area looks fine after switching between the `Write` and `Preview` tabs. The issue is particularly noticeable when the repository defines a rather lengthy PR template.
Affects Firefox, Chrome seems to be fine

With the `fit-textareas` enabled:
![refined-github-feature-enabled](https://github.com/user-attachments/assets/662563b8-2eaf-444b-b845-d088d08548f4)

With the feature disabled:
![refined-github-feature-disabled](https://github.com/user-attachments/assets/8f69734e-06e7-48b9-a65f-088600eb5bc4)


### How to replicate the issue + URL

Try to open a new pull request in a repository that has a PR template.
Example with the current repository -  https://github.com/refined-github/refined-github/compare/main...test/bun?expand=1
Example with the test repository with a long template - https://github.com/Seregy/pr-template-test/compare/test-branch?expand=1


### Extension version

24.8.3

### Browser(s) used

Firefox 129.0

### Token ⚠️

- [X] I set a token in the options page. I understand that some features require a token to work.

Pull request description:
`fit-textareas` - Improve Copilot compatibility
- Might fix #7664 

## Test URLs

https://github.com/refined-github/refined-github/issues/7664

## Screenshot

Demo of reloading:

![scroll](https://github.com/user-attachments/assets/8fb3bd63-5f70-4ed9-b89a-536f3b17786f)


Patch:
diff --git a/readme.md b/readme.md
index 8220b495..ec76996f 100644
--- a/readme.md
+++ b/readme.md
@@ -43,11 +43,6 @@ GitHub Enterprise is also supported: [How to enable it](https://fregante.github.
 		</sup>
 	</p>
 	<br>
-	<a href="https://retool.com/?utm_campaign=sindresorhus">
-		<img src="https://sindresorhus.com/assets/thanks/retool-logo.svg" width="210">
-	</a>
-	<br>
-	<br>
 	<a href="https://workos.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=refined-github&utm_source=github">
 		<div>
 			<img src="https://sindresorhus.com/assets/thanks/workos-logo-white-bg.svg" width="210" alt="WorkOS">
@@ -57,19 +52,6 @@ GitHub Enterprise is also supported: [How to enable it](https://fregante.github.
 			<sub>Start selling to enterprise customers with just a few lines of code. Add Single Sign-On (and more) in minutes instead of months.</sup>
 		</div>
 	</a>
-	<br>
-	<br>
-	<a href="https://www.gitpod.io/?utm_campaign=sindresorhus&utm_medium=referral&utm_content=awesome&utm_source=github">
-		<div>
-			<img src="https://sindresorhus.com/assets/thanks/gitpod-logo-white-bg.svg" width="220" alt="Gitpod">
-		</div>
-		<b>Dev environments built for the cloud</b>
-		<div>
-			<sub>
-			Natively integrated with GitLab, GitHub, and Bitbucket, Gitpod automatically and continuously prebuilds dev environments for all your branches. As a result team members can instantly start coding with fresh dev environments for each new task - no matter if you are building a new feature, want to fix a bug, or work on a code review.
-			</sub>
-		</div>
-	</a>
 </div>
 </table>
 
diff --git a/source/feature-renames.json b/source/feature-renames.json
new file mode 100644
index 00000000..fc53f4e8
--- /dev/null
+++ b/source/feature-renames.json
@@ -0,0 +1,39 @@
+{
+	"separate-draft-pr-button": "one-click-pr-or-gist",
+	"prevent-pr-commit-link-loss": "prevent-link-loss",
+	"remove-projects-tab": "clean-repo-tabs",
+	"remove-unused-repo-tabs": "clean-repo-tabs",
+	"more-dropdown": "clean-repo-tabs",
+	"remove-diff-signs": "hide-diff-signs",
+	"remove-label-faster": "quick-label-removal",
+	"edit-files-faster": "quick-file-edit",
+	"edit-comments-faster": "quick-comment-edit",
+	"delete-review-comments-faster": "quick-review-comment-deletion",
+	"hide-comments-faster": "quick-comment-hiding",
+	"faster-reviews": "quick-review",
+	"faster-pr-diff-options": "one-click-diff-options",
+	"hide-useless-comments": "hide-low-quality-comments",
+	"hide-useless-newsfeed-events": "hide-newsfeed-noise",
+	"hide-noisy-newsfeed-events": "hide-newsfeed-noise",
+	"no-useless-split-diff-view": "no-unnecessary-split-diff-view",
+	"unwrap-useless-dropdowns": "unwrap-unnecessary-dropdowns",
+	"tag-changelog-link": "tag-changes-link",
+	"navigate-pages-with-arrow-keys": "pagination-hotkey",
+	"list-pr-for-branch": "list-prs-for-branch",
+	"quick-label-hiding": "quick-label-removal",
+	"next-scheduled-github-action": "github-actions-indicators",
+	"raw-file-link": "more-file-links",
+	"conversation-filters": "more-conversation-filters",
+	"quick-pr-diff-options": "one-click-diff-options",
+	"quick-review-buttons": "one-click-review-submission",
+	"pull-request-hotkey": "pull-request-hotkeys",
+	"first-published-tag-for-merged-pr": "closing-remarks",
+	"scheduled-and-manual-workflow-indicators": "github-actions-indicators",
+	"useful-forks": "archive-forks-link",
+	"set-default-repositories-type-to-sources": "hide-user-forks",
+	"highlight-deleted-and-added-files-in-diffs": "new-or-deleted-file",
+	"enable-file-links-in-compare-view": "actionable-pr-view-file",
+	"use-first-commit-message-for-new-prs": "pr-first-commit-title",
+	"comment-on-draft-pr-indicator": "netiquette",
+	"draft-pr-notice": "netiquette"
+}
diff --git a/source/features/fit-textareas.tsx b/source/features/fit-textareas.tsx
index 3ca2360b..4e107299 100644
--- a/source/features/fit-textareas.tsx
+++ b/source/features/fit-textareas.tsx
@@ -36,9 +36,15 @@ function watchTextarea(textarea: HTMLTextAreaElement, {signal}: SignalAsOptions)
 }
 
 function init(signal: AbortSignal): void {
-	// Exclude PR review box because it's in a `position:fixed` container;
-	// The scroll HAS to appear within the fixed element.
-	observe('textarea:not(#pull_request_review_body)', watchTextarea, {signal});
+	// `anchored-position`: Exclude PR review box because it's in a `position:fixed` container; The scroll HAS to appear within the fixed element.
+	// `#pull_request_body_ghost`: Special textarea that GitHub just matches to the visible textarea
+	observe(`
+		textarea:not(
+			anchored-position #pull_request_review_body,
+			#pull_request_body_ghost,
+			#pull_request_body_ghost_ruler
+		)
+	`, watchTextarea, {signal});
 }
 
 void features.add(import.meta.url, {
diff --git a/source/features/hide-inactive-deployments.tsx b/source/features/hide-inactive-deployments.tsx
index 03b89cc4..f06b9d07 100644
--- a/source/features/hide-inactive-deployments.tsx
+++ b/source/features/hide-inactive-deployments.tsx
@@ -6,7 +6,7 @@ import features from '../feature-manager.js';
 // This feature doesn't need an active observer
 function init(): void {
 	// Selects all the deployments first so that we can leave the last one on the page
-	const deployments = $$('.js-socket-channel[data-url*="/partials/deployed_event/"]');
+	const deployments = $$('.js-socket-channel[data-gid^="PR"]:has(.octicon-rocket)');
 	deployments.pop(); // Don't hide the last deployment, even if it is inactive
 
 	for (const deployment of deployments) {
diff --git a/source/features/pr-branch-auto-delete.tsx b/source/features/pr-branch-auto-delete.tsx
index 8997e2f0..22f3ab6f 100644
--- a/source/features/pr-branch-auto-delete.tsx
+++ b/source/features/pr-branch-auto-delete.tsx
@@ -43,6 +43,17 @@ void features.add(import.meta.url, {
 	additionalListeners: [
 		onPrMerge,
 	],
+	awaitDomReady: true, // TODO: Remove after https://github.com/refined-github/refined-github/issues/6566
 	onlyAdditionalListeners: true,
 	init,
 });
+
+/*
+
+Test URLs:
+
+1. Open https://github.com/pulls
+2. Click on any PRs you can merge (in repositories without native auto-delete)
+3. Merge the PR
+
+*/
diff --git a/source/features/releases-tab.tsx b/source/features/releases-tab.tsx
index 9bd45856..85f74c45 100644
--- a/source/features/releases-tab.tsx
+++ b/source/features/releases-tab.tsx
@@ -91,7 +91,7 @@ async function addReleasesDropdownItem(dropdownMenu: HTMLElement): Promise<false
 
 	appendBefore(
 		dropdownMenu,
-		'.dropdown-divider', // Won't exist if `more-dropdown` is disabled
+		'.dropdown-divider', // Won't exist if `clean-repo-tabs` is disabled
 		createDropdownItem({
 			label: type,
 			href: buildRepoURL(type.toLowerCase()),
diff --git a/source/options-storage.ts b/source/options-storage.ts
index 1ebf14cc..8aea61ed 100644
--- a/source/options-storage.ts
+++ b/source/options-storage.ts
@@ -1,5 +1,6 @@
 import OptionsSyncPerDomain from 'webext-options-sync-per-domain';
 
+import renamedFeatures from './feature-renames.json';
 import {importedFeatures} from '../readme.md';
 
 export type RGHOptions = typeof defaults;
@@ -12,46 +13,6 @@ const defaults = Object.assign({
 	logHTTP: false,
 }, Object.fromEntries(importedFeatures.map(id => [`feature:${id}`, true])));
 
-const renamedFeatures = new Map<string, string>([
-	['separate-draft-pr-button', 'one-click-pr-or-gist'],
-	['prevent-pr-commit-link-loss', 'prevent-link-loss'],
-	['remove-projects-tab', 'remove-unused-repo-tabs'],
-	['remove-unused-repo-tabs', 'clean-repo-tabs'],
-	['more-dropdown', 'clean-repo-tabs'],
-	['remove-diff-signs', 'hide-diff-signs'],
-	['remove-label-faster', 'quick-label-hiding'],
-	['edit-files-faster', 'quick-file-edit'],
-	['edit-comments-faster', 'quick-comment-edit'],
-	['delete-review-comments-faster', 'quick-review-comment-deletion'],
-	['hide-comments-faster', 'quick-comment-hiding'],
-	['faster-reviews', 'quick-review'],
-	['faster-pr-diff-options', 'quick-pr-diff-options'],
-	['hide-useless-comments', 'hide-low-quality-comments'],
-	['hide-useless-newsfeed-events', 'hide-newsfeed-noise'],
-	['hide-noisy-newsfeed-events', 'hide-newsfeed-noise'],
-	['no-useless-split-diff-view', 'no-unnecessary-split-diff-view'],
-	['unwrap-useless-dropdowns', 'unwrap-unnecessary-dropdowns'],
-	['tag-changelog-link', 'tag-changes-link'],
-	['navigate-pages-with-arrow-keys', 'pagination-hotkey'],
-	['list-pr-for-branch', 'list-prs-for-branch'],
-	['quick-label-hiding', 'quick-label-removal'],
-	['next-scheduled-github-action', 'github-actions-indicators'],
-	['raw-file-link', 'more-file-links'],
-	['conversation-filters', 'more-conversation-filters'],
-	['quick-pr-diff-options', 'one-click-diff-options'],
-	['quick-review-buttons', 'one-click-review-submission'],
-	['pull-request-hotkey', 'pull-request-hotkeys'],
-	['first-published-tag-for-merged-pr', 'closing-remarks'],
-	['scheduled-and-manual-workflow-indicators', 'github-actions-indicators'],
-	['useful-forks', 'fork-notice'],
-	['set-default-repositories-type-to-sources', 'hide-user-forks'],
-	['highlight-deleted-and-added-files-in-diffs', 'new-or-deleted-file'],
-	['enable-file-links-in-compare-view', 'actionable-pr-view-file'],
-	['use-first-commit-message-for-new-prs', 'pr-first-commit-title`'],
-	['comment-on-draft-pr-indicator', 'netiquette'],
-	['draft-pr-notice', 'netiquette'],
-]);
-
 export function isFeatureDisabled(options: RGHOptions, id: string): boolean {
 	// Must check if it's specifically `false`: It could be undefined if not yet in the readme or if misread from the entry point #6606
 	// eslint-disable-next-line @typescript-eslint/no-unnecessary-boolean-literal-compare
@@ -59,17 +20,13 @@ export function isFeatureDisabled(options: RGHOptions, id: string): boolean {
 }
 
 export function getNewFeatureName(possibleFeatureName: string): FeatureID | undefined {
-	let newFeatureName = possibleFeatureName;
-	while (renamedFeatures.has(newFeatureName)) {
-		newFeatureName = renamedFeatures.get(newFeatureName)!;
-	}
-
-	return importedFeatures.includes(newFeatureName as FeatureID) ? newFeatureName as FeatureID : undefined;
+	// @ts-expect-error Useless "no index type" error as usual
+	return renamedFeatures[possibleFeatureName];
 }
 
 const migrations = [
 	(options: RGHOptions): void => {
-		for (const [from, to] of renamedFeatures) {
+		for (const [from, to] of Object.entries(renamedFeatures)) {
 			if (typeof options[`feature:${from}`] === 'boolean') {
 				options[`feature:${to}`] = options[`feature:${from}`];
 			}
diff --git a/tsconfig.json b/tsconfig.json
index 061fb24d..ff9bd63e 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -3,6 +3,7 @@
 	"compilerOptions": {
 		"noUncheckedIndexedAccess": false,
 		"noPropertyAccessFromIndexSignature": false,
+		"resolveJsonModule": true,
 		"lib": [
 			"DOM",
 			"DOM.Iterable",


Test patch:
diff --git a/source/feature-renames.test.ts b/source/feature-renames.test.ts
new file mode 100644
index 00000000..ad5355af
--- /dev/null
+++ b/source/feature-renames.test.ts
@@ -0,0 +1,31 @@
+
+import {execSync} from 'node:child_process';
+import fs from 'node:fs';
+import path from 'node:path';
+import {test, expect} from 'vitest';
+
+import featureRenames from './feature-renames.json';
+
+const oldNames = Object.keys(featureRenames);
+const newNames = Object.values(featureRenames);
+
+test('Old feature names cannot appear anywhere in the repo', () => {
+	for (const oldName of oldNames) {
+		const grep = `rg -l "[^-]${oldName}[^-]" -g !feature-renames.json -g !package-lock.json`;
+		let results;
+		try {
+			results = execSync(grep, {encoding: 'utf8'});
+		} catch {
+			continue;
+		}
+
+		throw new Error(`Old feature name "${oldName}" found in ${results}`);
+	}
+});
+
+test('New feature names must exist in source/features/{name}.tsx', () => {
+	for (const newName of newNames) {
+		const filePath = path.join('source', 'features', `${newName}.tsx`);
+		expect(fs.existsSync(filePath), `New feature name "${newName}" not found in source/features`).toBe(true);
+	}
+});