From 43ccbde39c615264a05d4689b1eea1758bd5aeff Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Apr 2026 08:48:20 +0000 Subject: [PATCH 01/34] Initial setup: create dprint configuration for TS/TSX formatting Agent-Logs-Url: https://github.com/refined-github/refined-github/sessions/c3dc672a-de56-45b7-a27a-15d171bd96cb Co-authored-by: fregante <1402241+fregante@users.noreply.github.com> --- dprint.json | 8 ++++++++ package-lock.json | 8 ++++++++ package.json | 1 + 3 files changed, 17 insertions(+) create mode 100644 dprint.json diff --git a/dprint.json b/dprint.json new file mode 100644 index 000000000000..9ac6c46c2ec1 --- /dev/null +++ b/dprint.json @@ -0,0 +1,8 @@ +{ + "excludes": [ + "**/*-lock.json" + ], + "plugins": [ + // specify plugin urls here + ] +} diff --git a/package-lock.json b/package-lock.json index fac2255dfdfa..6f7a762d008d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -56,6 +56,7 @@ }, "devDependencies": { "@biomejs/biome": "^2.3.12", + "@dprint/typescript": "^0.95.15", "@eslint-react/eslint-plugin": "^2.7.2", "@eslint/compat": "^2.0.1", "@rollup/plugin-alias": "^6.0.0", @@ -297,6 +298,13 @@ "node": ">=14.21.3" } }, + "node_modules/@dprint/typescript": { + "version": "0.95.15", + "resolved": "https://registry.npmjs.org/@dprint/typescript/-/typescript-0.95.15.tgz", + "integrity": "sha512-PQGYicsjlv7Eq+5BzpI04Ow2N9xeHkjr9O1TG3lDebK2B48IvXFQz0RwO4OhkKj22o7YLcjVIFM6WL7zVuM2ng==", + "dev": true, + "license": "MIT" + }, "node_modules/@emnapi/core": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz", diff --git a/package.json b/package.json index 1077e1581d8e..6df65bb30e0c 100644 --- a/package.json +++ b/package.json @@ -88,6 +88,7 @@ }, "devDependencies": { "@biomejs/biome": "^2.3.12", + "@dprint/typescript": "^0.95.15", "@eslint-react/eslint-plugin": "^2.7.2", "@eslint/compat": "^2.0.1", "@rollup/plugin-alias": "^6.0.0", From 0cd9b23f51a4feeb065b4fd2bdad6e18c5a2a1f0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Apr 2026 08:53:21 +0000 Subject: [PATCH 02/34] Format TS/TSX files with dprint and add lint:dprint script Agent-Logs-Url: https://github.com/refined-github/refined-github/sessions/c3dc672a-de56-45b7-a27a-15d171bd96cb Co-authored-by: fregante <1402241+fregante@users.noreply.github.com> --- dprint.json | 18 +- package.json | 3 +- source/background.ts | 38 ++-- source/feature-data.ts | 8 +- source/feature-manager.tsx | 38 ++-- source/feature-renames.test.ts | 6 +- source/features/action-pr-link.tsx | 16 +- source/features/action-used-by-link.tsx | 14 +- source/features/actionable-pr-view-file.tsx | 10 +- source/features/actions-run-removal.tsx | 10 +- source/features/align-issue-labels.tsx | 4 +- source/features/archive-forks-link.tsx | 4 +- .../features/avoid-accidental-submissions.tsx | 22 +- .../features/batch-mark-files-as-viewed.tsx | 16 +- source/features/bugs-tab.tsx | 40 ++-- source/features/ci-link.tsx | 39 ++-- .../features/clean-conversation-filters.tsx | 20 +- .../features/clean-conversation-headers.tsx | 62 +++--- .../features/clean-conversation-sidebar.tsx | 12 +- source/features/clean-readme-url.tsx | 2 +- .../features/clean-repo-filelist-actions.tsx | 10 +- source/features/clean-repo-sidebar.tsx | 8 +- source/features/clean-repo-tabs.tsx | 26 +-- .../clear-pr-merge-commit-message.tsx | 27 ++- source/features/click-outside-modal.tsx | 8 +- source/features/close-as-unplanned.tsx | 12 +- source/features/close-out-of-view-modals.tsx | 12 +- source/features/closing-remarks.tsx | 72 ++++--- .../features/collapsible-content-button.tsx | 38 ++-- .../command-palette-navigation-shortcuts.tsx | 8 +- source/features/comment-excess.tsx | 21 +- .../features/comments-time-machine-links.tsx | 74 ++++--- source/features/confirm-release.tsx | 8 +- source/features/conflict-marker.tsx | 28 +-- source/features/conventional-commits.tsx | 22 +- .../features/conversation-activity-filter.tsx | 116 ++++++----- source/features/conversation-authors.tsx | 20 +- .../conversation-links-on-repo-lists.tsx | 86 ++++---- source/features/convert-release-to-draft.tsx | 30 +-- source/features/copy-on-y.tsx | 6 +- source/features/create-release-shortcut.tsx | 6 +- source/features/cross-deleted-pr-branches.tsx | 6 +- source/features/deep-reblame.tsx | 85 ++++---- source/features/default-branch-button.tsx | 23 +-- source/features/dim-bots.tsx | 12 +- source/features/download-folder-button.tsx | 6 +- .../features/easy-toggle-commit-messages.tsx | 8 +- source/features/easy-toggle-files.tsx | 27 ++- source/features/embed-gist-inline.tsx | 25 ++- source/features/esc-to-cancel.tsx | 6 +- source/features/esc-to-deselect-line.tsx | 12 +- .../features/expand-all-hidden-comments.tsx | 12 +- .../extend-conversation-status-filters.tsx | 6 +- source/features/extend-diff-expander.tsx | 6 +- source/features/file-age-color.tsx | 6 +- source/features/fit-textareas.tsx | 26 ++- source/features/fix-no-pr-search.tsx | 2 +- source/features/github-actions-indicators.tsx | 64 +++--- .../global-conversation-list-filters.tsx | 6 +- .../hidden-review-comments-indicator.tsx | 34 +-- source/features/hide-inactive-deployments.tsx | 2 +- source/features/hide-low-quality-comments.tsx | 18 +- .../hide-navigation-hover-highlight.tsx | 2 +- source/features/highest-rated-comment.tsx | 57 +++--- .../highlight-non-default-base-branch.tsx | 43 ++-- source/features/html-preview-link.tsx | 26 +-- source/features/improve-shortcut-help.tsx | 82 ++++---- .../jump-to-change-requested-comment.tsx | 6 +- .../jump-to-conversation-close-event.tsx | 28 +-- source/features/keyboard-navigation.tsx | 29 ++- .../last-notification-page-button.tsx | 16 +- source/features/link-to-changelog-file.tsx | 27 +-- source/features/link-to-compare-diff.tsx | 8 +- source/features/link-to-github-io.tsx | 32 +-- source/features/linkify-branch-references.tsx | 32 +-- source/features/linkify-code.tsx | 10 +- source/features/linkify-commit-sha.tsx | 4 +- source/features/linkify-line-numbers.tsx | 6 +- source/features/linkify-symbolic-links.tsx | 12 +- source/features/linkify-text.tsx | 22 +- source/features/linkify-user-labels.tsx | 22 +- source/features/linkify-user-location.tsx | 10 +- source/features/list-prs-for-branch.tsx | 32 +-- source/features/list-prs-for-file.tsx | 52 ++--- source/features/locked-issue.tsx | 30 +-- .../features/mark-merge-commits-in-list.tsx | 40 ++-- source/features/mark-private-orgs.tsx | 16 +- source/features/more-dropdown-links.tsx | 14 +- source/features/more-file-links.tsx | 33 +-- source/features/netiquette.tsx | 68 +++--- source/features/new-milestone-button.tsx | 18 +- source/features/new-or-deleted-file.tsx | 10 +- .../new-repo-disable-projects-and-wikis.tsx | 57 +++--- .../no-duplicate-list-update-time.tsx | 4 +- source/features/no-modals.tsx | 17 +- source/features/no-self-reference.tsx | 4 +- .../no-unnecessary-split-diff-view.tsx | 6 +- source/features/one-click-diff-options.tsx | 65 +++--- source/features/one-click-pr-or-gist.tsx | 26 +-- .../features/one-click-review-submission.tsx | 34 +-- source/features/one-key-formatting.tsx | 33 +-- source/features/open-all-conversations.tsx | 46 +++-- source/features/open-all-notifications.tsx | 54 ++--- .../features/open-issue-to-latest-comment.tsx | 4 +- source/features/parse-backticks.tsx | 4 +- source/features/patch-diff-links.tsx | 60 +++--- source/features/pinned-issues-update-time.tsx | 26 +-- source/features/pr-base-commit.tsx | 28 ++- source/features/pr-branch-auto-delete.tsx | 21 +- source/features/pr-commit-lines-changed.tsx | 25 +-- source/features/pr-filters.tsx | 48 +++-- source/features/pr-first-commit-title.tsx | 23 +-- .../pr-jump-to-first-non-viewed-file.tsx | 6 +- source/features/pr-notification-link.tsx | 2 +- source/features/prevent-comment-loss.tsx | 6 +- .../prevent-duplicate-pr-submission.tsx | 4 +- source/features/prevent-link-loss.tsx | 33 +-- source/features/preview-hidden-comments.tsx | 17 +- .../features/previous-next-commit-buttons.tsx | 10 +- source/features/previous-version.tsx | 20 +- source/features/profile-gists-link.tsx | 38 ++-- source/features/profile-hotkey.tsx | 8 +- source/features/pull-request-hotkeys.tsx | 4 +- source/features/quick-comment-edit.tsx | 39 ++-- source/features/quick-comment-hiding.tsx | 41 ++-- source/features/quick-file-edit.tsx | 12 +- source/features/quick-label-removal.tsx | 36 ++-- source/features/quick-mention.tsx | 44 ++-- source/features/quick-new-issue.tsx | 6 +- source/features/quick-repo-deletion.tsx | 69 ++++--- .../quick-review-comment-deletion.tsx | 28 +-- source/features/quick-review.tsx | 59 +++--- source/features/reactions-avatars.tsx | 43 ++-- source/features/release-download-count.tsx | 40 ++-- source/features/releases-dropdown.tsx | 30 +-- source/features/releases-tab.tsx | 73 ++++--- .../features/reload-failed-proxied-images.tsx | 8 +- source/features/repo-age.tsx | 45 ++-- source/features/repo-avatars.tsx | 16 +- source/features/repo-header-info.tsx | 66 +++--- source/features/repo-wide-file-finder.tsx | 12 +- source/features/rerun-workflow.tsx | 8 +- source/features/restore-file.tsx | 96 +++++---- source/features/rgh-deduplicator.tsx | 4 +- source/features/rgh-dim-commits.tsx | 6 +- source/features/rgh-feature-descriptions.tsx | 86 ++++---- .../features/rgh-improve-new-issue-form.tsx | 77 ++++--- source/features/rgh-linkify-features.tsx | 48 +++-- source/features/rgh-linkify-yolo.tsx | 20 +- source/features/rgh-netiquette.tsx | 38 ++-- source/features/rgh-options-link.tsx | 6 +- source/features/rgh-pr-template.tsx | 16 +- source/features/rgh-token-user.tsx | 23 +-- .../features/same-branch-author-commits.tsx | 2 +- source/features/same-page-links.tsx | 22 +- .../select-all-notifications-shortcut.tsx | 6 +- source/features/select-notifications.tsx | 95 ++++----- source/features/selection-in-new-tab.tsx | 8 +- source/features/shorten-links.tsx | 4 +- .../show-associated-branch-prs-on-fork.tsx | 50 ++--- source/features/show-names.tsx | 58 +++--- source/features/show-open-prs-of-forks.tsx | 33 +-- .../features/show-user-top-repositories.tsx | 2 +- source/features/show-whitespace.tsx | 10 +- source/features/sidebar-focus-file.tsx | 6 +- source/features/small-user-avatars.tsx | 33 +-- .../sort-conversations-by-update-time.tsx | 10 +- source/features/status-subscription.tsx | 186 ++++++++--------- source/features/sticky-sidebar.tsx | 16 +- .../stop-redirecting-in-notification-bar.tsx | 4 +- .../features/suggest-commit-title-limit.tsx | 27 ++- source/features/swap-branches-on-compare.tsx | 14 +- source/features/sync-pr-commit-title.tsx | 52 +++-- source/features/tab-to-indent.tsx | 4 +- source/features/table-input.tsx | 91 +++++---- source/features/tag-changes-link.tsx | 32 +-- source/features/tags-on-commits-list.tsx | 59 +++--- .../features/toggle-everything-with-alt.tsx | 23 ++- source/features/unclip-checks.tsx | 6 +- source/features/unfinished-comments.tsx | 6 +- source/features/unread-anywhere.tsx | 21 +- source/features/unreleased-commits.tsx | 70 +++---- .../features/unwrap-unnecessary-dropdowns.tsx | 4 +- .../features/update-pr-from-base-branch.tsx | 106 +++++----- source/features/useful-not-found-page.tsx | 44 ++-- .../features/user-profile-follower-badge.tsx | 10 +- source/features/vertical-front-matter.tsx | 29 +-- source/features/view-last-pr-deployment.tsx | 24 ++- source/features/visit-tag.tsx | 48 +++-- source/features/warn-pr-from-master.tsx | 21 +- .../features/warning-for-disallow-edits.tsx | 11 +- .../github-events/on-commit-title-update.ts | 9 +- source/github-events/on-field-keydown.tsx | 4 +- source/github-events/on-pr-merge.ts | 8 +- source/github-events/on-react-page-update.tsx | 4 +- source/github-helpers/api.tsx | 44 ++-- source/github-helpers/banner.tsx | 8 +- source/github-helpers/bugs-label.test.ts | 2 +- source/github-helpers/bugs-label.ts | 3 +- .../github-helpers/create-dropdown-item.tsx | 19 +- source/github-helpers/dom-formatters.tsx | 20 +- .../get-current-git-ref.test.ts | 193 +++++++++++------- source/github-helpers/get-current-git-ref.ts | 8 +- source/github-helpers/get-default-branch.ts | 12 +- source/github-helpers/get-pr-info.ts | 4 +- source/github-helpers/get-tab-count.ts | 4 +- source/github-helpers/get-user-avatar.ts | 4 +- source/github-helpers/get-user-permission.ts | 11 +- source/github-helpers/github-file-url.test.ts | 21 +- source/github-helpers/github-file-url.ts | 21 +- source/github-helpers/github-token.ts | 20 +- source/github-helpers/group-buttons.tsx | 6 +- source/github-helpers/hotkey.tsx | 10 +- source/github-helpers/hotkeys.test.ts | 51 +++-- source/github-helpers/icon-loading.tsx | 6 +- source/github-helpers/index.test.ts | 65 +++--- source/github-helpers/index.ts | 47 +++-- .../github-helpers/is-conversation-locked.ts | 12 +- source/github-helpers/is-default-branch.ts | 2 +- source/github-helpers/load-details-menu.ts | 2 +- source/github-helpers/parse-backticks.test.ts | 4 +- source/github-helpers/parse-backticks.tsx | 6 +- .../github-helpers/parse-compare-url.test.ts | 2 +- source/github-helpers/parse-compare-url.ts | 4 +- source/github-helpers/parse-rendered-text.ts | 2 +- source/github-helpers/pr-branches.test.ts | 4 +- source/github-helpers/pr-branches.ts | 8 +- .../github-helpers/prevent-link-loss.test.ts | 98 ++++++--- source/github-helpers/prevent-link-loss.ts | 46 ++++- source/github-helpers/search-query.test.ts | 76 +++---- source/github-helpers/selectors.test.ts | 20 +- source/github-helpers/selectors.ts | 15 +- source/github-helpers/timeline-item.tsx | 2 +- source/github-helpers/toast.tsx | 26 ++- source/github-widgets/notice-bar.tsx | 16 +- source/globals.d.ts | 20 +- source/helpers/abbreviate-number.ts | 4 +- source/helpers/abortable-classname.ts | 2 +- source/helpers/attach-element.ts | 4 +- source/helpers/bisect.tsx | 68 +++--- .../helpers/calculate-css-calc-string.test.ts | 2 +- source/helpers/caller-id.test.ts | 4 +- source/helpers/caller-id.ts | 2 +- source/helpers/clean-commit-message.test.ts | 63 ++++-- source/helpers/clear-cache-handler.ts | 2 +- source/helpers/click-all.ts | 4 +- source/helpers/conventional-commits.test.ts | 8 +- source/helpers/conventional-commits.ts | 2 +- source/helpers/dom-utils.ts | 14 +- source/helpers/errors.ts | 29 +-- source/helpers/event-listener-loop.test.ts | 4 +- source/helpers/event-listener-loop.ts | 6 +- source/helpers/extension-release-age.ts | 4 +- source/helpers/feature-helpers.ts | 6 +- source/helpers/feature-utils.test.ts | 100 +++++---- source/helpers/feature-utils.ts | 7 +- source/helpers/fetch-dom.ts | 13 +- source/helpers/get-items-between.test.ts | 2 +- source/helpers/hotfix.tsx | 24 +-- source/helpers/is-development-version.ts | 2 +- source/helpers/is-low-quality-comment.test.ts | 2 +- source/helpers/isomorphic-fetch.ts | 10 +- source/helpers/loose-parse-int.test.ts | 2 +- source/helpers/on-element-removal.ts | 2 +- source/helpers/onetime.ts | 6 +- source/helpers/open-options.tsx | 8 +- source/helpers/open-tabs.ts | 2 +- source/helpers/p-utils.ts | 2 +- source/helpers/pluralize.test.ts | 2 +- source/helpers/pr-commit-cleaner.test.ts | 2 +- source/helpers/preserve-scroll.ts | 4 +- source/helpers/recreate-element.ts | 2 +- source/helpers/rgh-links.tsx | 6 +- source/helpers/selector-observer.tsx | 20 +- source/helpers/set-react-input-value.ts | 2 +- .../helpers/show-whitespace-on-line.test.ts | 4 +- source/helpers/show-whitespace-on-line.tsx | 8 +- source/helpers/types.d.ts | 2 +- source/options-storage.ts | 6 +- source/options.tsx | 56 ++--- source/options/feature-list.tsx | 44 ++-- source/options/reload-without.ts | 16 +- source/options/toggle-all.ts | 4 +- source/options/token-validation.tsx | 22 +- 284 files changed, 3797 insertions(+), 3056 deletions(-) diff --git a/dprint.json b/dprint.json index 9ac6c46c2ec1..de59d506ea56 100644 --- a/dprint.json +++ b/dprint.json @@ -1,8 +1,14 @@ { - "excludes": [ - "**/*-lock.json" - ], - "plugins": [ - // specify plugin urls here - ] + "typescript": { + "indentWidth": 1, + "useTabs": true, + "quoteStyle": "preferSingle", + "trailingCommas": "onlyMultiLine", + "semiColons": "always", + "lineWidth": 120, + "jsx.multiLineParens": "always" + }, + "includes": ["source/**/*.ts", "source/**/*.tsx"], + "excludes": ["**/*-lock.json"], + "plugins": ["node_modules/@dprint/typescript/plugin.wasm"] } diff --git a/package.json b/package.json index 6df65bb30e0c..9d259281da96 100644 --- a/package.json +++ b/package.json @@ -7,11 +7,12 @@ "build:typescript": "tsc --noEmit", "build:bundle": "rollup -c", "fix": "run-p \"lint:biome -- --write\" \"lint:js -- --fix\" format \"vitest -- --update\" --continue-on-error", - "format": "prettier . --write && biome format --fix", + "format": "prettier . --write && biome format --fix && dprint fmt", "lint": "run-p lint:* --continue-on-error", "lint:js": "eslint .", "lint:biome": "biome check", "lint:prettier": "prettier . --check", + "lint:dprint": "dprint check", "new": "bash build/new-feature.sh", "pack:safari": "xcodebuild -project 'safari/Refined GitHub.xcodeproj' -scheme 'Refined GitHub' -destination 'platform=macOS'", "prepare:safari": "bash build/prepare-safari-release.sh", diff --git a/source/background.ts b/source/background.ts index 7519dc6f0933..ccd634468191 100644 --- a/source/background.ts +++ b/source/background.ts @@ -1,22 +1,22 @@ import 'webext-dynamic-content-scripts'; import 'webext-bugs/options-menu-item'; -import {customizeNoAllUrlsErrorMessage} from 'webext-bugs/no-all-urls'; -import {globalCache} from 'webext-storage-cache'; // Also needed to regularly clear the cache +import { customizeNoAllUrlsErrorMessage } from 'webext-bugs/no-all-urls'; +import { isSafari } from 'webext-detect'; +import { handleMessages } from 'webext-msg'; import addPermissionToggle from 'webext-permission-toggle'; -import {StorageItem} from 'webext-storage'; -import {handleMessages} from 'webext-msg'; -import {isSafari} from 'webext-detect'; +import { StorageItem } from 'webext-storage'; +import { globalCache } from 'webext-storage-cache'; // Also needed to regularly clear the cache -import optionsStorage, {hasToken} from './options-storage.js'; +import { doesBrowserActionOpenOptions } from './helpers/feature-utils.js'; +import { styleHotfixes } from './helpers/hotfix.js'; import isDevelopmentVersion from './helpers/is-development-version.js'; -import {doesBrowserActionOpenOptions} from './helpers/feature-utils.js'; -import {styleHotfixes} from './helpers/hotfix.js'; -import {fetchText} from './helpers/isomorphic-fetch.js'; +import { fetchText } from './helpers/isomorphic-fetch.js'; +import optionsStorage, { hasToken } from './options-storage.js'; import addReloadWithoutContentScripts from './options/reload-without.js'; -const {version, permissions} = chrome.runtime.getManifest(); +const { version, permissions } = chrome.runtime.getManifest(); -const welcomeShown = new StorageItem('welcomed', {defaultValue: false}); +const welcomeShown = new StorageItem('welcomed', { defaultValue: false }); // GHE support if (!isSafari()) { @@ -27,15 +27,17 @@ if (!isSafari()) { addReloadWithoutContentScripts(); // Extend the error message for the "No All URLs" bugfix -customizeNoAllUrlsErrorMessage('Refined GitHub is not meant to run on every website. If you’re looking to enable it on GitHub Enterprise, follow the instructions in the Options page.'); +customizeNoAllUrlsErrorMessage( + 'Refined GitHub is not meant to run on every website. If you’re looking to enable it on GitHub Enterprise, follow the instructions in the Options page.', +); handleMessages({ - async openUrls(urls: string[], {tab}: chrome.runtime.MessageSender) { + async openUrls(urls: string[], { tab }: chrome.runtime.MessageSender) { // Reuse container // TODO: https://github.com/refined-github/refined-github/issues/8657 // Soft-disabled via `cookies` permission check: https://github.com/refined-github/refined-github/pull/8786#pullrequestreview-3491531965 const firefoxOnlyProps = tab && 'cookieStoreId' in tab && permissions!.includes('cookies') - ? {cookieStoreId: tab.cookieStoreId} + ? { cookieStoreId: tab.cookieStoreId } : {}; for (const [index, url] of urls.entries()) { @@ -47,7 +49,7 @@ handleMessages({ }); } }, - async closeTab(_: any, {tab}: chrome.runtime.MessageSender) { + async closeTab(_: any, { tab }: chrome.runtime.MessageSender) { void chrome.tabs.remove(tab!.id!); }, fetchText, @@ -71,7 +73,7 @@ chrome.action.onClicked.addListener(async tab => { return; } - const {actionUrl} = await optionsStorage.getAll(); + const { actionUrl } = await optionsStorage.getAll(); if (!actionUrl) { // Default to options page if unset void chrome.runtime.openOptionsPage(); @@ -91,7 +93,7 @@ async function showWelcomePage(): Promise { const [token, permissions] = await Promise.all([ hasToken(), // We can't handle an invalid token on a "Welcome" page, so just check whether the user has ever set one - chrome.permissions.contains({origins: ['https://github.com/*']}), + chrome.permissions.contains({ origins: ['https://github.com/*'] }), ]); try { @@ -101,7 +103,7 @@ async function showWelcomePage(): Promise { } const url = chrome.runtime.getURL('assets/welcome.html'); - await chrome.tabs.create({url}); + await chrome.tabs.create({ url }); } finally { // Make sure it's always set to true even in case of errors await welcomeShown.set(true); diff --git a/source/feature-data.ts b/source/feature-data.ts index c736fa61fbe3..cada1c9f4974 100644 --- a/source/feature-data.ts +++ b/source/feature-data.ts @@ -1,13 +1,13 @@ // Run `npm run vitest` to update these files -import importedFeaturesRaw from '../build/__snapshots__/imported-features.json' with {type: 'json'}; -import featuresMetasRaw from '../build/__snapshots__/features-meta.json' with {type: 'json'}; -import renamedFeatures from './feature-renames.json' with {type: 'json'}; +import featuresMetasRaw from '../build/__snapshots__/features-meta.json' with { type: 'json' }; +import importedFeaturesRaw from '../build/__snapshots__/imported-features.json' with { type: 'json' }; +import renamedFeatures from './feature-renames.json' with { type: 'json' }; export const importedFeatures = importedFeaturesRaw as FeatureId[]; export const featuresMeta = featuresMetasRaw as FeatureMeta[]; // eslint-disable-next-line unicorn/prefer-export-from -- The build silently fails to provide `renamedFeatures` in this scope. I don't know whose fault it is. -export {renamedFeatures}; +export { renamedFeatures }; export function getNewFeatureName(possibleFeatureName: string): FeatureId | undefined { // @ts-expect-error Useless "no index type" error as usual diff --git a/source/feature-manager.tsx b/source/feature-manager.tsx index 499915102fdb..81039813e677 100644 --- a/source/feature-manager.tsx +++ b/source/feature-manager.tsx @@ -1,35 +1,29 @@ /* eslint-disable no-await-in-loop -- Event loops */ import React from 'dom-chef'; -import {elementExists} from 'select-dom'; import domLoaded from 'dom-loaded'; -import stripIndent from 'strip-indent'; -import type {Promisable} from 'type-fest'; import * as pageDetect from 'github-url-detection'; -import {isWebPage} from 'webext-detect'; -import {messageRuntime} from 'webext-msg'; import oneEvent from 'one-event'; +import { elementExists } from 'select-dom'; +import stripIndent from 'strip-indent'; +import type { Promisable } from 'type-fest'; +import { isWebPage } from 'webext-detect'; +import { messageRuntime } from 'webext-msg'; -import waitFor from './helpers/wait-for.js'; -import ArrayMap from './helpers/map-of-arrays.js'; +import asyncForEach from './helpers/async-for-each.js'; import bisectFeatures from './helpers/bisect.js'; -import { - shouldFeatureRun, - isFeaturePrivate, - type RunConditions, -} from './helpers/feature-utils.js'; -import optionsStorage, {isFeatureDisabled, type RghOptions} from './options-storage.js'; +import { catchErrors, disableErrorLogging } from './helpers/errors.js'; +import { getFeatureId, listenToAjaxedLoad, log, shortcutMap } from './helpers/feature-helpers.js'; +import { isFeaturePrivate, type RunConditions, shouldFeatureRun } from './helpers/feature-utils.js'; import { applyStyleHotfixes, + brokenFeatures, getLocalHotfixesAsOptions, preloadSyncLocalStrings, - brokenFeatures, } from './helpers/hotfix.js'; -import asyncForEach from './helpers/async-for-each.js'; -import {catchErrors, disableErrorLogging} from './helpers/errors.js'; -import { - getFeatureId, listenToAjaxedLoad, log, shortcutMap, -} from './helpers/feature-helpers.js'; -import {contentScriptToggle} from './options/reload-without.js'; +import ArrayMap from './helpers/map-of-arrays.js'; +import waitFor from './helpers/wait-for.js'; +import optionsStorage, { isFeatureDisabled, type RghOptions } from './options-storage.js'; +import { contentScriptToggle } from './options/reload-without.js'; type FeatureInitResult = void | false; type FeatureInit = (signal: AbortSignal) => Promisable; @@ -102,7 +96,7 @@ const globalReady = new Promise(async resolve => { // Request in the background page to avoid showing a 404 request in the console // https://github.com/refined-github/refined-github/issues/6433 // eslint-disable-next-line promise/prefer-await-to-then -- Reads as a callback - void messageRuntime({getStyleHotfixes: true}).then(applyStyleHotfixes); + void messageRuntime({ getStyleHotfixes: true }).then(applyStyleHotfixes); if (options.customCss.trim().length > 0) { // Review #5857 and #5493 before making changes @@ -197,7 +191,7 @@ async function add(url: string, ...loaders: FeatureLoader[]): Promise { continue; } - if (!await shouldFeatureRun({asLongAs, include, exclude})) { + if (!await shouldFeatureRun({ asLongAs, include, exclude })) { continue; } diff --git a/source/feature-renames.test.ts b/source/feature-renames.test.ts index ab529286a858..363cde8c81e5 100644 --- a/source/feature-renames.test.ts +++ b/source/feature-renames.test.ts @@ -1,7 +1,7 @@ -import {execSync} from 'node:child_process'; +import { execSync } from 'node:child_process'; import fs from 'node:fs/promises'; import path from 'node:path'; -import {test, expect} from 'vitest'; +import { expect, test } from 'vitest'; import featureRenames from './feature-renames.json'; @@ -13,7 +13,7 @@ test('old feature names cannot appear anywhere in the repo', () => { const grep = `rg -l "[^-]${oldName}[^-]" -g !feature-renames.json -g !package-lock.json`; let results; try { - results = execSync(grep, {encoding: 'utf8'}); + results = execSync(grep, { encoding: 'utf8' }); } catch { continue; } diff --git a/source/features/action-pr-link.tsx b/source/features/action-pr-link.tsx index c7293b14d84f..02dde3b507fd 100644 --- a/source/features/action-pr-link.tsx +++ b/source/features/action-pr-link.tsx @@ -1,8 +1,8 @@ import * as pageDetect from 'github-url-detection'; import features from '../feature-manager.js'; +import { getConversationNumber } from '../github-helpers/index.js'; import observe from '../helpers/selector-observer.js'; -import {getConversationNumber} from '../github-helpers/index.js'; function setSearchParameter(anchorElement: HTMLAnchorElement, name: string, value: string): void { const parameters = new URLSearchParams(anchorElement.search); @@ -22,15 +22,19 @@ async function addForPr(actionLink: HTMLAnchorElement): Promise { } async function initForRepositoryActionsPage(signal: AbortSignal): Promise { - observe('div.Box-row[id^=check_suite_] a[data-hovercard-type="pull_request"]', addForRepositoryActions, {signal}); + observe('div.Box-row[id^=check_suite_] a[data-hovercard-type="pull_request"]', addForRepositoryActions, { signal }); } async function initForPrPage(signal: AbortSignal): Promise { // Exclude rgh-link, include isPRCommits - observe([ - 'main [href="/apps/github-actions"] ~ div a.status-actions', // Legacy - '[data-testid="check-run-item"] a[href*="/actions/runs/"]', // React component on isPRCommits - ], addForPr, {signal}); + observe( + [ + 'main [href="/apps/github-actions"] ~ div a.status-actions', // Legacy + '[data-testid="check-run-item"] a[href*="/actions/runs/"]', // React component on isPRCommits + ], + addForPr, + { signal }, + ); } void features.add(import.meta.url, { diff --git a/source/features/action-used-by-link.tsx b/source/features/action-used-by-link.tsx index 5c63b51e4390..2f4eb2120d35 100644 --- a/source/features/action-used-by-link.tsx +++ b/source/features/action-used-by-link.tsx @@ -1,7 +1,7 @@ import React from 'dom-chef'; -import {$} from 'select-dom/strict.js'; -import SearchIcon from 'octicons-plain-react/Search'; import * as pageDetect from 'github-url-detection'; +import SearchIcon from 'octicons-plain-react/Search'; +import { $ } from 'select-dom/strict.js'; import features from '../feature-manager.js'; import observe from '../helpers/selector-observer.js'; @@ -27,14 +27,16 @@ function addUsageLink(side: HTMLElement): void { // TODO: Integrate style better https://github.com/refined-github/refined-github/pull/8285/files#r1951911960 side.after( - - Usage examples - , + ( + + Usage examples + + ), ); } function init(signal: AbortSignal): void { - observe('[data-testid="resources"] > ul', addUsageLink, {signal}); + observe('[data-testid="resources"] > ul', addUsageLink, { signal }); } void features.add(import.meta.url, { diff --git a/source/features/actionable-pr-view-file.tsx b/source/features/actionable-pr-view-file.tsx index 64ed86ed36a3..03bc9d8b072e 100644 --- a/source/features/actionable-pr-view-file.tsx +++ b/source/features/actionable-pr-view-file.tsx @@ -1,14 +1,14 @@ -import {elementExists} from 'select-dom'; import * as pageDetect from 'github-url-detection'; +import { elementExists } from 'select-dom'; import features from '../feature-manager.js'; -import {getBranches} from '../github-helpers/pr-branches.js'; +import { getBranches } from '../github-helpers/pr-branches.js'; +import { deletedHeadRepository } from '../github-helpers/selectors.js'; import observe from '../helpers/selector-observer.js'; -import {deletedHeadRepository} from '../github-helpers/selectors.js'; /** Rebuilds the "View file" link because it points to the base repo and to the commit, instead of the head repo and its branch */ function alter(viewFileLink: HTMLAnchorElement): void { - const {owner, name, branch} = getBranches().head; + const { owner, name, branch } = getBranches().head; const filePath = viewFileLink.closest('[data-path]')!.getAttribute('data-path')!; // Do not replace with `GitHubFileURL` #3152 #3111 #2595 @@ -18,7 +18,7 @@ function alter(viewFileLink: HTMLAnchorElement): void { } function init(signal: AbortSignal): void { - observe('.file-header:not([data-file-deleted="true"]) a.dropdown-item[data-ga-click^="View file"]', alter, {signal}); + observe('.file-header:not([data-file-deleted="true"]) a.dropdown-item[data-ga-click^="View file"]', alter, { signal }); } void features.add(import.meta.url, { diff --git a/source/features/actions-run-removal.tsx b/source/features/actions-run-removal.tsx index ff9e9a5b3428..d1c522377403 100644 --- a/source/features/actions-run-removal.tsx +++ b/source/features/actions-run-removal.tsx @@ -1,13 +1,13 @@ import './actions-run-removal.css'; import React from 'dom-chef'; -import TrashIcon from 'octicons-plain-react/Trash'; -import SquareCircleIcon from 'octicons-plain-react/SquareCircle'; import * as pageDetect from 'github-url-detection'; -import {$, $optional} from 'select-dom/strict.js'; +import SquareCircleIcon from 'octicons-plain-react/SquareCircle'; +import TrashIcon from 'octicons-plain-react/Trash'; +import { $, $optional } from 'select-dom/strict.js'; -import observe from '../helpers/selector-observer.js'; import features from '../feature-manager.js'; +import observe from '../helpers/selector-observer.js'; function addQuickButtons(contextMenuIcon: HTMLElement): void { const contextMenuDetails = contextMenuIcon.closest('details')!; @@ -35,7 +35,7 @@ function addQuickButtons(contextMenuIcon: HTMLElement): void { } function init(signal: AbortSignal): void { - observe('#partial-actions-workflow-runs .Box-row details .octicon-kebab-horizontal', addQuickButtons, {signal}); + observe('#partial-actions-workflow-runs .Box-row details .octicon-kebab-horizontal', addQuickButtons, { signal }); } void features.add(import.meta.url, { diff --git a/source/features/align-issue-labels.tsx b/source/features/align-issue-labels.tsx index 18d5dc8ff179..81d135064946 100644 --- a/source/features/align-issue-labels.tsx +++ b/source/features/align-issue-labels.tsx @@ -1,7 +1,7 @@ import './align-issue-labels.css'; import * as pageDetect from 'github-url-detection'; -import {$} from 'select-dom/strict.js'; +import { $ } from 'select-dom/strict.js'; import features from '../feature-manager.js'; import observe from '../helpers/selector-observer.js'; @@ -17,7 +17,7 @@ function alignBadges(badges: HTMLElement): void { } function init(signal: AbortSignal): void { - observe('[class*="trailingBadgesContainer"]:not(:empty)', alignBadges, {signal}); + observe('[class*="trailingBadgesContainer"]:not(:empty)', alignBadges, { signal }); } void features.add(import.meta.url, { diff --git a/source/features/archive-forks-link.tsx b/source/features/archive-forks-link.tsx index 91792563ee37..e437cbdfe3f8 100644 --- a/source/features/archive-forks-link.tsx +++ b/source/features/archive-forks-link.tsx @@ -2,8 +2,8 @@ import React from 'dom-chef'; import * as pageDetect from 'github-url-detection'; import features from '../feature-manager.js'; +import { buildRepoUrl } from '../github-helpers/index.js'; import observe from '../helpers/selector-observer.js'; -import {buildRepoUrl} from '../github-helpers/index.js'; function addLinkToBanner(banner: HTMLElement): void { if (banner.lastChild!.textContent.includes('repository has been archived')) { @@ -16,7 +16,7 @@ function addLinkToBanner(banner: HTMLElement): void { } function init(signal: AbortSignal): void { - observe('#js-repo-pjax-container > .flash-warn:first-of-type', addLinkToBanner, {signal}); + observe('#js-repo-pjax-container > .flash-warn:first-of-type', addLinkToBanner, { signal }); } void features.add(import.meta.url, { diff --git a/source/features/avoid-accidental-submissions.tsx b/source/features/avoid-accidental-submissions.tsx index 7040afc6358c..dbcd0bb20ed4 100644 --- a/source/features/avoid-accidental-submissions.tsx +++ b/source/features/avoid-accidental-submissions.tsx @@ -1,10 +1,10 @@ +import delegate, { type DelegateEvent } from 'delegate-it'; import React from 'dom-chef'; -import {elementExists} from 'select-dom'; import * as pageDetect from 'github-url-detection'; -import delegate, {type DelegateEvent} from 'delegate-it'; +import { elementExists } from 'select-dom'; import features from '../feature-manager.js'; -import {modKey as moduleKey} from '../github-helpers/hotkey.js'; +import { modKey as moduleKey } from '../github-helpers/hotkey.js'; function onKeyDown(event: DelegateEvent): void { const field = event.delegateTarget; @@ -22,10 +22,12 @@ function onKeyDown(event: DelegateEvent): void return; } - if (elementExists([ - 'button[data-hotkey="Mod+Enter"]:disabled', - 'button[type="submit"]:disabled', - ], form)) { + if ( + elementExists([ + 'button[data-hotkey="Mod+Enter"]:disabled', + 'button[type="submit"]:disabled', + ], form) + ) { return; } @@ -37,7 +39,9 @@ function onKeyDown(event: DelegateEvent): void const message = (

- A submission via enter has been prevented. You can press enter again or use {moduleKey}enter. + A submission via enter has been prevented. You can press enter again or use{' '} + {moduleKey} + enter.

); @@ -65,7 +69,7 @@ const inputElements = [ ]; function init(signal: AbortSignal): void { - delegate([...inputElements, ...legacyInputElements], 'keydown', onKeyDown, {signal, capture: true}); + delegate([...inputElements, ...legacyInputElements], 'keydown', onKeyDown, { signal, capture: true }); } void features.add(import.meta.url, { diff --git a/source/features/batch-mark-files-as-viewed.tsx b/source/features/batch-mark-files-as-viewed.tsx index 2005114578ee..5d2d73ea7fe5 100644 --- a/source/features/batch-mark-files-as-viewed.tsx +++ b/source/features/batch-mark-files-as-viewed.tsx @@ -1,12 +1,12 @@ -import {$$, elementExists} from 'select-dom'; -import {$} from 'select-dom/strict.js'; -import {onAbort} from 'abort-utils'; +import { onAbort } from 'abort-utils'; +import delegate, { type DelegateEvent } from 'delegate-it'; import * as pageDetect from 'github-url-detection'; -import delegate, {type DelegateEvent} from 'delegate-it'; +import { $$, elementExists } from 'select-dom'; +import { $ } from 'select-dom/strict.js'; import features from '../feature-manager.js'; -import clickAll from '../helpers/click-all.js'; import showToast from '../github-helpers/toast.js'; +import clickAll from '../helpers/click-all.js'; import getItemsBetween from '../helpers/get-items-between.js'; export const viewedToggleSelector = [ @@ -91,9 +91,9 @@ const onAltClick = (event: DelegateEvent): void => }; function init(signal: AbortSignal): void { - delegate(viewedToggleSelector, 'click', onAltClick, {signal}); - delegate(viewedToggleSelector, 'click', batchToggle, {signal}); - delegate(viewedToggleSelector, 'click', remember, {signal}); + delegate(viewedToggleSelector, 'click', onAltClick, { signal }); + delegate(viewedToggleSelector, 'click', batchToggle, { signal }); + delegate(viewedToggleSelector, 'click', remember, { signal }); onAbort(signal, () => { previousFile = undefined; }); diff --git a/source/features/bugs-tab.tsx b/source/features/bugs-tab.tsx index f0fe794fc49d..80ed76d8638a 100644 --- a/source/features/bugs-tab.tsx +++ b/source/features/bugs-tab.tsx @@ -1,20 +1,20 @@ import React from 'dom-chef'; -import {CachedFunction} from 'webext-storage-cache'; -import {$} from 'select-dom/strict.js'; -import {elementExists} from 'select-dom'; -import BugIcon from 'octicons-plain-react/Bug'; import elementReady from 'element-ready'; import * as pageDetect from 'github-url-detection'; +import BugIcon from 'octicons-plain-react/Bug'; +import { elementExists } from 'select-dom'; +import { $ } from 'select-dom/strict.js'; +import { CachedFunction } from 'webext-storage-cache'; import features from '../feature-manager.js'; import api from '../github-helpers/api.js'; -import {cacheByRepo, triggerRepoNavOverflow} from '../github-helpers/index.js'; +import isBugLabel from '../github-helpers/bugs-label.js'; +import { expectToken } from '../github-helpers/github-token.js'; +import { cacheByRepo, triggerRepoNavOverflow } from '../github-helpers/index.js'; import SearchQuery from '../github-helpers/search-query.js'; import abbreviateNumber from '../helpers/abbreviate-number.js'; -import {highlightTab, unhighlightTab} from '../helpers/dom-utils.js'; -import isBugLabel from '../github-helpers/bugs-label.js'; +import { highlightTab, unhighlightTab } from '../helpers/dom-utils.js'; import CountBugs from './bugs-tab.gql'; -import {expectToken} from '../github-helpers/github-token.js'; type ApiResponse = { issues: { @@ -36,7 +36,7 @@ type Bugs = { }; async function countBugs(): Promise { - const {repository} = await api.v4(CountBugs) as {repository: ApiResponse}; + const { repository } = await api.v4(CountBugs) as { repository: ApiResponse; }; const bugTypeCount = repository.issues.totalCount; let label = repository.labels.nodes.find(label => label.name === 'bug'); @@ -47,18 +47,18 @@ async function countBugs(): Promise { const bugCount = Math.max(bugTypeCount, bugLabelCount); // Label might not be found if the repo uses a non-standard bug label name - return {label: label?.name ?? 'bug', count: bugCount}; + return { label: label?.name ?? 'bug', count: bugCount }; } const bugs = new CachedFunction('bugs', { updater: countBugs, - maxAge: {minutes: 30}, - staleWhileRevalidate: {days: 4}, + maxAge: { minutes: 30 }, + staleWhileRevalidate: { days: 4 }, cacheKey: cacheByRepo, }); async function getSearchQueryBugLabel(): Promise { - const {label} = await bugs.getCached() ?? {}; + const { label } = await bugs.getCached() ?? {}; return `(label:${SearchQuery.escapeValue(label ?? 'bug')} OR type:Bug)`; } @@ -76,13 +76,13 @@ async function addBugsTab(): Promise { // On other pages: // - only show the tab if needed if (!(await isBugsListing())) { - const {count} = await bugsPromise; + const { count } = await bugsPromise; if (count === 0) { return false; } } - const issuesTab = await elementReady('a.UnderlineNav-item[data-hotkey="g i"]', {waitForChildren: false}); + const issuesTab = await elementReady('a.UnderlineNav-item[data-hotkey="g i"]', { waitForChildren: false }); if (!issuesTab) { // Issues are disabled return false; @@ -102,7 +102,7 @@ async function addBugsTab(): Promise { const bugsTabTitle = $('[data-content]', bugsTab); bugsTabTitle.dataset.content = 'Bugs'; bugsTabTitle.textContent = 'Bugs'; - $('.octicon', bugsTab).replaceWith(); + $('.octicon', bugsTab).replaceWith(); // Set temporary counter const bugsCounter = $('.Counter', bugsTab); @@ -114,7 +114,7 @@ async function addBugsTab(): Promise { // In case GitHub changes its layout again #4166 if (issuesTab.parentElement instanceof HTMLLIElement) { - issuesTab.parentElement.after(
  • {bugsTab}
  • ); + issuesTab.parentElement.after(
  • {bugsTab}
  • ); } else { issuesTab.after(bugsTab); } @@ -123,7 +123,7 @@ async function addBugsTab(): Promise { // Update bugs count try { - const {count: bugCount} = await bugsPromise; + const { count: bugCount } = await bugsPromise; bugsCounter.textContent = abbreviateNumber(bugCount); bugsCounter.title = bugCount > 999 ? String(bugCount) : ''; } catch (error) { @@ -140,12 +140,12 @@ function highlightBugsTab(): void { } async function removePinnedIssues(): Promise { - const pinnedIssues = await elementReady('.js-pinned-issues-reorder-container', {waitForChildren: false}); + const pinnedIssues = await elementReady('.js-pinned-issues-reorder-container', { waitForChildren: false }); pinnedIssues?.remove(); } async function updateBugsTagHighlighting(): Promise { - const {count, label} = await bugs.get(); + const { count, label } = await bugs.get(); if (count === 0) { return false; } diff --git a/source/features/ci-link.tsx b/source/features/ci-link.tsx index a84def3a5aa7..43db62d0de01 100644 --- a/source/features/ci-link.tsx +++ b/source/features/ci-link.tsx @@ -5,14 +5,14 @@ import * as pageDetect from 'github-url-detection'; import features from '../feature-manager.js'; import api from '../github-helpers/api.js'; -import {buildRepoUrl} from '../github-helpers/index.js'; +import { expectToken } from '../github-helpers/github-token.js'; +import { buildRepoUrl } from '../github-helpers/index.js'; +import { isSmallDevice } from '../helpers/dom-utils.js'; import observe from '../helpers/selector-observer.js'; import getChecks from './ci-link.gql'; -import {expectToken} from '../github-helpers/github-token.js'; -import {isSmallDevice} from '../helpers/dom-utils.js'; async function getCommitWithChecks(): Promise { - const {repository} = await api.v4(getChecks); + const { repository } = await api.v4(getChecks); if (repository.isEmpty) { return; @@ -39,12 +39,15 @@ async function add(anchor: HTMLElement): Promise { const endpoint = buildRepoUrl('commits/checks-statuses-rollups'); anchor.parentElement!.append( // Hide in small viewports, matches `repo-header-info` - + , @@ -57,15 +60,19 @@ async function add(anchor: HTMLElement): Promise { async function init(signal: AbortSignal): Promise { await expectToken(); - observe([ - 'div[data-testid="top-nav-center"] li:last-child > a[class*="prc-Breadcrumbs-Item"]', - // TODO: Remove after July 2026 - // Desktop - '.AppHeader-context-item:not([data-hovercard-type])', - - // Mobile. `> *:first-child` avoids finding our own element - '.AppHeader-context-compact-mainItem > span:first-child', - ], add, {signal}); + observe( + [ + 'div[data-testid="top-nav-center"] li:last-child > a[class*="prc-Breadcrumbs-Item"]', + // TODO: Remove after July 2026 + // Desktop + '.AppHeader-context-item:not([data-hovercard-type])', + + // Mobile. `> *:first-child` avoids finding our own element + '.AppHeader-context-compact-mainItem > span:first-child', + ], + add, + { signal }, + ); } void features.add(import.meta.url, { diff --git a/source/features/clean-conversation-filters.tsx b/source/features/clean-conversation-filters.tsx index 07ef496814b3..3ccd6aa282c0 100644 --- a/source/features/clean-conversation-filters.tsx +++ b/source/features/clean-conversation-filters.tsx @@ -1,15 +1,15 @@ -import {CachedFunction} from 'webext-storage-cache'; -import {$optional} from 'select-dom/strict.js'; -import {elementExists} from 'select-dom'; import elementReady from 'element-ready'; import * as pageDetect from 'github-url-detection'; +import { elementExists } from 'select-dom'; +import { $optional } from 'select-dom/strict.js'; +import { CachedFunction } from 'webext-storage-cache'; import features from '../feature-manager.js'; -import {cacheByRepo} from '../github-helpers/index.js'; -import HasAnyProjects from './clean-conversation-filters.gql'; import api from '../github-helpers/api.js'; -import {expectToken, expectTokenScope} from '../github-helpers/github-token.js'; +import { expectToken, expectTokenScope } from '../github-helpers/github-token.js'; +import { cacheByRepo } from '../github-helpers/index.js'; import observe from '../helpers/selector-observer.js'; +import HasAnyProjects from './clean-conversation-filters.gql'; const hasAnyProjects = new CachedFunction('has-projects', { async updater(): Promise { @@ -26,7 +26,7 @@ const hasAnyProjects = new CachedFunction('has-projects', { } await expectTokenScope('read:project'); - const {repository, organization} = await api.v4(HasAnyProjects, { + const { repository, organization } = await api.v4(HasAnyProjects, { allowErrors: true, }); @@ -35,8 +35,8 @@ const hasAnyProjects = new CachedFunction('has-projects', { || Boolean(organization?.projects?.totalCount) || Boolean(organization?.projectsV2?.totalCount); }, - maxAge: {days: 1}, - staleWhileRevalidate: {days: 20}, + maxAge: { days: 1 }, + staleWhileRevalidate: { days: 20 }, cacheKey: cacheByRepo, }); @@ -60,7 +60,7 @@ async function hide(container: HTMLElement): Promise { async function init(signal: AbortSignal): Promise { await expectToken(); - observe(String.raw`#\:rs\:-list-view-metadata`, hide, {signal}); + observe(String.raw`#\:rs\:-list-view-metadata`, hide, { signal }); } void features.add(import.meta.url, { diff --git a/source/features/clean-conversation-headers.tsx b/source/features/clean-conversation-headers.tsx index 0deb159e6457..5dbca747199c 100644 --- a/source/features/clean-conversation-headers.tsx +++ b/source/features/clean-conversation-headers.tsx @@ -1,17 +1,17 @@ import './clean-conversation-headers.css'; import React from 'dom-chef'; -import {$, $optional} from 'select-dom/strict.js'; import elementReady from 'element-ready'; -import ArrowLeftIcon from 'octicons-plain-react/ArrowLeft'; import * as pageDetect from 'github-url-detection'; +import ArrowLeftIcon from 'octicons-plain-react/ArrowLeft'; +import { $, $optional } from 'select-dom/strict.js'; import features from '../feature-manager.js'; import getDefaultBranch from '../github-helpers/get-default-branch.js'; +import { expectToken } from '../github-helpers/github-token.js'; +import { parseReferenceRaw } from '../github-helpers/pr-branches.js'; +import { assertNodeContent } from '../helpers/dom-utils.js'; import observe from '../helpers/selector-observer.js'; -import {expectToken} from '../github-helpers/github-token.js'; -import {parseReferenceRaw} from '../github-helpers/pr-branches.js'; -import {assertNodeContent} from '../helpers/dom-utils.js'; async function highlightNonDefaultBranchPrs(base: HTMLElement, baseBranch: string): Promise { const wasDefaultBranch = pageDetect.isClosedConversation() && baseBranch === 'master'; @@ -31,17 +31,16 @@ async function cleanPrHeader(summaryRow: HTMLElement): Promise { // Extra author name is only shown on `isPRConversation` // Hide if it's the same as the opener (always) or merger - const shouldHideAuthor - = pageDetect.isPRConversation() - // #7802 - && !summaryRow.closest([ - 'div[class*="stickyHeader"]', - // TODO: Remove after July 2026 - '.sticky-content', - '.gh-header-sticky', - ]) - // First link in the summary row is always the author - && $('a', summaryRow).textContent === (await elementReady(prCreatorSelector))!.textContent; + const shouldHideAuthor = pageDetect.isPRConversation() + // #7802 + && !summaryRow.closest([ + 'div[class*="stickyHeader"]', + // TODO: Remove after July 2026 + '.sticky-content', + '.gh-header-sticky', + ]) + // First link in the summary row is always the author + && $('a', summaryRow).textContent === (await elementReady(prCreatorSelector))!.textContent; if (shouldHideAuthor) { summaryRow.classList.add('rgh-hide-author'); @@ -65,28 +64,33 @@ async function cleanPrHeader(summaryRow: HTMLElement): Promise { void highlightNonDefaultBranchPrs(base, baseBranch); // Shows on PRs: main [←] feature - const anchor - = $optional('.commit-ref-dropdown', summaryRow)?.nextSibling // TODO: remove after July 2026 - ?? base.nextSibling!.nextSibling!; + const anchor = $optional('.commit-ref-dropdown', summaryRow)?.nextSibling // TODO: remove after July 2026 + ?? base.nextSibling!.nextSibling!; assertNodeContent(anchor, 'from'); anchor.after( - - - , + ( + + + + ), ); } async function init(signal: AbortSignal): Promise { await expectToken(); - observe([ - 'span[class*="PullRequestHeaderSummary"]', - // Old views. TODO: Remove after July 2026 - '.gh-header-meta > .flex-auto', // Real - '.js-issues-results .rgh-conversation-activity-filter', // Helper in case it runs first and breaks the `>` selector, because it wraps the .flex-auto element - '[class^="StateLabel"] + div > span:first-child', - ], cleanPrHeader, {signal}); + observe( + [ + 'span[class*="PullRequestHeaderSummary"]', + // Old views. TODO: Remove after July 2026 + '.gh-header-meta > .flex-auto', // Real + '.js-issues-results .rgh-conversation-activity-filter', // Helper in case it runs first and breaks the `>` selector, because it wraps the .flex-auto element + '[class^="StateLabel"] + div > span:first-child', + ], + cleanPrHeader, + { signal }, + ); } void features.add(import.meta.url, { diff --git a/source/features/clean-conversation-sidebar.tsx b/source/features/clean-conversation-sidebar.tsx index d322c404579b..ca28dfdfd751 100644 --- a/source/features/clean-conversation-sidebar.tsx +++ b/source/features/clean-conversation-sidebar.tsx @@ -1,14 +1,14 @@ import './clean-conversation-sidebar.css'; import React from 'dom-chef'; -import {elementExists} from 'select-dom'; -import {$, $optional} from 'select-dom/strict.js'; import * as pageDetect from 'github-url-detection'; +import { elementExists } from 'select-dom'; +import { $, $optional } from 'select-dom/strict.js'; import features from '../feature-manager.js'; +import { removeTextNodeContaining } from '../helpers/dom-utils.js'; import onElementRemoval from '../helpers/on-element-removal.js'; import observe from '../helpers/selector-observer.js'; -import {removeTextNodeContaining} from '../helpers/dom-utils.js'; // Don't cache: https://github.com/refined-github/refined-github/issues/7283 const canEditSidebar = (): boolean => elementExists('.discussion-sidebar-item [data-hotkey="l"]'); @@ -92,7 +92,7 @@ async function cleanSidebarLegacy(sidebar: HTMLElement): Promise { if (assignYourself) { removeTextNodeContaining(assignYourself.previousSibling!, 'No one—'); $('[aria-label="Select assignees"] summary').append( - – {assignYourself}, + – {assignYourself}, ); assignees.closest('.discussion-sidebar-item')!.classList.add('rgh-clean-sidebar'); } @@ -121,7 +121,7 @@ async function cleanSidebarLegacy(sidebar: HTMLElement): Promise { if (createBranchLink && !openWorkspaceButton) { createBranchLink.classList.add('Link--muted', 'Link--inTextBlock'); $('[aria-label="Link issues"] summary').append( - – {createBranchLink}, + – {createBranchLink}, ); } @@ -135,7 +135,7 @@ async function cleanSidebarLegacy(sidebar: HTMLElement): Promise { } function init(signal: AbortSignal): void { - observe('#partial-discussion-sidebar', cleanSidebarLegacy, {signal}); + observe('#partial-discussion-sidebar', cleanSidebarLegacy, { signal }); } void features.add(import.meta.url, { diff --git a/source/features/clean-readme-url.tsx b/source/features/clean-readme-url.tsx index 56ae4219cbe9..ab4894235484 100644 --- a/source/features/clean-readme-url.tsx +++ b/source/features/clean-readme-url.tsx @@ -14,7 +14,7 @@ function init(signal: AbortSignal): void { maybeCleanUrl(); let interval: NodeJS.Timeout; if (globalThis.navigation) { - navigation.addEventListener('navigate', maybeCleanUrl, {signal}); + navigation.addEventListener('navigate', maybeCleanUrl, { signal }); } else { interval = setInterval(() => { maybeCleanUrl(); diff --git a/source/features/clean-repo-filelist-actions.tsx b/source/features/clean-repo-filelist-actions.tsx index 054184875cf0..93d050b14479 100644 --- a/source/features/clean-repo-filelist-actions.tsx +++ b/source/features/clean-repo-filelist-actions.tsx @@ -1,18 +1,18 @@ import React from 'dom-chef'; -import {$, $optional} from 'select-dom/strict.js'; import * as pageDetect from 'github-url-detection'; import PlusIcon from 'octicons-plain-react/Plus'; +import { $, $optional } from 'select-dom/strict.js'; -import observe from '../helpers/selector-observer.js'; -import {assertNodeContent, wrap} from '../helpers/dom-utils.js'; import features from '../feature-manager.js'; +import { assertNodeContent, wrap } from '../helpers/dom-utils.js'; +import observe from '../helpers/selector-observer.js'; import './clean-repo-filelist-actions.css'; /** Add tooltip on a wrapper to avoid breaking dropdown functionality */ function addTooltipToSummary(childElement: Element, tooltip: string): void { wrap( childElement, -
    , +
    , ); } @@ -45,7 +45,7 @@ function cleanCodeButton(codeButton: Element): void { } function init(signal: AbortSignal): void { - observe('.react-directory-remove-file-icon', cleanFilelistActions, {signal}); + observe('.react-directory-remove-file-icon', cleanFilelistActions, { signal }); } void features.add(import.meta.url, { diff --git a/source/features/clean-repo-sidebar.tsx b/source/features/clean-repo-sidebar.tsx index 5eabe7186df9..80935b15ff0e 100644 --- a/source/features/clean-repo-sidebar.tsx +++ b/source/features/clean-repo-sidebar.tsx @@ -1,18 +1,18 @@ import './clean-repo-sidebar.css'; -import {elementExists} from 'select-dom'; -import {$, $optional} from 'select-dom/strict.js'; import domLoaded from 'dom-loaded'; import elementReady from 'element-ready'; import * as pageDetect from 'github-url-detection'; +import { elementExists } from 'select-dom'; +import { $, $optional } from 'select-dom/strict.js'; import features from '../feature-manager.js'; -import {buildRepoUrl} from '../github-helpers/index.js'; +import { buildRepoUrl } from '../github-helpers/index.js'; // The h2 is to avoid hiding website links that include '/releases' #4424 // TODO: It's broken const releasesSidebarSelector = '.Layout-sidebar .BorderGrid-cell h2 a[href$="/releases"]'; async function cleanReleases(): Promise { - const sidebarReleases = await elementReady(releasesSidebarSelector, {waitForChildren: false}); + const sidebarReleases = await elementReady(releasesSidebarSelector, { waitForChildren: false }); if (!sidebarReleases) { return; } diff --git a/source/features/clean-repo-tabs.tsx b/source/features/clean-repo-tabs.tsx index e33fffda6ad7..47adaefdcece 100644 --- a/source/features/clean-repo-tabs.tsx +++ b/source/features/clean-repo-tabs.tsx @@ -1,18 +1,18 @@ import React from 'dom-chef'; -import {CachedFunction} from 'webext-storage-cache'; -import {countElements} from 'select-dom'; -import {$, $optional} from 'select-dom/strict.js'; import elementReady from 'element-ready'; import * as pageDetect from 'github-url-detection'; +import { countElements } from 'select-dom'; +import { $, $optional } from 'select-dom/strict.js'; +import { CachedFunction } from 'webext-storage-cache'; import features from '../feature-manager.js'; -import fetchDom from '../helpers/fetch-dom.js'; import api from '../github-helpers/api.js'; import getTabCount from '../github-helpers/get-tab-count.js'; -import looseParseInt from '../helpers/loose-parse-int.js'; +import { buildRepoUrl, cacheByRepo, getRepo } from '../github-helpers/index.js'; import abbreviateNumber from '../helpers/abbreviate-number.js'; -import {buildRepoUrl, cacheByRepo, getRepo} from '../github-helpers/index.js'; -import {unhideOverflowDropdown} from './more-dropdown-links.js'; +import fetchDom from '../helpers/fetch-dom.js'; +import looseParseInt from '../helpers/loose-parse-int.js'; +import { unhideOverflowDropdown } from './more-dropdown-links.js'; async function canUserEditOrganization(): Promise { return Boolean(await elementReady('.btn-primary[href$="repositories/new"]')); @@ -30,8 +30,8 @@ function mustKeepTab(tab: HTMLElement): boolean { function setTabCounter(tab: HTMLElement, count: number): void { let tabCounter = $optional('.Counter, .num', tab); if (!tabCounter) { - tabCounter = as HTMLSpanElement; - tab.append({tabCounter}); + tabCounter = as HTMLSpanElement; + tab.append({tabCounter}); } tabCounter.textContent = abbreviateNumber(count); @@ -65,8 +65,8 @@ const wikiPageCount = new CachedFunction('wiki-page-count', { return countElements('#wiki-content > .Box .Box-row', dom); }, - maxAge: {hours: 1}, - staleWhileRevalidate: {days: 5}, + maxAge: { hours: 1 }, + staleWhileRevalidate: { days: 5 }, cacheKey: cacheByRepo, }); @@ -74,8 +74,8 @@ const hasActionRuns = new CachedFunction('workflows-count', { async updater(repoWithOwner: string): Promise { return api.v3hasAnyItems(`/repos/${repoWithOwner}/actions/runs`); }, - maxAge: {days: 1}, - staleWhileRevalidate: {days: 10}, + maxAge: { days: 1 }, + staleWhileRevalidate: { days: 10 }, }); async function updateWikiTab(): Promise { diff --git a/source/features/clear-pr-merge-commit-message.tsx b/source/features/clear-pr-merge-commit-message.tsx index 27d4b211e466..775d478bd6dc 100644 --- a/source/features/clear-pr-merge-commit-message.tsx +++ b/source/features/clear-pr-merge-commit-message.tsx @@ -1,14 +1,14 @@ import React from 'dom-chef'; -import {countElements} from 'select-dom'; import * as pageDetect from 'github-url-detection'; +import { countElements } from 'select-dom'; import features from '../feature-manager.js'; -import {getBranches} from '../github-helpers/pr-branches.js'; import getDefaultBranch from '../github-helpers/get-default-branch.js'; -import cleanCommitMessage from '../helpers/clean-commit-message.js'; -import {userHasPushAccess} from '../github-helpers/get-user-permission.js'; -import {expectToken} from '../github-helpers/github-token.js'; +import { userHasPushAccess } from '../github-helpers/get-user-permission.js'; +import { expectToken } from '../github-helpers/github-token.js'; +import { getBranches } from '../github-helpers/pr-branches.js'; import attachElement from '../helpers/attach-element.js'; +import cleanCommitMessage from '../helpers/clean-commit-message.js'; import observe from '../helpers/selector-observer.js'; const isPrAgainstDefaultBranch = async (): Promise => getBranches().base.branch === await getDefaultBranch(); @@ -25,15 +25,22 @@ async function clear(messageField: HTMLTextAreaElement): Promise { messageField.value = cleanedMessage ? cleanedMessage + '\n' : ''; // Trigger `fit-textareas` if enabled - messageField.dispatchEvent(new Event('input', {bubbles: true})); + messageField.dispatchEvent(new Event('input', { bubbles: true })); const anchor = messageField.closest('div[data-has-label]')!; attachElement(anchor, { after: () => ( -
    -

    - The description field was cleared by Refined GitHub. +

    +

    + The description field was cleared by{' '} + + Refined GitHub + .

    ), @@ -42,7 +49,7 @@ async function clear(messageField: HTMLTextAreaElement): Promise { async function init(signal: AbortSignal): Promise { await expectToken(); - observe('textarea[placeholder="Add an optional extended description…"]', clear, {signal}); + observe('textarea[placeholder="Add an optional extended description…"]', clear, { signal }); } void features.add(import.meta.url, { diff --git a/source/features/click-outside-modal.tsx b/source/features/click-outside-modal.tsx index 7e3c69a48595..c7d541b12e96 100644 --- a/source/features/click-outside-modal.tsx +++ b/source/features/click-outside-modal.tsx @@ -1,17 +1,17 @@ +import delegate, { type DelegateEvent } from 'delegate-it'; import * as pageDetect from 'github-url-detection'; -import delegate, {type DelegateEvent} from 'delegate-it'; import features from '../feature-manager.js'; -function onButtonClick({delegateTarget: delegate, target}: DelegateEvent): void { +function onButtonClick({ delegateTarget: delegate, target }: DelegateEvent): void { // Only close if clicking outside of modal if (delegate === target) { - delegate.dispatchEvent(new KeyboardEvent('keydown', {bubbles: true, key: 'Escape', code: 'Escape'})); + delegate.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: 'Escape', code: 'Escape' })); } } function init(signal: AbortSignal): void { - delegate('[class*="Dialog__Backdrop-"]', 'click', onButtonClick, {signal}); + delegate('[class*="Dialog__Backdrop-"]', 'click', onButtonClick, { signal }); } void features.add(import.meta.url, { diff --git a/source/features/close-as-unplanned.tsx b/source/features/close-as-unplanned.tsx index 6e89a83955aa..c57d9d541c13 100644 --- a/source/features/close-as-unplanned.tsx +++ b/source/features/close-as-unplanned.tsx @@ -1,10 +1,10 @@ -import delegate, {type DelegateEvent} from 'delegate-it'; +import delegate, { type DelegateEvent } from 'delegate-it'; import * as pageDetect from 'github-url-detection'; -import {$} from 'select-dom/strict.js'; +import { $ } from 'select-dom/strict.js'; import features from '../feature-manager.js'; +import { getFeatureId } from '../helpers/feature-helpers.js'; import observe from '../helpers/selector-observer.js'; -import {getFeatureId} from '../helpers/feature-helpers.js'; const id = getFeatureId(import.meta.url); @@ -35,13 +35,13 @@ function update(dropdown: HTMLElement): void { form.append(checkbox); } -function updateCheckbox({delegateTarget: button}: DelegateEvent): void { +function updateCheckbox({ delegateTarget: button }: DelegateEvent): void { $(unplannedCheckbox, button.form!).checked = button.id === id; } function init(signal: AbortSignal): void { - observe('close-reason-selector .select-menu', update, {signal}); - delegate('[name="comment_and_close"]', 'click', updateCheckbox, {signal}); + observe('close-reason-selector .select-menu', update, { signal }); + delegate('[name="comment_and_close"]', 'click', updateCheckbox, { signal }); } void features.add(import.meta.url, { diff --git a/source/features/close-out-of-view-modals.tsx b/source/features/close-out-of-view-modals.tsx index 6b8b3d9c24fb..5155e1030460 100644 --- a/source/features/close-out-of-view-modals.tsx +++ b/source/features/close-out-of-view-modals.tsx @@ -1,14 +1,14 @@ -import {$$} from 'select-dom'; -import delegate, {type DelegateEvent} from 'delegate-it'; +import delegate, { type DelegateEvent } from 'delegate-it'; +import { $$ } from 'select-dom'; -import onetime from '../helpers/onetime.js'; import features from '../feature-manager.js'; -import {getFeatureId} from '../helpers/feature-helpers.js'; +import { getFeatureId } from '../helpers/feature-helpers.js'; +import onetime from '../helpers/onetime.js'; const visible = new Set(); const observer = new IntersectionObserver(entries => { let lastModal: Element; - for (const {intersectionRatio, target: modal} of entries) { + for (const { intersectionRatio, target: modal } of entries) { if (intersectionRatio > 0) { visible.add(modal); } else { @@ -52,7 +52,7 @@ function menuActivatedHandler(event: DelegateEvent): void { } function initOnce(): void { - delegate('.details-overlay', 'toggle', menuActivatedHandler, {capture: true, signal: safetySwitch.signal}); + delegate('.details-overlay', 'toggle', menuActivatedHandler, { capture: true, signal: safetySwitch.signal }); } void features.add(import.meta.url, { diff --git a/source/features/closing-remarks.tsx b/source/features/closing-remarks.tsx index ddce41318aeb..caf00189ce6c 100644 --- a/source/features/closing-remarks.tsx +++ b/source/features/closing-remarks.tsx @@ -1,30 +1,31 @@ import React from 'dom-chef'; -import {CachedFunction} from 'webext-storage-cache'; -import {$$} from 'select-dom'; -import {$} from 'select-dom/strict.js'; +import { $$ } from 'select-dom'; +import { $ } from 'select-dom/strict.js'; +import { CachedFunction } from 'webext-storage-cache'; -import TagIcon from 'octicons-plain-react/Tag'; import * as pageDetect from 'github-url-detection'; +import TagIcon from 'octicons-plain-react/Tag'; import features from '../feature-manager.js'; -import fetchDom from '../helpers/fetch-dom.js'; import waitForPrMerge from '../github-events/on-pr-merge.js'; -import createBanner, {type BannerProps} from '../github-helpers/banner.js'; +import createBanner, { type BannerProps } from '../github-helpers/banner.js'; +import { userHasPushAccess } from '../github-helpers/get-user-permission.js'; +import { buildRepoUrl, getRepo, isRefinedGitHubRepo } from '../github-helpers/index.js'; import TimelineItem from '../github-helpers/timeline-item.js'; import attachElement from '../helpers/attach-element.js'; -import {buildRepoUrl, getRepo, isRefinedGitHubRepo} from '../github-helpers/index.js'; -import {getReleases} from './releases-tab.js'; +import fetchDom from '../helpers/fetch-dom.js'; import observe from '../helpers/selector-observer.js'; -import {userHasPushAccess} from '../github-helpers/get-user-permission.js'; +import { getReleases } from './releases-tab.js'; -function excludeNightliesAndJunk({textContent}: HTMLAnchorElement): boolean { +function excludeNightliesAndJunk({ textContent }: HTMLAnchorElement): boolean { // https://github.com/refined-github/refined-github/issues/7206 return !textContent.includes('nightly') && /\d[.]\d/.test(textContent); } +// dprint-ignore function ExplanationLink(): JSX.Element { return ( - + ); } @@ -47,7 +48,8 @@ function createReleaseUrl(): string { } async function init(signal: AbortSignal): Promise { - const mergeCommit = $(`.TimelineItem.js-details-container.Details a[href^="/${getRepo()!.nameWithOwner}/commit/" i] > code`).textContent; + const mergeCommit = + $(`.TimelineItem.js-details-container.Details a[href^="/${getRepo()!.nameWithOwner}/commit/" i] > code`).textContent; const tagName = await firstTag.get(mergeCommit); if (tagName) { @@ -57,35 +59,49 @@ async function init(signal: AbortSignal): Promise { addExistingTagLinkFooter(tagName, tagUrl); // PRs have a regular and a sticky header - observe('#partial-discussion-header relative-time', addExistingTagLinkToHeader.bind(undefined, tagName, tagUrl), {signal}); + observe('#partial-discussion-header relative-time', addExistingTagLinkToHeader.bind(undefined, tagName, tagUrl), { + signal, + }); } else { - void addReleaseBanner(<>This PR seems to be not yet released.); + void addReleaseBanner( + ( + <> + This PR seems to be not yet released. + + ), + ); } } function addExistingTagLinkToHeader(tagName: string, tagUrl: string, discussionHeader: HTMLElement): void { discussionHeader.parentElement!.append( - - - - {tagName} - - , + ( + + + + {tagName} + + + ), ); } function addExistingTagLinkFooter(tagName: string, tagUrl: string): void { - const linkedTag = {tagName}; + const linkedTag = {tagName}; attachElement($('#issue-comment-box'), { before: () => ( {createBanner({ - icon: , - text: <>This pull request first appeared in {linkedTag}, + icon: , + text: ( + <> + This pull request first appeared in {linkedTag} + + ), classes: ['flash-success', 'rgh-bg-none'], })} @@ -102,7 +118,7 @@ async function addReleaseBanner(text: string | JSX.Element): Promise { const url = createReleaseUrl(); const bannerContent = { text, - icon: , + icon: , classes: ['rgh-bg-none'], } satisfies BannerProps; diff --git a/source/features/collapsible-content-button.tsx b/source/features/collapsible-content-button.tsx index 7da1bce92e1d..28f32e871a63 100644 --- a/source/features/collapsible-content-button.tsx +++ b/source/features/collapsible-content-button.tsx @@ -1,17 +1,17 @@ +import delegate, { type DelegateEvent } from 'delegate-it'; import React from 'dom-chef'; -import FoldDownIcon from 'octicons-plain-react/FoldDown'; import * as pageDetect from 'github-url-detection'; -import {insertTextIntoField} from 'text-field-edit'; -import delegate, {type DelegateEvent} from 'delegate-it'; -import {$} from 'select-dom/strict.js'; +import FoldDownIcon from 'octicons-plain-react/FoldDown'; +import { $ } from 'select-dom/strict.js'; +import { insertTextIntoField } from 'text-field-edit'; import features from '../feature-manager.js'; -import smartBlockWrap from '../helpers/smart-block-wrap.js'; +import { triggerActionBarOverflow } from '../github-helpers/index.js'; +import { actionBarSelectors } from '../github-helpers/selectors.js'; import observe from '../helpers/selector-observer.js'; -import {triggerActionBarOverflow} from '../github-helpers/index.js'; -import {actionBarSelectors} from '../github-helpers/selectors.js'; +import smartBlockWrap from '../helpers/smart-block-wrap.js'; -function addContentToDetails({delegateTarget}: DelegateEvent): void { +function addContentToDetails({ delegateTarget }: DelegateEvent): void { const container = delegateTarget.closest([ 'form', '[data-testid="comment-composer"]', // Add comment form @@ -65,14 +65,16 @@ function append(container: HTMLElement): void { container.append( divider, - , + ( + + ), ); if (container.getAttribute('aria-label') === 'Formatting tools') { @@ -85,8 +87,8 @@ function append(container: HTMLElement): void { } function init(signal: AbortSignal): void { - observe(actionBarSelectors, append, {signal}); - delegate('.rgh-collapsible-content-btn', 'click', addContentToDetails, {signal}); + observe(actionBarSelectors, append, { signal }); + delegate('.rgh-collapsible-content-btn', 'click', addContentToDetails, { signal }); } void features.add(import.meta.url, { diff --git a/source/features/command-palette-navigation-shortcuts.tsx b/source/features/command-palette-navigation-shortcuts.tsx index d2dbf52e5202..420db73f287f 100644 --- a/source/features/command-palette-navigation-shortcuts.tsx +++ b/source/features/command-palette-navigation-shortcuts.tsx @@ -1,11 +1,11 @@ -import delegate, {type DelegateEvent} from 'delegate-it'; +import delegate, { type DelegateEvent } from 'delegate-it'; -import {isMac} from '../github-helpers/index.js'; import features from '../feature-manager.js'; +import { isMac } from '../github-helpers/index.js'; import onetime from '../helpers/onetime.js'; function commandPaletteKeydown(event: DelegateEvent): void { - const {key, ctrlKey, delegateTarget} = event; + const { key, ctrlKey, delegateTarget } = event; if (!ctrlKey || (key !== 'n' && key !== 'p')) { return; @@ -14,7 +14,7 @@ function commandPaletteKeydown(event: DelegateEvent): void { event.preventDefault(); const targetKey = key === 'n' ? 'ArrowDown' : 'ArrowUp'; - delegateTarget.dispatchEvent(new KeyboardEvent('keydown', {bubbles: true, key: targetKey, code: targetKey})); + delegateTarget.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: targetKey, code: targetKey })); } function initOnce(): void { diff --git a/source/features/comment-excess.tsx b/source/features/comment-excess.tsx index 7a110e78c5d6..5ada7b2119c6 100644 --- a/source/features/comment-excess.tsx +++ b/source/features/comment-excess.tsx @@ -1,13 +1,13 @@ -import React from 'react'; -import * as pageDetect from 'github-url-detection'; import elementReady from 'element-ready'; -import {$, $optional} from 'select-dom/strict.js'; +import * as pageDetect from 'github-url-detection'; +import React from 'react'; +import { $, $optional } from 'select-dom/strict.js'; import features from '../feature-manager.js'; +import { isMac, scrollIntoViewIfNeeded } from '../github-helpers/index.js'; +import { paginationButtonSelector } from '../github-helpers/selectors.js'; +import { assertNodeContent } from '../helpers/dom-utils.js'; import observe from '../helpers/selector-observer.js'; -import {assertNodeContent} from '../helpers/dom-utils.js'; -import {paginationButtonSelector} from '../github-helpers/selectors.js'; -import {isMac, scrollIntoViewIfNeeded} from '../github-helpers/index.js'; const hiddenCommentsForm = '#js-progressive-timeline-item-container'; @@ -33,7 +33,7 @@ function addIndicator(headerCommentCount: HTMLSpanElement): void { const spacer = new Text(' · '); const link = ( { // The count will be outdated after the first expansion. We can remove it until the next header update by GitHub. @@ -45,14 +45,13 @@ function addIndicator(headerCommentCount: HTMLSpanElement): void { ); headerCommentCount.append(spacer); - headerCommentCount.after(link, - ); + headerCommentCount.after(link); } async function init(signal: AbortSignal): Promise { if (await elementReady(`${hiddenCommentsForm} ${paginationButtonSelector}`)) { - observe('.gh-header-meta relative-time + span', addIndicator, {signal}); - globalThis.addEventListener('keydown', scrollOnSearch, {signal}); + observe('.gh-header-meta relative-time + span', addIndicator, { signal }); + globalThis.addEventListener('keydown', scrollOnSearch, { signal }); } } diff --git a/source/features/comments-time-machine-links.tsx b/source/features/comments-time-machine-links.tsx index 1c3a333c4f53..50bf7c2e8465 100644 --- a/source/features/comments-time-machine-links.tsx +++ b/source/features/comments-time-machine-links.tsx @@ -1,21 +1,21 @@ +import delegate, { type DelegateEvent } from 'delegate-it'; import React from 'dom-chef'; -import {$} from 'select-dom/strict.js'; import elementReady from 'element-ready'; import * as pageDetect from 'github-url-detection'; -import delegate, {type DelegateEvent} from 'delegate-it'; import HistoryIcon from 'octicons-plain-react/History'; +import { $ } from 'select-dom/strict.js'; import features from '../feature-manager.js'; import api from '../github-helpers/api.js'; +import { linkifiedUrlClass } from '../github-helpers/dom-formatters.js'; +import getDefaultBranch from '../github-helpers/get-default-branch.js'; import GitHubFileUrl from '../github-helpers/github-file-url.js'; +import { expectToken } from '../github-helpers/github-token.js'; +import { buildRepoUrl, isPermalink } from '../github-helpers/index.js'; import addNotice from '../github-widgets/notice-bar.js'; -import {linkifiedUrlClass} from '../github-helpers/dom-formatters.js'; -import {buildRepoUrl, isPermalink} from '../github-helpers/index.js'; -import {saveOriginalHref} from './sort-conversations-by-update-time.js'; import observe from '../helpers/selector-observer.js'; import GetCommitAtDate from './comments-time-machine-links.gql'; -import {expectToken} from '../github-helpers/github-token.js'; -import getDefaultBranch from '../github-helpers/get-default-branch.js'; +import { saveOriginalHref } from './sort-conversations-by-update-time.js'; const commentSelector = [ '.loaded .react-issue-body', // Issue description @@ -25,10 +25,10 @@ const commentSelector = [ ].join(','); async function updateUrltoDatedSha(url: GitHubFileUrl, date: string): Promise { - const {repository} = await api.v4(GetCommitAtDate, {variables: {date, branch: url.branch}}); + const { repository } = await api.v4(GetCommitAtDate, { variables: { date, branch: url.branch } }); - const [{oid}] = repository.ref.target.history.nodes; - $('a.rgh-link-date').pathname = url.assign({branch: oid}).pathname; + const [{ oid }] = repository.ref.target.history.nodes; + $('a.rgh-link-date').pathname = url.assign({ branch: oid }).pathname; } async function showTimeMachineBar(): Promise { @@ -50,7 +50,7 @@ async function showTimeMachineBar(): Promise { } // Selector note: isRepoFile and isRepoTree have different DOM for this element - const lastCommitDate = await elementReady('.Box-header relative-time', {waitForChildren: false}); + const lastCommitDate = await elementReady('.Box-header relative-time', { waitForChildren: false }); if (lastCommitDate && date > lastCommitDate.getAttribute('datetime')!) { return false; } @@ -72,12 +72,16 @@ async function showTimeMachineBar(): Promise { } const link = ( - + view this object as it appeared at the time of the comment ); await addNotice( - <>You can also {link} (), + ( + <> + You can also {link} () + + ), ); } @@ -105,20 +109,23 @@ function addDateParameterToLink(link: HTMLAnchorElement): void { function addDropdownLink(menu: HTMLElement, timestamp: string): void { $('.show-more-popover', menu.parentElement!).append( -
    , - - View repo at this time - , +
    , + ( + + View repo at this time + + ), ); } -function addDropdownLinkReact({delegateTarget: delegate}: DelegateEvent): void { - const timestamp = delegate.closest('[class^="Box"]')!.querySelector('relative-time[datetime]')!.attributes.datetime.value; +function addDropdownLinkReact({ delegateTarget: delegate }: DelegateEvent): void { + const timestamp = + delegate.closest('[class^="Box"]')!.querySelector('relative-time[datetime]')!.attributes.datetime.value; const menuItemList = $('[class^="prc-ActionList-ActionList"]'); const menuItem = $('[class^="prc-ActionList-ActionListItem"]', menuItemList).cloneNode(true); @@ -129,9 +136,9 @@ function addDropdownLinkReact({delegateTarget: delegate}: DelegateEvent): void { ); @@ -141,7 +148,7 @@ function addDropdownLinkReact({delegateTarget: delegate}: DelegateEvent): void { $('[class^="prc-ActionList-LeadingVisual"]', menuItem).replaceChildren(); menuItemList.append( -
  • - @@ -173,55 +174,58 @@ async function addWidget(state: State, anchor: HTMLElement): Promise { } await delay(100); // Let `clean-conversation-headers` run first - wrap(position,
    ); + wrap(position,
    ); position.classList.add('rgh-conversation-activity-filter'); const baseId = crypto.randomUUID(); const menu = ( 0 ? 'ml-2' : ''}`} - data-select-variant="single"> - + className={`rgh-conversation-activity-filter-menu d-inline-block position-relative lh-condensed-ultra v-align-middle ${ + position.offsetWidth > 0 ? 'ml-2' : '' + }`} + data-select-variant='single' + > + -
    -
    +
    +
    @@ -261,19 +265,23 @@ async function init(signal: AbortSignal): Promise { ? 'hideEventsAndCollapsedComments' // Automatically hide resolved comments on "Minor codebase updates and fixes" issue pages : 'showAll'); - observe([ - // Issue view - '[class^="HeaderMetadata-module__metadataContent"]', - '[class*="HeaderMetadata-module__smallMetadataRow"]', - // PR view - 'span[class*="PullRequestHeaderSummary-module"] > .d-flex', - // Old PR view - TODO: Remove after July 2026 - '#partial-discussion-header .gh-header-meta > .flex-auto:last-child', - '#partial-discussion-header .sticky-header-container .meta:last-child', - ], addWidget.bind(undefined, initialState), {signal}); - - globalThis.addEventListener('hashchange', uncollapseTargetedComment, {signal}); - observe(timelineItem, processItem, {signal}); + observe( + [ + // Issue view + '[class^="HeaderMetadata-module__metadataContent"]', + '[class*="HeaderMetadata-module__smallMetadataRow"]', + // PR view + 'span[class*="PullRequestHeaderSummary-module"] > .d-flex', + // Old PR view - TODO: Remove after July 2026 + '#partial-discussion-header .gh-header-meta > .flex-auto:last-child', + '#partial-discussion-header .sticky-header-container .meta:last-child', + ], + addWidget.bind(undefined, initialState), + { signal }, + ); + + globalThis.addEventListener('hashchange', uncollapseTargetedComment, { signal }); + observe(timelineItem, processItem, { signal }); delegate('.rgh-conversation-activity-filter-menu', 'itemActivated', handleSelection); if (initialState !== 'showAll') { @@ -283,7 +291,7 @@ async function init(signal: AbortSignal): Promise { applyState(initialState); } - registerHotkey('h', switchToNextFilter, {signal}); + registerHotkey('h', switchToNextFilter, { signal }); } void features.add(import.meta.url, { diff --git a/source/features/conversation-authors.tsx b/source/features/conversation-authors.tsx index b11f8868dd2d..c3db8911a14d 100644 --- a/source/features/conversation-authors.tsx +++ b/source/features/conversation-authors.tsx @@ -1,22 +1,22 @@ import './conversation-authors.css'; -import {CachedFunction} from 'webext-storage-cache'; import * as pageDetect from 'github-url-detection'; +import { CachedFunction } from 'webext-storage-cache'; import features from '../feature-manager.js'; import api from '../github-helpers/api.js'; -import {cacheByRepo, getLoggedInUser} from '../github-helpers/index.js'; +import { expectToken } from '../github-helpers/github-token.js'; +import { cacheByRepo, getLoggedInUser } from '../github-helpers/index.js'; import observe from '../helpers/selector-observer.js'; -import {expectToken} from '../github-helpers/github-token.js'; import GetCollaborators from './conversation-authors.gql'; const collaborators = new CachedFunction('repo-collaborators', { async updater(): Promise { - const {repository} = await api.v4(GetCollaborators); + const { repository } = await api.v4(GetCollaborators); return repository.collaborators.nodes.map((user: Record) => user.login); }, - maxAge: {days: 1}, - staleWhileRevalidate: {days: 20}, + maxAge: { days: 1 }, + staleWhileRevalidate: { days: 20 }, cacheKey: cacheByRepo, }); @@ -28,7 +28,7 @@ async function highlightCollaborators(signal: AbortSignal): Promise { if (list.includes(name) && name !== getLoggedInUser()) { author.classList.add('rgh-collaborator'); } - }, {signal}); + }, { signal }); } function highlightSelf(signal: AbortSignal): void { @@ -36,10 +36,12 @@ function highlightSelf(signal: AbortSignal): void { observe([ // PRs - TODO: Drop in 2026 `.opened-by a[title$="ed by ${CSS.escape(getLoggedInUser()!)}"]`, - `a[class^="IssueItem-module__authorCreatedLink"][data-hovercard-url="/users/${CSS.escape(getLoggedInUser()!)}/hovercard"]`, + `a[class^="IssueItem-module__authorCreatedLink"][data-hovercard-url="/users/${ + CSS.escape(getLoggedInUser()!) + }/hovercard"]`, ], author => { author.classList.add('rgh-own-conversation'); - }, {signal}); + }, { signal }); } void features.add(import.meta.url, { diff --git a/source/features/conversation-links-on-repo-lists.tsx b/source/features/conversation-links-on-repo-lists.tsx index 701de08671dc..2106e036b372 100644 --- a/source/features/conversation-links-on-repo-lists.tsx +++ b/source/features/conversation-links-on-repo-lists.tsx @@ -1,12 +1,12 @@ import React from 'dom-chef'; -import {$, $optional} from 'select-dom/strict.js'; import * as pageDetect from 'github-url-detection'; import GitPullRequestIcon from 'octicons-plain-react/GitPullRequest'; import IssueOpenedIcon from 'octicons-plain-react/IssueOpened'; +import { $, $optional } from 'select-dom/strict.js'; import features from '../feature-manager.js'; +import { assertNodeContent } from '../helpers/dom-utils.js'; import observe from '../helpers/selector-observer.js'; -import {assertNodeContent} from '../helpers/dom-utils.js'; function addConversationLinks(repositoryLink: HTMLAnchorElement): void { const repository = repositoryLink.closest('li')!; @@ -19,20 +19,22 @@ function addConversationLinks(repositoryLink: HTMLAnchorElement): void { $('relative-time', repository).previousSibling, 'Updated', ).before( - <> - - - - - - - , + ( + <> + + + + + + + + ), ); } @@ -49,39 +51,41 @@ function addSearchConversationLinks(repositoryLink: HTMLAnchorElement): void { .closest('[data-testid="results-list"] > div')! .querySelector('ul > span:last-of-type')! .before( - <> - -
  • - - - -
  • -
  • - + -
  • - , + · + +
  • + + + +
  • +
  • + + + +
  • + + ), ); } function init(signal: AbortSignal): void { - observe('a[itemprop="name codeRepository"]', addConversationLinks, {signal}); + observe('a[itemprop="name codeRepository"]', addConversationLinks, { signal }); } function initSearch(signal: AbortSignal): void { - observe('.search-title a', addSearchConversationLinks, {signal}); + observe('.search-title a', addSearchConversationLinks, { signal }); } void features.add(import.meta.url, { diff --git a/source/features/convert-release-to-draft.tsx b/source/features/convert-release-to-draft.tsx index c98599c0c5c0..ed9c5a605aa8 100644 --- a/source/features/convert-release-to-draft.tsx +++ b/source/features/convert-release-to-draft.tsx @@ -1,15 +1,15 @@ +import delegate from 'delegate-it'; import React from 'dom-chef'; -import {$} from 'select-dom/strict.js'; -import {elementExists} from 'select-dom'; import * as pageDetect from 'github-url-detection'; -import delegate from 'delegate-it'; +import { elementExists } from 'select-dom'; +import { $ } from 'select-dom/strict.js'; import features from '../feature-manager.js'; import api from '../github-helpers/api.js'; -import {getRepo} from '../github-helpers/index.js'; -import observe from '../helpers/selector-observer.js'; +import { expectToken } from '../github-helpers/github-token.js'; +import { getRepo } from '../github-helpers/index.js'; import showToast from '../github-helpers/toast.js'; -import {expectToken} from '../github-helpers/github-token.js'; +import observe from '../helpers/selector-observer.js'; const getReleaseEditLinkSelector = (): 'a' => `a[href^="/${getRepo()!.nameWithOwner}/releases/edit"]` as 'a'; @@ -50,20 +50,22 @@ function attachButton(editButton: HTMLAnchorElement): void { } editButton.before( - , + ( + + ), ); } async function init(signal: AbortSignal): Promise { await expectToken(); - observe(getReleaseEditLinkSelector(), attachButton, {signal}); - delegate('.rgh-convert-draft', 'click', onConvertClick, {signal}); + observe(getReleaseEditLinkSelector(), attachButton, { signal }); + delegate('.rgh-convert-draft', 'click', onConvertClick, { signal }); } void features.add(import.meta.url, { diff --git a/source/features/copy-on-y.tsx b/source/features/copy-on-y.tsx index 16808b282775..25e9924afe1a 100644 --- a/source/features/copy-on-y.tsx +++ b/source/features/copy-on-y.tsx @@ -1,7 +1,7 @@ import features from '../feature-manager.js'; -import {isEditable} from '../helpers/dom-utils.js'; +import { isEditable } from '../helpers/dom-utils.js'; -async function handler({key, target}: KeyboardEvent): Promise { +async function handler({ key, target }: KeyboardEvent): Promise { if (key === 'y' && !isEditable(target)) { const url = location.href; await navigator.clipboard.writeText(url); @@ -11,7 +11,7 @@ async function handler({key, target}: KeyboardEvent): Promise { } function init(signal: AbortSignal): void { - globalThis.addEventListener('keyup', handler, {signal}); + globalThis.addEventListener('keyup', handler, { signal }); } void features.add(import.meta.url, { diff --git a/source/features/create-release-shortcut.tsx b/source/features/create-release-shortcut.tsx index a0b5521362a9..3803cf803b70 100644 --- a/source/features/create-release-shortcut.tsx +++ b/source/features/create-release-shortcut.tsx @@ -1,12 +1,12 @@ import * as pageDetect from 'github-url-detection'; import features from '../feature-manager.js'; -import {registerHotkey} from '../github-helpers/hotkey.js'; -import {buildRepoUrl} from '../github-helpers/index.js'; +import { registerHotkey } from '../github-helpers/hotkey.js'; +import { buildRepoUrl } from '../github-helpers/index.js'; function init(signal: AbortSignal): void { // Reasoning for this feature: #1254 - registerHotkey('c', buildRepoUrl('releases/new'), {signal}); + registerHotkey('c', buildRepoUrl('releases/new'), { signal }); } void features.add(import.meta.url, { diff --git a/source/features/cross-deleted-pr-branches.tsx b/source/features/cross-deleted-pr-branches.tsx index 416a44197d90..69defebde869 100644 --- a/source/features/cross-deleted-pr-branches.tsx +++ b/source/features/cross-deleted-pr-branches.tsx @@ -1,12 +1,12 @@ import './cross-deleted-pr-branches.css'; import React from 'dom-chef'; -import {$$, lastElement} from 'select-dom'; -import {$, $optional} from 'select-dom/strict.js'; import * as pageDetect from 'github-url-detection'; +import { $$, lastElement } from 'select-dom'; +import { $, $optional } from 'select-dom/strict.js'; -import {wrap} from '../helpers/dom-utils.js'; import features from '../feature-manager.js'; +import { wrap } from '../helpers/dom-utils.js'; function init(): void | false { const lastBranchAction = lastElement('.TimelineItem-body .user-select-contain.commit-ref'); diff --git a/source/features/deep-reblame.tsx b/source/features/deep-reblame.tsx index 5be99f46a222..26db7e9266dc 100644 --- a/source/features/deep-reblame.tsx +++ b/source/features/deep-reblame.tsx @@ -1,49 +1,53 @@ import './deep-reblame.css'; -import mem from 'memoize'; +import delegate, { type DelegateEvent } from 'delegate-it'; import React from 'dom-chef'; -import {$$} from 'select-dom'; -import {$, $optional} from 'select-dom/strict.js'; -import VersionsIcon from 'octicons-plain-react/Versions'; import * as pageDetect from 'github-url-detection'; -import delegate, {type DelegateEvent} from 'delegate-it'; +import mem from 'memoize'; +import VersionsIcon from 'octicons-plain-react/Versions'; +import { $$ } from 'select-dom'; +import { $, $optional } from 'select-dom/strict.js'; import features from '../feature-manager.js'; import api from '../github-helpers/api.js'; import GitHubFileUrl from '../github-helpers/github-file-url.js'; +import { expectToken } from '../github-helpers/github-token.js'; +import { multilineAriaLabel } from '../github-helpers/index.js'; import showToast from '../github-helpers/toast.js'; import looseParseInt from '../helpers/loose-parse-int.js'; import observe from '../helpers/selector-observer.js'; import GetPullRequestBlameCommit from './deep-reblame.gql'; -import {multilineAriaLabel} from '../github-helpers/index.js'; -import {expectToken} from '../github-helpers/github-token.js'; - -const getPullRequestBlameCommit = mem(async (commit: string, prNumbers: number[], currentFilename: string): Promise => { - const {repository} = await api.v4(GetPullRequestBlameCommit, { - variables: { - commit, - file: commit + ':' + currentFilename, - }, - }); - const associatedPr = repository.object.associatedPullRequests.nodes[0]; +const getPullRequestBlameCommit = mem( + async (commit: string, prNumbers: number[], currentFilename: string): Promise => { + const { repository } = await api.v4(GetPullRequestBlameCommit, { + variables: { + commit, + file: commit + ':' + currentFilename, + }, + }); - if (!associatedPr || !prNumbers.includes(associatedPr.number) || associatedPr.mergeCommit.oid !== commit) { - throw new Error('The PR linked in the title didn’t create this commit'); - } + const associatedPr = repository.object.associatedPullRequests.nodes[0]; - if (!repository.file) { - throw new Error('The file was renamed and Refined GitHub can’t find it'); - } + if (!associatedPr || !prNumbers.includes(associatedPr.number) || associatedPr.mergeCommit.oid !== commit) { + throw new Error('The PR linked in the title didn’t create this commit'); + } - return associatedPr.commits.nodes[0].commit.oid; -}); + if (!repository.file) { + throw new Error('The file was renamed and Refined GitHub can’t find it'); + } + + return associatedPr.commits.nodes[0].commit.oid; + }, +); function extractCommitFromHoverCardUrl(url: string): string { return /[/]commit[/]([0-9a-f]{40})[/]/i.exec(url)![1]; } -async function redirectToBlameCommit(event: DelegateEvent): Promise { +async function redirectToBlameCommit( + event: DelegateEvent, +): Promise { const blameElement = event.delegateTarget; if (blameElement instanceof HTMLAnchorElement && !event.altKey) { return; // Unmodified click on regular link: let it proceed @@ -71,20 +75,25 @@ async function redirectToBlameCommit(event: DelegateEvent - - , + ( + + ), ); } } @@ -92,8 +101,8 @@ function addButton(hunk: HTMLElement): void { async function init(signal: AbortSignal): Promise { await expectToken(); - delegate('.rgh-deep-reblame', 'click', redirectToBlameCommit, {signal}); - observe('.react-blame-for-range:has([data-hovercard-type="pull_request"])', addButton, {signal}); + delegate('.rgh-deep-reblame', 'click', redirectToBlameCommit, { signal }); + observe('.react-blame-for-range:has([data-hovercard-type="pull_request"])', addButton, { signal }); } void features.add(import.meta.url, { diff --git a/source/features/default-branch-button.tsx b/source/features/default-branch-button.tsx index 6fa55f3551e9..5ad30ac17b29 100644 --- a/source/features/default-branch-button.tsx +++ b/source/features/default-branch-button.tsx @@ -2,19 +2,19 @@ import './default-branch-button.css'; import React from 'dom-chef'; import * as pageDetect from 'github-url-detection'; -import ChevronLeftIcon from 'octicons-plain-react/ChevronLeft'; -import {$optional} from 'select-dom/strict.js'; import memoize from 'memoize'; +import ChevronLeftIcon from 'octicons-plain-react/ChevronLeft'; +import { $optional } from 'select-dom/strict.js'; import features from '../feature-manager.js'; -import GitHubFileUrl from '../github-helpers/github-file-url.js'; -import {groupButtons} from '../github-helpers/group-buttons.js'; import getDefaultBranch from '../github-helpers/get-default-branch.js'; -import observe from '../helpers/selector-observer.js'; -import {branchSelector} from '../github-helpers/selectors.js'; +import GitHubFileUrl from '../github-helpers/github-file-url.js'; +import { expectToken } from '../github-helpers/github-token.js'; +import { groupButtons } from '../github-helpers/group-buttons.js'; +import { fixFileHeaderOverlap, isRepoCommitListRoot } from '../github-helpers/index.js'; import isDefaultBranch from '../github-helpers/is-default-branch.js'; -import {fixFileHeaderOverlap, isRepoCommitListRoot} from '../github-helpers/index.js'; -import {expectToken} from '../github-helpers/github-token.js'; +import { branchSelector } from '../github-helpers/selectors.js'; +import observe from '../helpers/selector-observer.js'; const getUrl = memoize(async (currentUrl: string): Promise => { const defaultUrl = new GitHubFileUrl(currentUrl); @@ -64,14 +64,13 @@ async function add(branchSelector: HTMLElement): Promise { const defaultLink = ( @@ -85,7 +84,7 @@ async function add(branchSelector: HTMLElement): Promise { async function init(signal: AbortSignal): Promise { await expectToken(); - observe(branchSelector, add, {signal}); + observe(branchSelector, add, { signal }); } void features.add(import.meta.url, { diff --git a/source/features/dim-bots.tsx b/source/features/dim-bots.tsx index 1f09f55a6799..cf00eb29b1e4 100644 --- a/source/features/dim-bots.tsx +++ b/source/features/dim-bots.tsx @@ -1,14 +1,14 @@ import './dim-bots.css'; -import {$$} from 'select-dom'; +import delegate, { type DelegateEvent } from 'delegate-it'; import * as pageDetect from 'github-url-detection'; -import delegate, {type DelegateEvent} from 'delegate-it'; +import { $$ } from 'select-dom'; import features from '../feature-manager.js'; +import { botLinksCommitSelectors, botLinksPrSelectors } from '../github-helpers/selectors.js'; +import { getIdentifiers } from '../helpers/feature-helpers.js'; import preserveScroll from '../helpers/preserve-scroll.js'; import observe from '../helpers/selector-observer.js'; -import {botLinksCommitSelectors, botLinksPrSelectors} from '../github-helpers/selectors.js'; -import {getIdentifiers} from '../helpers/feature-helpers.js'; const botLinksCommitSelectorsExceptCopilot = botLinksCommitSelectors.map( selector => `${selector}:not([href*="copilot"])`, @@ -42,10 +42,10 @@ function dim(commit: HTMLElement): void { } async function init(signal: AbortSignal): Promise { - observe([...botLinksCommitSelectorsExceptCopilot, ...botLinksPrSelectors], dim, {signal}); + observe([...botLinksCommitSelectorsExceptCopilot, ...botLinksPrSelectors], dim, { signal }); // Undim on mouse focus - delegate(dimBots.selector, 'click', undimBots, {signal}); + delegate(dimBots.selector, 'click', undimBots, { signal }); } void features.add(import.meta.url, { diff --git a/source/features/download-folder-button.tsx b/source/features/download-folder-button.tsx index 6b044290a42d..2f2296c010ca 100644 --- a/source/features/download-folder-button.tsx +++ b/source/features/download-folder-button.tsx @@ -1,11 +1,11 @@ import React from 'dom-chef'; -import {$, $optional} from 'select-dom/strict.js'; import * as pageDetect from 'github-url-detection'; import DownloadIcon from 'octicons-plain-react/Download'; +import { $, $optional } from 'select-dom/strict.js'; import features from '../feature-manager.js'; -import observe from '../helpers/selector-observer.js'; import replaceElementTypeInPlace from '../helpers/recreate-element.js'; +import observe from '../helpers/selector-observer.js'; function add(menu: HTMLUListElement): void { const downloadUrl = new URL('https://download-directory.github.io/'); @@ -38,7 +38,7 @@ function add(menu: HTMLUListElement): void { } function init(signal: AbortSignal): void { - observe('ul[role="menu"]:has([aria-keyshortcuts="c"])', add, {signal}); + observe('ul[role="menu"]:has([aria-keyshortcuts="c"])', add, { signal }); } void features.add(import.meta.url, { diff --git a/source/features/easy-toggle-commit-messages.tsx b/source/features/easy-toggle-commit-messages.tsx index dd57593a836d..73b3a3a71272 100644 --- a/source/features/easy-toggle-commit-messages.tsx +++ b/source/features/easy-toggle-commit-messages.tsx @@ -1,6 +1,6 @@ -import {$optional} from 'select-dom/strict.js'; +import delegate, { type DelegateEvent } from 'delegate-it'; import * as pageDetect from 'github-url-detection'; -import delegate, {type DelegateEvent} from 'delegate-it'; +import { $optional } from 'select-dom/strict.js'; import features from '../feature-manager.js'; @@ -23,7 +23,7 @@ function toggleCommitMessage(event: DelegateEvent): void { '[data-testid="latest-commit-details-toggle"]', // File/folder '.ellipsis-expander', // Compare ], event.delegateTarget)?.dispatchEvent( - new MouseEvent('click', {bubbles: true, altKey: event.altKey}), + new MouseEvent('click', { bubbles: true, altKey: event.altKey }), ); } @@ -34,7 +34,7 @@ const commitMessagesSelector = [ ]; function init(signal: AbortSignal): void { - delegate(commitMessagesSelector, 'click', toggleCommitMessage, {signal}); + delegate(commitMessagesSelector, 'click', toggleCommitMessage, { signal }); } void features.add(import.meta.url, { diff --git a/source/features/easy-toggle-files.tsx b/source/features/easy-toggle-files.tsx index e07d9b1bf642..0a6cde3618e8 100644 --- a/source/features/easy-toggle-files.tsx +++ b/source/features/easy-toggle-files.tsx @@ -1,9 +1,9 @@ -import {$} from 'select-dom/strict.js'; -import delegate, {type DelegateEvent} from 'delegate-it'; +import delegate, { type DelegateEvent } from 'delegate-it'; import * as pageDetect from 'github-url-detection'; +import { $ } from 'select-dom/strict.js'; -import {codeSearchHeader} from '../github-helpers/selectors.js'; import features from '../feature-manager.js'; +import { codeSearchHeader } from '../github-helpers/selectors.js'; function toggleFile(event: DelegateEvent): void { const elementClicked = event.target as HTMLElement; @@ -12,7 +12,7 @@ function toggleFile(event: DelegateEvent): void { // Exclude interactive elements if (!elementClicked.closest(['a', 'button', 'clipboard-copy', 'details'])) { $('button:has(> .octicon-chevron-down, > .octicon-chevron-right)', headerBar) - .dispatchEvent(new MouseEvent('click', {bubbles: true, altKey: event.altKey})); + .dispatchEvent(new MouseEvent('click', { bubbles: true, altKey: event.altKey })); } } @@ -23,20 +23,25 @@ function toggleCodeSearchFile(event: DelegateEvent): void { // The clicked element is either the bar itself or one of its children excluding the button if (elementClicked === headerBar || (elementClicked.parentElement === headerBar && elementClicked !== toggle)) { - toggle.dispatchEvent(new MouseEvent('click', {bubbles: true, altKey: event.altKey})); + toggle.dispatchEvent(new MouseEvent('click', { bubbles: true, altKey: event.altKey })); } } function init(signal: AbortSignal): void { - delegate([ - '.file-header', - // React - '[class^="Diff-module__diffHeaderWrapper"]', - ], 'click', toggleFile, {signal}); + delegate( + [ + '.file-header', + // React + '[class^="Diff-module__diffHeaderWrapper"]', + ], + 'click', + toggleFile, + { signal }, + ); } function initSearchPage(signal: AbortSignal): void { - delegate(codeSearchHeader, 'click', toggleCodeSearchFile, {signal}); + delegate(codeSearchHeader, 'click', toggleCodeSearchFile, { signal }); } void features.add(import.meta.url, { diff --git a/source/features/embed-gist-inline.tsx b/source/features/embed-gist-inline.tsx index 91bc13e74161..eff3371d6e6f 100644 --- a/source/features/embed-gist-inline.tsx +++ b/source/features/embed-gist-inline.tsx @@ -2,11 +2,11 @@ import React from 'dom-chef'; import domify from 'doma'; import * as pageDetect from 'github-url-detection'; import mem from 'memoize'; -import {messageRuntime} from 'webext-msg'; +import { messageRuntime } from 'webext-msg'; import features from '../feature-manager.js'; +import { standaloneGistLinkInMarkdown } from '../github-helpers/selectors.js'; import observe from '../helpers/selector-observer.js'; -import {standaloneGistLinkInMarkdown} from '../github-helpers/selectors.js'; type GistData = { div: string; @@ -16,14 +16,14 @@ type GistData = { // Fetch via background.js due to CORB policies. Also memoize to avoid multiple requests. const fetchGist = mem( - async (url: string): Promise => - messageRuntime({fetchJson: `${url}.json`}), + async (url: string): Promise => messageRuntime({ fetchJson: `${url}.json` }), ); -const isOnlyChild = (link: HTMLAnchorElement): boolean => link.textContent.trim() === link.parentElement!.textContent.trim(); +const isOnlyChild = (link: HTMLAnchorElement): boolean => + link.textContent.trim() === link.parentElement!.textContent.trim(); async function embedGist(link: HTMLAnchorElement): Promise { - const info = (loading); + const info = (loading); link.after(info); try { @@ -38,15 +38,18 @@ async function embedGist(link: HTMLAnchorElement): Promise { info.textContent = ` (${fileCount} files)`; } else { const container =
    ; - container.attachShadow({mode: 'open'}).append( - , - , + + ), + , domify.one(gistData.div)!, ); link.parentElement!.after(container); @@ -63,7 +66,7 @@ function init(signal: AbortSignal): void { if (pageDetect.isGistFile(link) && isOnlyChild(link)) { void embedGist(link); } - }, {signal}); + }, { signal }); } void features.add(import.meta.url, { diff --git a/source/features/esc-to-cancel.tsx b/source/features/esc-to-cancel.tsx index 49a20ba4a5a7..96f65113d9f8 100644 --- a/source/features/esc-to-cancel.tsx +++ b/source/features/esc-to-cancel.tsx @@ -1,9 +1,9 @@ -import {$} from 'select-dom/strict.js'; -import type {DelegateEvent} from 'delegate-it'; +import type { DelegateEvent } from 'delegate-it'; import * as pageDetect from 'github-url-detection'; +import { $ } from 'select-dom/strict.js'; import features from '../feature-manager.js'; -import {onConversationTitleFieldKeydown} from '../github-events/on-field-keydown.js'; +import { onConversationTitleFieldKeydown } from '../github-events/on-field-keydown.js'; function handleEscPress(event: DelegateEvent): void { if (event.key === 'Escape') { diff --git a/source/features/esc-to-deselect-line.tsx b/source/features/esc-to-deselect-line.tsx index ea0009fc249e..d21452d12280 100644 --- a/source/features/esc-to-deselect-line.tsx +++ b/source/features/esc-to-deselect-line.tsx @@ -1,8 +1,8 @@ -import {$optional} from 'select-dom/strict.js'; import * as pageDetect from 'github-url-detection'; +import { $optional } from 'select-dom/strict.js'; import features from '../feature-manager.js'; -import {isEditable} from '../helpers/dom-utils.js'; +import { isEditable } from '../helpers/dom-utils.js'; import removeHashFromUrlBar from '../helpers/history.js'; function isLineSelected(): boolean { @@ -13,16 +13,16 @@ function isLineSelected(): boolean { return /^#L|^#diff-[\da-f]+R\d+/.test(location.hash); } -function listener({key, target}: KeyboardEvent): void { +function listener({ key, target }: KeyboardEvent): void { if (key === 'Escape' && isLineSelected() && !isEditable(target)) { const selectedLineNumber = $optional('.react-line-number.highlighted-line'); if (selectedLineNumber) { // Save and remove line number - const {lineNumber} = selectedLineNumber.dataset; + const { lineNumber } = selectedLineNumber.dataset; selectedLineNumber.dataset.lineNumber = ''; // Trigger click to deselect - selectedLineNumber.dispatchEvent(new MouseEvent('mousedown', {bubbles: true})); + selectedLineNumber.dispatchEvent(new MouseEvent('mousedown', { bubbles: true })); // Restore line number selectedLineNumber.dataset.lineNumber = lineNumber; // Un-focus code block @@ -37,7 +37,7 @@ function listener({key, target}: KeyboardEvent): void { } function init(signal: AbortSignal): void { - document.body.addEventListener('keyup', listener, {signal}); + document.body.addEventListener('keyup', listener, { signal }); } void features.add(import.meta.url, { diff --git a/source/features/expand-all-hidden-comments.tsx b/source/features/expand-all-hidden-comments.tsx index 063904ad4482..d01ad97a04df 100644 --- a/source/features/expand-all-hidden-comments.tsx +++ b/source/features/expand-all-hidden-comments.tsx @@ -1,11 +1,11 @@ -import {$optional} from 'select-dom/strict.js'; -import oneEvent from 'one-event'; -import delegate, {type DelegateEvent} from 'delegate-it'; +import delegate, { type DelegateEvent } from 'delegate-it'; import * as pageDetect from 'github-url-detection'; +import oneEvent from 'one-event'; +import { $optional } from 'select-dom/strict.js'; import features from '../feature-manager.js'; +import { paginationButtonSelector } from '../github-helpers/selectors.js'; import showToast from '../github-helpers/toast.js'; -import {paginationButtonSelector} from '../github-helpers/selectors.js'; // eslint-disable-next-line @typescript-eslint/explicit-function-return-type async function expandHidden(paginationButton: HTMLButtonElement | undefined) { @@ -25,7 +25,7 @@ async function expandHidden(paginationButton: HTMLButtonElement | undefined) { } } -async function handleAltClick({altKey, delegateTarget}: DelegateEvent): Promise { +async function handleAltClick({ altKey, delegateTarget }: DelegateEvent): Promise { if (!altKey) { return; } @@ -37,7 +37,7 @@ async function handleAltClick({altKey, delegateTarget}: DelegateEvent [title]', addHeatIndex, {signal}); + observe('.react-directory-commit-age > [title]', addHeatIndex, { signal }); } void features.add(import.meta.url, { diff --git a/source/features/fit-textareas.tsx b/source/features/fit-textareas.tsx index 0f29850fa833..7b65bc2087db 100644 --- a/source/features/fit-textareas.tsx +++ b/source/features/fit-textareas.tsx @@ -1,26 +1,26 @@ import './fit-textareas.css'; -import {isSafari} from 'webext-detect'; import fitTextarea from 'fit-textarea'; -import {$} from 'select-dom/strict.js'; import * as pageDetect from 'github-url-detection'; +import { $ } from 'select-dom/strict.js'; +import { isSafari } from 'webext-detect'; import features from '../feature-manager.js'; import observe from '../helpers/selector-observer.js'; const nativeFit = CSS.supports('field-sizing', 'content'); -function resetListener({target}: Event): void { +function resetListener({ target }: Event): void { const field = $('textarea', target as HTMLFormElement); // Delay because the field is still filled while the `reset` event is firing setTimeout(fitTextarea, 0, field); } -function inputListener({target}: Event): void { +function inputListener({ target }: Event): void { fitTextarea(target as HTMLTextAreaElement); } -function watchTextarea(textarea: HTMLTextAreaElement, {signal}: SignalAsOptions): void { +function watchTextarea(textarea: HTMLTextAreaElement, { signal }: SignalAsOptions): void { // Disable constrained GitHub feature textarea.classList.remove('size-to-fit', 'js-size-to-fit', 'issue-form-textarea'); // Remove !important height and min-height textarea.classList.add('rgh-fit-textareas'); @@ -29,23 +29,27 @@ function watchTextarea(textarea: HTMLTextAreaElement, {signal}: SignalAsOptions) return; } - textarea.addEventListener('input', inputListener, {signal}); // The user triggers `input` event - textarea.addEventListener('focus', inputListener, {signal}); // The user triggers `focus` event - textarea.addEventListener('change', inputListener, {signal}); // File uploads trigger `change` events - textarea.form?.addEventListener('reset', resetListener, {signal}); + textarea.addEventListener('input', inputListener, { signal }); // The user triggers `input` event + textarea.addEventListener('focus', inputListener, { signal }); // The user triggers `focus` event + textarea.addEventListener('change', inputListener, { signal }); // File uploads trigger `change` events + textarea.form?.addEventListener('reset', resetListener, { signal }); fitTextarea(textarea); } function init(signal: AbortSignal): void { // `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(` + observe( + ` textarea:not( anchored-position #pull_request_review_body, #pull_request_body_ghost, #pull_request_body_ghost_ruler ) - `, watchTextarea, {signal}); + `, + watchTextarea, + { signal }, + ); } void features.add(import.meta.url, { diff --git a/source/features/fix-no-pr-search.tsx b/source/features/fix-no-pr-search.tsx index dc6cc860d46f..3af83c59e632 100644 --- a/source/features/fix-no-pr-search.tsx +++ b/source/features/fix-no-pr-search.tsx @@ -1,5 +1,5 @@ +import delegate, { type DelegateEvent } from 'delegate-it'; import * as pageDetect from 'github-url-detection'; -import delegate, {type DelegateEvent} from 'delegate-it'; import features from '../feature-manager.js'; import SearchQuery from '../github-helpers/search-query.js'; diff --git a/source/features/github-actions-indicators.tsx b/source/features/github-actions-indicators.tsx index d71e2d9c72d6..f2b41e8327ca 100644 --- a/source/features/github-actions-indicators.tsx +++ b/source/features/github-actions-indicators.tsx @@ -1,17 +1,17 @@ -import {CachedFunction} from 'webext-storage-cache'; +import { parseCron } from '@fregante/mi-cron'; import React from 'dom-chef'; -import {$} from 'select-dom/strict.js'; -import PlayIcon from 'octicons-plain-react/Play'; -import {parseCron} from '@fregante/mi-cron'; import * as pageDetect from 'github-url-detection'; +import PlayIcon from 'octicons-plain-react/Play'; +import { $ } from 'select-dom/strict.js'; +import { CachedFunction } from 'webext-storage-cache'; import features from '../feature-manager.js'; import api from '../github-helpers/api.js'; -import {cacheByRepo} from '../github-helpers/index.js'; +import { expectToken } from '../github-helpers/github-token.js'; +import { cacheByRepo } from '../github-helpers/index.js'; +import removeHashFromUrlBar from '../helpers/history.js'; import observe from '../helpers/selector-observer.js'; import GetWorkflows from './github-actions-indicators.gql'; -import {expectToken} from '../github-helpers/github-token.js'; -import removeHashFromUrlBar from '../helpers/history.js'; type Workflow = { name: string; @@ -38,7 +38,7 @@ async function getWorkflows(): Promise { } async function getFilesInWorkflowPath(): Promise> { - const {repository: {workflowFiles}} = await api.v4(GetWorkflows); + const { repository: { workflowFiles } } = await api.v4(GetWorkflows); const workflows: any[] = workflowFiles?.entries ?? []; @@ -74,8 +74,8 @@ const workflowDetails = new CachedFunction('workflows-details', { return details; }, - maxAge: {days: 1}, - staleWhileRevalidate: {days: 10}, + maxAge: { days: 1 }, + staleWhileRevalidate: { days: 10 }, cacheKey: cacheByRepo, }); @@ -93,26 +93,30 @@ async function addIndicators(workflowLink: HTMLAnchorElement): Promise { const url = new URL(workflowLink.href); url.hash = 'rgh-run-workflow'; workflowLink.after( - - - , + ( + + + + ), ); } else { // This class keeps the action on a single line. It natively exists if the item can be pinned (if current user has write access) workflowLink.parentElement!.classList.add('ActionListItem--withActions'); workflowLink.after( -
    - -
    , + ( +
    + +
    + ), ); } } @@ -130,15 +134,17 @@ async function addIndicators(workflowLink: HTMLAnchorElement): Promise { } $('.ActionListItem-label', workflowLink).append( - - () - , + ( + + () + + ), ); } async function init(signal: AbortSignal): Promise { await expectToken(); - observe('a.ActionListContent', addIndicators, {signal}); + observe('a.ActionListContent', addIndicators, { signal }); } function openRunWorkflow(): void { diff --git a/source/features/global-conversation-list-filters.tsx b/source/features/global-conversation-list-filters.tsx index 3574f0e19ebf..7ed7022590b1 100644 --- a/source/features/global-conversation-list-filters.tsx +++ b/source/features/global-conversation-list-filters.tsx @@ -1,8 +1,8 @@ import './global-conversation-list-filters.css'; import React from 'dom-chef'; -import {$$, elementExists} from 'select-dom'; import * as pageDetect from 'github-url-detection'; +import { $$, elementExists } from 'select-dom'; import features from '../feature-manager.js'; import SearchQuery from '../github-helpers/search-query.js'; @@ -11,7 +11,7 @@ import observe from '../helpers/selector-observer.js'; function createLink(label: string, title: string, query: string): HTMLElement { const url = new URL('/pulls', location.origin); url.searchParams.set('q', `is:open archived:false ${query}`); - const link = {label}; + const link = {label}; const isCurrentPage = SearchQuery.from(location).includes(query); @@ -36,7 +36,7 @@ function addLinks(container: HTMLElement): void { } function init(signal: AbortSignal): void { - observe('.subnav-links', addLinks, {signal}); + observe('.subnav-links', addLinks, { signal }); } void features.add(import.meta.url, { diff --git a/source/features/hidden-review-comments-indicator.tsx b/source/features/hidden-review-comments-indicator.tsx index cc1b18eafdbf..3408499f4244 100644 --- a/source/features/hidden-review-comments-indicator.tsx +++ b/source/features/hidden-review-comments-indicator.tsx @@ -1,19 +1,19 @@ import './hidden-review-comments-indicator.css'; -import mem from 'memoize'; +import { onAbort } from 'abort-utils'; +import delegate, { type DelegateEvent } from 'delegate-it'; import React from 'dom-chef'; -import {$$, countElements} from 'select-dom'; -import CommentIcon from 'octicons-plain-react/Comment'; import * as pageDetect from 'github-url-detection'; -import delegate, {type DelegateEvent} from 'delegate-it'; -import {onAbort} from 'abort-utils'; +import mem from 'memoize'; +import CommentIcon from 'octicons-plain-react/Comment'; +import { $$, countElements } from 'select-dom'; import features from '../feature-manager.js'; import preserveScroll from '../helpers/preserve-scroll.js'; import observe from '../helpers/selector-observer.js'; // When an indicator is clicked, this will show comments on the current file -function handleIndicatorClick({delegateTarget}: DelegateEvent): void { +function handleIndicatorClick({ delegateTarget }: DelegateEvent): void { const commentedLine = delegateTarget.closest('tr')!.previousElementSibling!; const resetScroll = preserveScroll(commentedLine); delegateTarget @@ -28,14 +28,16 @@ function handleIndicatorClick({delegateTarget}: DelegateEvent): void { const addIndicator = mem((commentThread: HTMLElement): void => { const commentCount = countElements('.review-comment.js-comment', commentThread); commentThread.before( - - - - - , + ( + + + + + + ), ); }); @@ -62,9 +64,9 @@ function init(signal: AbortSignal): void { attributeOldValue: true, attributeFilter: ['class'], }); - }, {signal}); + }, { signal }); - delegate('.rgh-comments-indicator', 'click', handleIndicatorClick, {signal}); + delegate('.rgh-comments-indicator', 'click', handleIndicatorClick, { signal }); onAbort(signal, indicatorToggleObserver); } diff --git a/source/features/hide-inactive-deployments.tsx b/source/features/hide-inactive-deployments.tsx index f06b9d07271c..52b1197a6032 100644 --- a/source/features/hide-inactive-deployments.tsx +++ b/source/features/hide-inactive-deployments.tsx @@ -1,5 +1,5 @@ -import {$$, elementExists} from 'select-dom'; import * as pageDetect from 'github-url-detection'; +import { $$, elementExists } from 'select-dom'; import features from '../feature-manager.js'; diff --git a/source/features/hide-low-quality-comments.tsx b/source/features/hide-low-quality-comments.tsx index 277be6397adc..c7503d7c5016 100644 --- a/source/features/hide-low-quality-comments.tsx +++ b/source/features/hide-low-quality-comments.tsx @@ -1,13 +1,13 @@ import './hide-low-quality-comments.css'; +import delegate, { type DelegateEvent } from 'delegate-it'; import React from 'dom-chef'; -import {$, $optional} from 'select-dom/strict.js'; -import {$$, countElements, elementExists} from 'select-dom'; import * as pageDetect from 'github-url-detection'; -import delegate, {type DelegateEvent} from 'delegate-it'; +import { $$, countElements, elementExists } from 'select-dom'; +import { $, $optional } from 'select-dom/strict.js'; -import delay from '../helpers/delay.js'; import features from '../feature-manager.js'; +import delay from '../helpers/delay.js'; import isLowQualityComment from '../helpers/is-low-quality-comment.js'; export const singleParagraphCommentSelector = '.comment-body > p:only-child'; @@ -79,10 +79,12 @@ function init(): void { const lowQualityCount = countElements('.rgh-hidden-comment'); if (lowQualityCount > 0) { $('.discussion-timeline-actions').prepend( -

    - {`${lowQualityCount} unhelpful comment${lowQualityCount > 1 ? 's were' : ' was'} automatically hidden. `} - -

    , + ( +

    + {`${lowQualityCount} unhelpful comment${lowQualityCount > 1 ? 's were' : ' was'} automatically hidden. `} + +

    + ), ); // No need to add the signal here diff --git a/source/features/hide-navigation-hover-highlight.tsx b/source/features/hide-navigation-hover-highlight.tsx index cafb13678117..69adadcb5429 100644 --- a/source/features/hide-navigation-hover-highlight.tsx +++ b/source/features/hide-navigation-hover-highlight.tsx @@ -9,7 +9,7 @@ function init(): void { html.setAttribute(attribute, ''); html.addEventListener('navigation:keydown', () => { html.removeAttribute(attribute); - }, {once: true}); + }, { once: true }); } void features.add(import.meta.url, { diff --git a/source/features/highest-rated-comment.tsx b/source/features/highest-rated-comment.tsx index 3b2c1c8b9093..cc309245996e 100644 --- a/source/features/highest-rated-comment.tsx +++ b/source/features/highest-rated-comment.tsx @@ -1,17 +1,17 @@ import './highest-rated-comment.css'; -import mem from 'memoize'; import React from 'dom-chef'; -import {$, $optional} from 'select-dom/strict.js'; -import {$$} from 'select-dom'; import * as pageDetect from 'github-url-detection'; +import mem from 'memoize'; import ArrowDownIcon from 'octicons-plain-react/ArrowDown'; import CheckCircleFillIcon from 'octicons-plain-react/CheckCircleFill'; +import { $$ } from 'select-dom'; +import { $, $optional } from 'select-dom/strict.js'; import features from '../feature-manager.js'; -import looseParseInt from '../helpers/loose-parse-int.js'; import isLowQualityComment from '../helpers/is-low-quality-comment.js'; -import {singleParagraphCommentSelector} from './hide-low-quality-comments.js'; +import looseParseInt from '../helpers/loose-parse-int.js'; +import { singleParagraphCommentSelector } from './hide-low-quality-comments.js'; // `.js-timeline-item` gets the nearest comment excluding the very first comment (OP post) const commentSelector = '.js-timeline-item'; @@ -31,7 +31,6 @@ const getPositiveReactions = mem((comment: HTMLElement): number | void => { if ( // It needs to be upvoted enough times count >= 10 - // It can't be a controversial comment && selectSum(negativeReactionsSelector, comment) < count / 2 ) { @@ -45,7 +44,7 @@ function getBestComment(): HTMLElement | undefined { const comment = reaction.closest(commentSelector)!; const positiveReactions = getPositiveReactions(comment); if (positiveReactions && (!highest || positiveReactions > highest.count)) { - highest = {comment, count: positiveReactions}; + highest = { comment, count: positiveReactions }; } } @@ -55,12 +54,14 @@ function getBestComment(): HTMLElement | undefined { function highlightBestComment(bestComment: Element): void { $('.unminimized-comment', bestComment).classList.add('rgh-highest-rated-comment'); $('.unminimized-comment .timeline-comment-header > h3', bestComment).before( - - - , + ( + + + + ), ); } @@ -74,21 +75,27 @@ function linkBestComment(bestComment: HTMLElement): void { } const text = $('.comment-body', bestComment).textContent.slice(0, 100); - const {hash} = $('a.js-timestamp', bestComment); + const { hash } = $('a.js-timestamp', bestComment); const avatar = $('img.avatar', bestComment).cloneNode(); bestComment.parentElement!.firstElementChild!.after( - - {avatar} - -

    - Highest-rated{text} -

    - -
    - Jump to comment -
    -
    , + ( + + {avatar} + +

    + Highest-rated + {text} +

    + +
    + Jump to comment +
    +
    + ), ); } diff --git a/source/features/highlight-non-default-base-branch.tsx b/source/features/highlight-non-default-base-branch.tsx index 8823b49b39f0..cef0e078a035 100644 --- a/source/features/highlight-non-default-base-branch.tsx +++ b/source/features/highlight-non-default-base-branch.tsx @@ -1,14 +1,14 @@ +import batchedFunction from 'batched-function'; import React from 'dom-chef'; import * as pageDetect from 'github-url-detection'; import GitPullRequestIcon from 'octicons-plain-react/GitPullRequest'; -import batchedFunction from 'batched-function'; -import {elementExists} from 'select-dom'; +import { elementExists } from 'select-dom'; import features from '../feature-manager.js'; import api from '../github-helpers/api.js'; -import observe from '../helpers/selector-observer.js'; -import {expectToken} from '../github-helpers/github-token.js'; +import { expectToken } from '../github-helpers/github-token.js'; import abbreviateString from '../helpers/abbreviate-string.js'; +import observe from '../helpers/selector-observer.js'; type BaseBranch = { ref: string | undefined; @@ -39,17 +39,19 @@ function isClosed(prLink: HTMLElement): boolean { function buildQuery(prsByRepo: Map): string { return [...prsByRepo.values()].map(prs => { - const {owner, repo} = prs[0]; + const { owner, repo } = prs[0]; return ` ${api.escapeKey('repo', owner, repo)}: repository(owner: "${owner}", name: "${repo}") { nameWithOwner defaultBranchRef {name} - ${prs.map(pr => ` + ${ + prs.map(pr => ` ${api.escapeKey('pr', pr.number)}: pullRequest(number: ${pr.number}) { ref: baseRef {id} refName: baseRefName } - `).join('\n')} + `).join('\n') + } } `; }).join('\n'); @@ -60,12 +62,12 @@ function renderBranches(pr: Pr, baseBranch: BaseBranch, nameWithOwner: string): const displayName = abbreviateString(baseBranch.refName, 25); const badge = ( - + {' To '} {displayName} @@ -90,7 +92,10 @@ async function add(prLinks: HTMLAnchorElement[]): Promise { for (const link of prLinks) { const [, owner, repo, , number] = link.pathname.split('/'); prs.add({ - link, owner, repo, number: Number(number), + link, + owner, + repo, + number: Number(number), }); } @@ -98,7 +103,7 @@ async function add(prLinks: HTMLAnchorElement[]): Promise { const data = await api.v4(buildQuery(prsByRepo)); for (const repoPrs of prsByRepo.values()) { - const {owner, repo} = repoPrs[0]; + const { owner, repo } = repoPrs[0]; const repository = data[api.escapeKey('repo', owner, repo)]; const defaultBranch = repository.defaultBranchRef.name; for (const pr of repoPrs) { @@ -120,11 +125,15 @@ async function add(prLinks: HTMLAnchorElement[]): Promise { async function init(signal: AbortSignal): Promise { await expectToken(); - observe([ - '.js-issue-row a[data-hovercard-type="pull_request"]', // Repo and global PR lists - 'a[data-hovercard-type="pull_request"][data-testid="listitem-title-link"]', // Preview global PR list - 'a[data-hovercard-type="pull_request"][data-testid="issue-pr-title-link"]', // Issue list - ], batchedFunction(add, {delay: 100}), {signal}); + observe( + [ + '.js-issue-row a[data-hovercard-type="pull_request"]', // Repo and global PR lists + 'a[data-hovercard-type="pull_request"][data-testid="listitem-title-link"]', // Preview global PR list + 'a[data-hovercard-type="pull_request"][data-testid="issue-pr-title-link"]', // Issue list + ], + batchedFunction(add, { delay: 100 }), + { signal }, + ); } void features.add(import.meta.url, { diff --git a/source/features/html-preview-link.tsx b/source/features/html-preview-link.tsx index 5863e0786f2c..33f58d8d193b 100644 --- a/source/features/html-preview-link.tsx +++ b/source/features/html-preview-link.tsx @@ -15,22 +15,24 @@ function add(rawButton: HTMLAnchorElement): void { .parentElement! // `div` .parentElement! // `BtnGroup` .prepend( - , + ( + + ), ); } function init(signal: AbortSignal): void { - observe(['a#raw-url', 'a[data-testid="raw-button"]'], add, {signal}); + observe(['a#raw-url', 'a[data-testid="raw-button"]'], add, { signal }); } void features.add(import.meta.url, { diff --git a/source/features/improve-shortcut-help.tsx b/source/features/improve-shortcut-help.tsx index 4a304ee59c14..14cbffb1a109 100644 --- a/source/features/improve-shortcut-help.tsx +++ b/source/features/improve-shortcut-help.tsx @@ -1,58 +1,64 @@ import './improve-shortcut-help.css'; import React from 'dom-chef'; -import {elementExists} from 'select-dom'; -import {$, $optional} from 'select-dom/strict.js'; import memoize from 'memoize'; +import { elementExists } from 'select-dom'; +import { $, $optional } from 'select-dom/strict.js'; -import onetime from '../helpers/onetime.js'; import features from '../feature-manager.js'; -import {isEditable} from '../helpers/dom-utils.js'; -import {shortcutMap} from '../helpers/feature-helpers.js'; +import { isEditable } from '../helpers/dom-utils.js'; +import { shortcutMap } from '../helpers/feature-helpers.js'; +import onetime from '../helpers/onetime.js'; import observe from '../helpers/selector-observer.js'; function splitKeys(keys: string): DocumentFragment[] { - return keys.split(' ').map(key => <> {key}); + return keys.split(' ').map(key => ( + <> + {key} + + )); } function improveShortcutHelpLegacy(dialog: Element): void { $('.Box-body .col-5 .Box:first-child', dialog).after( -
    -
    -

    Refined GitHub

    + ( +
    +
    +

    Refined GitHub

    +
    + +
      + {[...shortcutMap] + .toSorted(([, a], [, b]) => a.localeCompare(b)) + .map(([hotkey, description]) => ( +
    • +
      {description}
      +
      + {splitKeys(hotkey)} +
      +
    • + ))} +
    - -
      - {[...shortcutMap] - .toSorted(([, a], [, b]) => a.localeCompare(b)) - .map(([hotkey, description]) => ( -
    • -
      {description}
      -
      - {splitKeys(hotkey)} -
      -
    • - ))} -
    -
    , + ), ); } -const observer = new MutationObserver(([{target}]) => { +const observer = new MutationObserver(([{ target }]) => { if (target instanceof Element && !elementExists('.js-details-dialog-spinner', target)) { improveShortcutHelpLegacy(target); observer.disconnect(); } }); -function observeShortcutModal({key, target}: KeyboardEvent): void { +function observeShortcutModal({ key, target }: KeyboardEvent): void { if (key !== '?' || isEditable(target)) { return; } const modal = $optional('body > details:not(.js-command-palette-dialog) > details-dialog'); if (modal) { - observer.observe(modal, {childList: true}); + observer.observe(modal, { childList: true }); } } @@ -77,16 +83,18 @@ const getRghShortcutsContainer = memoize( const currentItem = shortcutItem.cloneNode(true); currentItem.firstElementChild!.textContent = description; currentItem.lastElementChild!.replaceChildren( - - {hotkey.split(' ').map((key, index) => ( - <> - {index > 0 && ' '} - - {key.charAt(0).toUpperCase() + key.slice(1)} - - - ))} - , + ( + + {hotkey.split(' ').map((key, index) => ( + <> + {index > 0 && ' '} + + {key.charAt(0).toUpperCase() + key.slice(1)} + + + ))} + + ), ); return currentItem; }), @@ -110,7 +118,7 @@ function improveShortcutHelp(columnsContainer: HTMLElement): void { } function init(signal: AbortSignal): void { - observe('div[class^="ShortcutsDialog"][class*="ColumnsContainer"]', improveShortcutHelp, {signal}); + observe('div[class^="ShortcutsDialog"][class*="ColumnsContainer"]', improveShortcutHelp, { signal }); } void features.add(import.meta.url, { diff --git a/source/features/jump-to-change-requested-comment.tsx b/source/features/jump-to-change-requested-comment.tsx index 8877c1343207..0903996c1faa 100644 --- a/source/features/jump-to-change-requested-comment.tsx +++ b/source/features/jump-to-change-requested-comment.tsx @@ -1,9 +1,9 @@ import React from 'dom-chef'; -import {$} from 'select-dom/strict.js'; import * as pageDetect from 'github-url-detection'; +import { $ } from 'select-dom/strict.js'; -import {wrap} from '../helpers/dom-utils.js'; import features from '../feature-manager.js'; +import { wrap } from '../helpers/dom-utils.js'; import observe from '../helpers/selector-observer.js'; function linkify(textLine: HTMLElement): void { @@ -13,7 +13,7 @@ function linkify(textLine: HTMLElement): void { } function init(signal: AbortSignal): void { - observe('.merge-status-item.review-item [title*="requested changes"]', linkify, {signal}); + observe('.merge-status-item.review-item [title*="requested changes"]', linkify, { signal }); } void features.add(import.meta.url, { diff --git a/source/features/jump-to-conversation-close-event.tsx b/source/features/jump-to-conversation-close-event.tsx index 656727aaa0c3..2885325d44d0 100644 --- a/source/features/jump-to-conversation-close-event.tsx +++ b/source/features/jump-to-conversation-close-event.tsx @@ -1,13 +1,13 @@ +import debounce from 'debounce-fn'; import React from 'dom-chef'; -import {$, $$, lastElement} from 'select-dom/strict.js'; import * as pageDetect from 'github-url-detection'; -import debounce from 'debounce-fn'; +import { $, $$, lastElement } from 'select-dom/strict.js'; -import {wrap} from '../helpers/dom-utils.js'; import features from '../feature-manager.js'; +import { conversationCloseEvent } from '../github-helpers/selectors.js'; +import { wrap } from '../helpers/dom-utils.js'; +import { getIdentifiers } from '../helpers/feature-helpers.js'; import observe from '../helpers/selector-observer.js'; -import {conversationCloseEvent} from '../github-helpers/selectors.js'; -import {getIdentifiers} from '../helpers/feature-helpers.js'; import './jump-to-conversation-close-event.css'; export const statusBadgeSelector = [ @@ -15,7 +15,7 @@ export const statusBadgeSelector = [ '[data-testid="header-state"]', ] as const; -export const {class: featureClass, selector: featureSelector} = getIdentifiers(import.meta.url); +export const { class: featureClass, selector: featureSelector } = getIdentifiers(import.meta.url); function updateStatusBadges(): void { // Not processing the element that has been observed because past events may load in the middle of the page @@ -32,11 +32,13 @@ function updateStatusBadges(): void { statusBadge.style.pointerEvents = 'none'; wrap( statusBadge, - , + ( + + ), ); } } @@ -46,8 +48,8 @@ function init(signal: AbortSignal): void { observe( conversationCloseEvent, // Avoid calling `updateStatusBadges` for every close event on initial load - debounce(updateStatusBadges, {wait: 100}), - {signal}, + debounce(updateStatusBadges, { wait: 100 }), + { signal }, ); } diff --git a/source/features/keyboard-navigation.tsx b/source/features/keyboard-navigation.tsx index c7b45d3a50f6..dc40a087d34a 100644 --- a/source/features/keyboard-navigation.tsx +++ b/source/features/keyboard-navigation.tsx @@ -1,10 +1,10 @@ -import {$optional} from 'select-dom/strict.js'; -import {$$, elementExists} from 'select-dom'; import * as pageDetect from 'github-url-detection'; +import { $$, elementExists } from 'select-dom'; +import { $optional } from 'select-dom/strict.js'; -import {isEditable} from '../helpers/dom-utils.js'; -import {viewedToggleSelector} from './batch-mark-files-as-viewed.js'; import features from '../feature-manager.js'; +import { isEditable } from '../helpers/dom-utils.js'; +import { viewedToggleSelector } from './batch-mark-files-as-viewed.js'; const isCommentGroupMinimized = (comment: HTMLElement): boolean => elementExists('.minimized-comment:not(.d-none)', comment) @@ -27,16 +27,15 @@ function runShortcuts(event: KeyboardEvent): void { return; } - const items - = $$([ - 'div[class*="targetable" i][id^="diff-"]', // Files in diffs - '.js-minimizable-comment-group', // Comments (to be `.filter()`ed) - ]) - .filter(element => - element.classList.contains('js-minimizable-comment-group') - ? !isCommentGroupMinimized(element) - : true, - ); + const items = $$([ + 'div[class*="targetable" i][id^="diff-"]', // Files in diffs + '.js-minimizable-comment-group', // Comments (to be `.filter()`ed) + ]) + .filter(element => + element.classList.contains('js-minimizable-comment-group') + ? !isCommentGroupMinimized(element) + : true + ); // `j` goes to the next item, `k` goes back an item const direction = event.key === 'j' ? 1 : -1; @@ -56,7 +55,7 @@ function runShortcuts(event: KeyboardEvent): void { } function init(signal: AbortSignal): void { - document.body.addEventListener('keypress', runShortcuts, {signal}); + document.body.addEventListener('keypress', runShortcuts, { signal }); } void features.add(import.meta.url, { diff --git a/source/features/last-notification-page-button.tsx b/source/features/last-notification-page-button.tsx index 1c9bb5ae284e..631a6e4d4732 100644 --- a/source/features/last-notification-page-button.tsx +++ b/source/features/last-notification-page-button.tsx @@ -1,11 +1,11 @@ import React from 'dom-chef'; -import {$} from 'select-dom/strict.js'; import * as pageDetect from 'github-url-detection'; -import {stringToBase64} from 'uint8array-extras'; +import { $ } from 'select-dom/strict.js'; +import { stringToBase64 } from 'uint8array-extras'; import features from '../feature-manager.js'; +import { assertNodeContent } from '../helpers/dom-utils.js'; import looseParseInt from '../helpers/loose-parse-int.js'; -import {assertNodeContent} from '../helpers/dom-utils.js'; import observe from '../helpers/selector-observer.js'; const itemsPerNotificationsPage = 25; @@ -19,15 +19,17 @@ function linkify(nextButton: HTMLAnchorElement): void { nextButtonSearch.set('after', stringToBase64(`cursor:${lastCursor}`)); totalNotificationsNode.replaceWith( ' of ', - - {totalNotificationsNumber} - , + ( + + {totalNotificationsNumber} + + ), ); } function init(signal: AbortSignal): void { // When there's no "next page", this element becomes ` -
    -
    +
    +
    File also being edited in
    -