diff --git a/.oxlintrc.json b/.oxlintrc.json
new file mode 100644
index 0000000..4e049bb
--- /dev/null
+++ b/.oxlintrc.json
@@ -0,0 +1,262 @@
+{
+ "plugins": [
+ "unicorn",
+ "typescript",
+ "oxc",
+ "jsdoc",
+ "promise",
+ "eslint"
+ ],
+ "categories": {},
+ "rules": {
+ /*
+ "ruleName": "severity"
+ OR
+ "ruleName": ["severity", { "ruleOption": "value" (boolean and numeric values are unquoted) }]
+ */
+ "accessor-pairs": "warn",
+ "block-scoped-var": "warn",
+ "complexity": "warn",
+ "for-direction": "warn",
+ "no-async-promise-executor": "warn",
+ "no-await-in-loop": "warn",
+ "no-caller": "warn",
+ "no-class-assign": "warn",
+ "no-compare-neg-zero": "warn",
+ "no-cond-assign": "warn",
+ "no-const-assign": "warn",
+ "no-constant-binary-expression": "warn",
+ "no-constant-condition": "warn",
+ "no-control-regex": "warn",
+ "no-debugger": "warn",
+ "no-delete-var": "warn",
+ "no-dupe-class-members": "warn",
+ "no-dupe-else-if": "warn",
+ "no-dupe-keys": "warn",
+ "no-duplicate-case": "warn",
+ "no-empty-character-class": "warn",
+ "no-empty-pattern": "warn",
+ "no-empty-static-block": "warn",
+ "no-eval": "warn",
+ "no-ex-assign": "warn",
+// "no-extend-native": "warn",
+ "no-extra-bind": "warn",
+ "no-extra-boolean-cast": "warn",
+ "no-fallthrough": "warn",
+ "no-func-assign": "warn",
+ "no-global-assign": "warn",
+ "no-import-assign": "warn",
+// "no-implicit-coercion": "warn",
+ "no-invalid-regexp": "warn",
+ "no-irregular-whitespace": "warn",
+ "no-loop-func": "error",
+ "no-loss-of-precision": "warn",
+ "no-new": "warn",
+ "no-new-native-nonconstructor": "warn",
+ "no-nonoctal-decimal-escape": "warn",
+ "no-obj-calls": "warn",
+ "no-promise-executor-return": "warn",
+ "no-regex-spaces": "warn",
+ "no-return-assign": "warn",
+ "no-self-assign": "warn",
+ "no-sequences": [ "warn", { "allowInParentheses": false } ],
+ "no-setter-return": "warn",
+ "no-shadow-restricted-names": "warn",
+ "no-sparse-arrays": "warn",
+ "no-this-before-super": "warn",
+ "no-unassigned-vars": "warn",
+ "no-unexpected-multiline": "warn",
+ "no-unneeded-ternary": "warn",
+ "no-unsafe-finally": "warn",
+ "no-unsafe-negation": "warn",
+ "no-unsafe-optional-chaining": "warn",
+ "no-unused-expressions": "warn",
+ "no-unused-labels": "warn",
+ "no-unused-private-class-members": "warn",
+ "no-unused-vars": "warn",
+ "no-useless-backreference": "warn",
+ "no-useless-call": "warn",
+ "no-useless-catch": "warn",
+ "no-useless-computed-key": "warn",
+ "no-useless-concat": "warn",
+ "no-useless-constructor": "warn",
+ "no-useless-escape": "warn",
+ "no-useless-rename": "warn",
+ "no-with": "warn",
+ "preserve-caught-error": "warn",
+ "require-yield": "warn",
+ "use-isnan": "warn",
+ "valid-typeof": "warn",
+ "jsdoc/check-property-names": "warn",
+ "jsdoc/check-tag-names": "warn",
+ "jsdoc/implements-on-classes": "warn",
+ "jsdoc/no-defaults": "warn",
+ "jsdoc/require-property": "warn",
+ "jsdoc/require-property-description": "warn",
+ "jsdoc/require-property-name": "warn",
+ "jsdoc/require-property-type": "warn",
+ "jsdoc/require-yields": "warn",
+ "oxc/approx-constant": "warn",
+ "oxc/bad-array-method-on-arguments": "warn",
+ "oxc/bad-char-at-comparison": "warn",
+ "oxc/bad-comparison-sequence": "warn",
+ "oxc/bad-min-max-func": "warn",
+ "oxc/bad-object-literal-comparison": "warn",
+ "oxc/bad-replace-all-arg": "warn",
+ "oxc/const-comparisons": "warn",
+ "oxc/double-comparisons": "warn",
+ "oxc/erasing-op": "warn",
+ "oxc/misrefactored-assign-op": "warn",
+ "oxc/missing-throw": "warn",
+ "oxc/no-accumulating-spread": "warn",
+ "oxc/no-async-endpoint-handlers": "warn",
+ "oxc/no-this-in-exported-function": "warn",
+ "oxc/number-arg-out-of-range": "warn",
+ "oxc/only-used-in-recursion": "warn",
+ "oxc/uninvoked-array-callback": "warn",
+ "promise/always-return": "warn",
+ "promise/no-callback-in-promise": "warn",
+ "promise/no-multiple-resolved": "warn",
+ "promise/no-new-statics": "warn",
+ "promise/no-promise-in-callback": "warn",
+ "promise/valid-params": "warn",
+ "typescript/await-thenable": "warn",
+ "typescript/no-array-delete": "warn",
+ "typescript/no-base-to-string": "warn",
+// "typescript/no-confusing-non-null-assertion": "warn",
+ "typescript/no-duplicate-enum-values": "warn",
+ "typescript/no-duplicate-type-constituents": "warn",
+ "typescript/no-extra-non-null-assertion": "warn",
+// "typescript/no-extraneous-class": "warn",
+ "typescript/no-floating-promises": "warn",
+ "typescript/no-for-in-array": "warn",
+ "typescript/no-implied-eval": "warn",
+ "typescript/no-meaningless-void-operator": "warn",
+ "typescript/no-misused-new": "warn",
+ "typescript/no-misused-spread": "warn",
+ "typescript/no-non-null-asserted-optional-chain": "warn",
+ "typescript/no-redundant-type-constituents": "warn",
+ "typescript/no-this-alias": "warn",
+// "typescript/no-unnecessary-boolean-literal-compare": "warn",
+ "typescript/no-unnecessary-parameter-property-assignment": "warn",
+// "typescript/no-unnecessary-template-expression": "warn",
+// "typescript/no-unnecessary-type-arguments": "warn",
+// "typescript/no-unnecessary-type-assertion": "warn",
+// "typescript/no-unnecessary-type-constraint": "warn",
+ "typescript/no-unsafe-declaration-merging": "warn",
+// "typescript/no-unsafe-enum-comparison": "warn",
+// "typescript/no-unsafe-type-assertion": "warn",
+ "typescript/no-unsafe-unary-minus": "warn",
+ "typescript/no-useless-empty-export": "warn",
+ "typescript/no-wrapper-object-types": "warn",
+ "typescript/prefer-as-const": "warn",
+ "typescript/require-array-sort-compare": "warn",
+ "typescript/restrict-template-expressions": "warn",
+ "typescript/triple-slash-reference": "warn",
+ "typescript/unbound-method": "warn",
+ "unicorn/consistent-function-scoping": "warn",
+ "unicorn/no-accessor-recursion": "warn",
+ "unicorn/no-array-reverse": "warn",
+ "unicorn/no-array-sort": "warn",
+ "unicorn/no-await-in-promise-methods": "warn",
+ "unicorn/no-empty-file": "warn",
+ "unicorn/no-immediate-mutation": "warn",
+ "unicorn/no-instanceof-builtins": "warn",
+ "unicorn/no-invalid-fetch-options": "warn",
+ "unicorn/no-invalid-remove-event-listener": "warn",
+ "unicorn/no-negation-in-equality-check": "warn",
+ "unicorn/no-new-array": "warn",
+ "unicorn/no-single-promise-in-promise-methods": "warn",
+ "unicorn/no-thenable": "warn",
+ "unicorn/no-unnecessary-await": "warn",
+ "unicorn/no-useless-fallback-in-spread": "warn",
+ "unicorn/no-unnecessary-array-flat-depth": "warn",
+ "unicorn/no-useless-collection-argument": "warn",
+ "unicorn/no-useless-error-capture-stack-trace": "warn",
+ "unicorn/no-useless-length-check": "warn",
+ "unicorn/no-useless-spread": "warn",
+ "unicorn/prefer-add-event-listener": "warn",
+ "unicorn/prefer-array-find": "warn",
+ "unicorn/prefer-array-flat-map": "warn",
+ "unicorn/prefer-dom-node-append": "warn",
+ "unicorn/prefer-dom-node-dataset": "off",
+ "unicorn/prefer-dom-node-remove": "warn",
+ "unicorn/prefer-dom-node-text-content": "warn",
+ "unicorn/prefer-default-parameters": "warn",
+ "unicorn/prefer-keyboard-event-key": "warn",
+ "unicorn/prefer-logical-operator-over-ternary": "warn",
+ "unicorn/prefer-optional-catch-binding": "warn",
+ "unicorn/prefer-query-selector": "warn",
+ "unicorn/prefer-response-static-json": "warn",
+ "unicorn/prefer-set-has": "warn",
+ "unicorn/prefer-set-size": "warn",
+ "unicorn/prefer-string-starts-ends-with": "warn",
+ "unicorn/require-module-specifiers": "warn",
+ "unicorn/require-post-message-target-origin": "warn",
+ "unicorn/text-encoding-identifier-case": "warn"
+ },
+ "settings": {
+ "jsx-a11y": {
+ "polymorphicPropName": null,
+ "components": {},
+ "attributes": {}
+ },
+ "next": {
+ "rootDir": []
+ },
+ "react": {
+ "formComponents": [],
+ "linkComponents": []
+ },
+ "jsdoc": {
+ "ignorePrivate": false,
+ "ignoreInternal": false,
+ "ignoreReplacesDocs": true,
+ "overrideReplacesDocs": true,
+ "augmentsExtendsReplacesDocs": false,
+ "implementsReplacesDocs": false,
+ "exemptDestructuredRootsFromChecks": false,
+ "tagNamePreference": {}
+ },
+ "vitest": {
+ "typecheck": false
+ }
+ },
+ "env": {
+ "builtin": true
+ },
+ "globals": {},
+ "ignorePatterns": [],
+ "extends": [
+// "localrules.json"
+ ],
+ "overrides": [
+ // REFERENCE: https://eslint.org/docs/v8.x/use/configure/language-options
+ // REFERENCE: https://oxc.rs/docs/guide/usage/linter/config-file-reference
+ { // userscripts
+ "files": ["**/*.user.js"],
+ "rules": {
+ // Always allow console api in userscripts
+ "no-console": "off",
+ // Doesn't understand that immediately-executed-functions are only ever called once, and just whines...
+ "unicorn/consistent-function-scoping": "off",
+ // Portability
+ "unicorn/prefer-global-this": "warn",
+ // Too new
+ "typescript/prefer-nullish-coalescing": "off",
+ "typescript/prefer-optional-chain": "off",
+ "oxc/no-optional-chaining": [ "error", { "message": "es2018 target does not support optional chaining operator"} ]
+ },
+ "env": {
+ "browser": true,
+ "greasemonkey": true,
+ "es2018": true,
+ "es2019": false
+ },
+ "globals": {
+ "GM": "writable",
+ "unsafeWindow": "writable"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index 608ab2c..b8bc053 100644
--- a/README.md
+++ b/README.md
@@ -36,8 +36,8 @@ To add a script:
| [Twitch Hide Content Disclosure](#THCD) | [install][raw-THCD] | N/A | :heavy_check_mark: | MIT | Jun 29, 2023 | Dec 12, 2024 |
| [Twitch Hide Channel Leaderboard](#THCL) | [install][raw-THCL] | N/A | :heavy_check_mark: | MIT | Jun 19, 2020 | Dec 12, 2024 |
| [Twitch Hide Overlay Ads](#THOA) | [install][raw-THOA] | N/A | :heavy_check_mark: | MIT | Dec 14, 2023 | Dec 12, 2024 |
-| [Twitch Transparent Video Stats](#TTVS) | [install][raw-TTVS] | N/A | :heavy_check_mark: | MIT | May 19, 2021 | Dec 12, 2024 |
-| [GitHub Repo Network Tab](#GRNT) | [install][raw-GRNT] | N/A | :heavy_check_mark: | MIT | Apr 6, 2020 | Feb 1, 2024 |
+| [Twitch Transparent Video Stats](#TTVS) | [install][raw-TTVS] | N/A | :heavy_check_mark: | MIT | May 19, 2021 | Feb 9, 2026 |
+| [GitHub Repo Network Tab](#GRNT) | [install][raw-GRNT] | N/A | :heavy_check_mark: | MIT | Apr 6, 2020 | Feb 27, 2026 |
| [Bigger GitHub Network Graph](#BGNG) | [install][raw-BGNG] | N/A | :heavy_check_mark: | MIT | Apr 12, 2020 | Oct 28, 2021 |
| [GitHub Notification Page Tweaks](#GNPT) | [install][raw-GNPT] | N/A | :heavy_check_mark: | MIT | Oct 22, 2020 | Aug 3, 2021 |
| [GitHub Sticky Editor Header](#GSEH) | [install][raw-GSEH] | N/A | :heavy_check_mark: | MIT | Nov 24, 2021 | Nov 24, 2021 |
@@ -50,12 +50,12 @@ To add a script:
| [Wider Google Form Fields](#WGFF) | [install][raw-WGFF] | N/A | :heavy_check_mark: | MIT | Sep 30, 2021 | Aug 19, 2022 |
| [Correct Google Form Correctness](#GFCC) | [install][raw-GFCC] | N/A | :heavy_check_mark: | MIT | Nov 9, 2021 | Nov 9, 2021 |
| [Google Search Lean Query Updates](#GSLQU) | [install][raw-GSLQU] | N/A | :heavy_check_mark: | MIT | Jul 12, 2023 | Sep 28, 2024 |
-| [Google Search Footer Privacy](#GSFP) | [install][raw-GSFP] | N/A | :heavy_check_mark: | MIT | Dec 30, 2023 | Dec 30, 2023 |
+| [Google Search Footer Privacy](#GSFP) | [install][raw-GSFP] | N/A | :heavy_check_mark: | MIT | Dec 30, 2023 | Dec 16, 2025 |
| [Roll20 Nonscrolling Number Fields](#RNNF) | [install][raw-RNNF] | N/A | :heavy_check_mark: | MIT | Jan 23, 2021 | Apr 5, 2021 |
| [Bypass Blogspot's Blogger IFrame](#BBBI) | [install][raw-BBBI] | N/A | :heavy_check_mark: | MIT | Jun 2, 2021 | May 1, 2022 |
| [Foxaholic Fixes](#FoxF) | [install][raw-FoxF] | N/A | :heavy_check_mark: | MIT | Jun 2, 2021 | Aug 27, 2021 |
| [Mitigate Target \_blank Risk](#MTBR) | [install][raw-MTBR] | N/A | :heavy_check_mark: | MIT | Aug 27, 2021 | Nov 23, 2021 |
-| [MSYS2 Package Description In Title](#MDIT) | [install][raw-MDIT] | N/A | :heavy_check_mark: | MIT | Apr 28, 2021 | Sep 28, 2024 |
+| [MSYS2 Package Description In Title](#MDIT) | [install][raw-MDIT] | N/A | :heavy_check_mark: | MIT | Apr 28, 2021 | Feb 9, 2026 |
| [Minecraft CurseForge Title Tweaks](#MCTT) | [install][raw-MCTT] | N/A | :heavy_check_mark: | MIT | Apr 20, 2022 | Jun 18, 2022 |
| [Another "Open In Steam" Button](#OISB) | [install][raw-OISB] | N/A | :heavy_check_mark: | MIT | Nov 25, 2022 | Nov 25, 2022 |
| [Ubuntu Packages Description In Title](#UPDIT) | [install][raw-UPDIT] | N/A | :heavy_check_mark: | MIT | May 11, 2023 | May 11, 2023 |
@@ -71,6 +71,7 @@ To add a script:
| [StackExchange Wide Mode](#SEWM) | [install][raw-SEWM] | N/A | :heavy_check_mark: | MIT | Jun 20, 2024 | Jun 20, 2024 |
| [Better IzzyOnDroid App Titles](#BIAT) | [install][raw-BIAT] | N/A | :heavy_check_mark: | MIT | May 2, 2024 | May 22, 2025 |
| [Better F-Droid App Titles](#BFAT) | [install][raw-BFAT] | N/A | :heavy_check_mark: | MIT | Aug 15, 2025 | Aug 15, 2025 |
+| [Better Greasyfork Page Titles](#BGPT) | [install][raw-BGPT] | N/A | :heavy_check_mark: | MIT | Nov 15, 2025 | Nov 15, 2025 |
---
@@ -334,7 +335,7 @@ e.g. Setting `customAllowedOrigins` to `'http://wordpress.com https://stackexcha
### MSYS2 Package Description In Title
-Include the package description on the tab title for a package's page on packages.msys2.org/packages
+Include the package description on the tab title for a package's page on packages.msys2.org
[[Install]][raw-MDIT]
@@ -485,6 +486,15 @@ Adds app description to page titles where possible.
[[Install]][raw-BFAT]
+---
+
+
+### Better Greasyfork Page Titles
+
+Include userscript descriptions in page titles on Greasy Fork (and Sleazy Fork)
+
+[[Install]][raw-BGPT]
+
---
## Legacy Workaround Scripts
@@ -622,6 +632,7 @@ Additionally, I do occasionally take requests for simple scripts, so feel free t
[raw-SEWM]: /stackexchange_wide_mode.user.js?raw=1
[raw-BIAT]: /izzyondroid_description_in_title.user.js?raw=1
[raw-BFAT]: /fdroid_app_description_in_title.user.js?raw=1
+[raw-BGPT]: /greasyfork_better_page_titles.user.js?raw=1
[raw-DFSF]: /legacy_browser_workarounds/discourse_forum_scroll_fixer.user.js?raw=1
diff --git a/build/ABOUT.md b/build/ABOUT.md
new file mode 100644
index 0000000..e728bb3
--- /dev/null
+++ b/build/ABOUT.md
@@ -0,0 +1,88 @@
+This file contains instructions for using the readme automation in this directory, and also a todo list.
+
+# Instructions
+## Prerequisites
+The following tools must be available on the `PATH`:
+ * Python (minimum version: 3.7)
+ * [MiniJinja CLI](https://github.com/mitsuhiko/minijinja)
+ * [jq](https://github.com/stedolan/jq)
+ * Bash/Zsh/Ksh/other-sh-interpreter
+ * git
+
+# What the hell do I do with any of this?
+Run `./run.sh --help` for instructions on what needs to be done manually
+when adding a new userscript.
+
+Otherwise, run `./run.sh`, then manually compare:
+ * `./data_files/main_script_manifest.json` <-> `./new_main_manifest.json`
+ * `./data_files/legacy_scripts.json` <-> `./new_legacy_manifest.json`
+ * `../README.md` <-> `./output.md`
+If satisfied, move/copy the right-hand files to the (version-controlled)
+left-hand files.
+
+Then add the changes and commit.
+
+# TODO list
+1. [X] The updater script, templates, and data files should reside under a directory named something like `build` or `deploy`
+
+2. [X] Clear fucking instructions on how to run this shit.
+
+ 1. [X] How should the updater know which userscript files to check for updates?
+ Currently it only checks files specified in the data files.
+ 2. [X] I don't believe the script currently understands how to read from multiple separate data files; should it?
+ Script now reads and updates both the main and legacy script manifests separately.
+ Support has been added for alternate keys to access the list of scripts in a file,
+ as the legacy manifest uses 'legacy_scripts', while the main manifest uses 'scripts'.
+
+ Running `./update_script_manifest.py` with Python3.7 or later and the
+ non-empty environment variable `WRITE_FILES` will produce updated
+ `new_main_manifest.json` and `new_legacy_manifest.json` from
+ `data_files/main_script_manifest.json` and `data_files/legacy_scripts.json`.
+ The updated readme can then be written to `output.md` by running:
+```bash
+jq -rs 'reduce .[] as $item ({}; . * $item)' 'data_files/general_url_references.json' new_legacy_manifest.json new_main_manifest.json |
+minijinja-cli --format json templates/primary_template.md.j2 - > output.md
+```
+
+**For convenience, simply run `./run.sh`**
+
+3. [ ] Automatically display (?git-?)diff of data file after running updater script.
+
+ 1. [-] Do I want to ultimately want to write back to the original data file, or should I write to a temporary file, then compare them,
+ and prompt for confirmation before overwriting the original? What if I want only part of the changes?
+ Currently all outputs go to temporary files that must be manually checked, copied (if good), and cleaned up.
+
+4. [X] If the metadata block of a script doesn't contain the non-standard `@createdAt` property,
+ and the script doesn't already have a `created` value in the data file,
+
+ _EITHER_
+ 1. Attempt to check the creation and modified date of the script file according to the file system. Keep the older of the two.
+ 2. Call out to git to determine when the script was first added to the repository.
+ 3. Set the earlier of the two as the `created` value in the data file.
+
+ _OR_
+
+ set the `created` value in the data file to the current date.
+
+ The first scenario seems preferable, but in the interest of speed and
+ convenience, the second option is employed.
+
+5. [ ] Support automatic detection of version-controlled scripts not yet in a data file, and automatically add them to the appropriate one.
+ This should also handle adding the `created` date if necessary.
+
+6. [ ] Decide on some process to automatically assign an `anchorString`.
+ Consider ditching custom anchor strings entirely in favor of just using the name of the script file, sans extension.
+
+7. [ ] Figure out what part of `getUpdatedScriptData` can be replaced with a call to `dict.update()`.
+
+8. [ ] Investigate converting some part of `findScriptData` into a generator.
+
+9. [ ] Vendor icons into a repo (Projects/third_party/asset_archive) (Projects/Userscripts/notes/icon_index.txt)
+ 1. [ ] Add some kind of disclaimer section mentioning that site names and logos are property of their original owners.
+
+10. [ ] Group scripts under collapsible `section`/`detail` tags according to the sites they affect.
+ 1. [ ] Support a *manually added* `group` property in the data files to denote a named group; scripts without (a non-empty, non-null value for) this property should be under an "ungrouped" group, and appear only after all other groups.
+
+11. [X] Temporary local branch in which to test repository layout with new automation files included.
+
+12. [X] Finish/fix date parsing and conversion for the non-standard `createdAt` property in script metadata, so that it can be stored *sensibly* in the manifest.
diff --git a/build/data_files/legacy_scripts.json b/build/data_files/legacy_scripts.json
index 32f83fb..dc30e2f 100644
--- a/build/data_files/legacy_scripts.json
+++ b/build/data_files/legacy_scripts.json
@@ -5,70 +5,77 @@
"anchorString": "DFSF",
"path": "/legacy_browser_workarounds/discourse_forum_scroll_fixer.user.js",
"license": "MIT",
- "autoUpdates": ":heavy_check_mark:",
+ "autoUpdates": true,
"created": "Jun 29, 2024",
"updated": "Mar 23, 2025",
- "desc": "Fix for Discourse forums breaking the ability to scroll on browsers that are too old or are blocking some script or other."
+ "desc": "Fix for Discourse forums breaking the ability to scroll on browsers that are too old or are blocking some script or other.",
+ "version": "1.0.4"
},
{
"name": "GitHub Line Hyperlink Workaround",
"anchorString": "GLHW",
"path": "/legacy_browser_workarounds/github_line_hyperlink_workaround.user.js",
"license": "MIT",
- "autoUpdates": ":heavy_check_mark:",
+ "autoUpdates": true,
"created": "Aug 10, 2022",
"updated": "Nov 18, 2022",
- "desc": "Add simple onclick handlers to the line numbers when viewing files on GitHub, as the normal behavior of linking directly to a clicked line number seems to have broken on legacy browsers as a result of some change to the implementation."
+ "desc": "Add simple onclick handlers to the line numbers when viewing files on GitHub, as the normal behavior of linking directly to a clicked line number seems to have broken on legacy browsers as a result of some change to the implementation.",
+ "version": "1.1.0"
},
{
"name": "GitHub Notifications Archive Workaround",
"anchorString": "GNAW",
"path": "/legacy_browser_workarounds/github_notifications_archive_workaround.user.js",
"license": "MIT",
- "autoUpdates": ":heavy_check_mark:",
+ "autoUpdates": true,
"created": "Jul 10, 2022",
"updated": "Mar 12, 2023",
- "desc": "Quick and simple redirect to work around strange behavior of being sent to github.com/notifications/beta/archive when marking notifications as done."
+ "desc": "Quick and simple redirect to work around strange behavior of being sent to github.com/notifications/beta/archive when marking notifications as done.",
+ "version": "1.1.0"
},
{
"name": "GitHub Collapsed Details Workaround",
"anchorString": "GCDW",
"path": "/legacy_browser_workarounds/github_collapsed_details_workaround.user.js",
"license": "MIT",
- "autoUpdates": ":heavy_check_mark:",
+ "autoUpdates": true,
"created": "Sep 30, 2022",
"updated": "Jun 10, 2023",
- "desc": "Add simple onclick handlers to the collapsed details of commits on GitHub, as the normal behavior of expanding the ellipses to the full commit message when clicked seems to have broken on legacy browsers as a result of some change to the implementation."
+ "desc": "Add simple onclick handlers to the collapsed details of commits on GitHub, as the normal behavior of expanding the ellipses to the full commit message when clicked seems to have broken on legacy browsers as a result of some change to the implementation.",
+ "version": "1.2.1"
},
{
"name": "GitHub Lazy Release Asset Workaround",
"anchorString": "GLRAW",
"path": "/legacy_browser_workarounds/github_lazy_release_asset_workaround.user.js",
"license": "MIT",
- "autoUpdates": ":heavy_check_mark:",
+ "autoUpdates": true,
"created": "Oct 8, 2022",
"updated": "Mar 12, 2023",
- "desc": "Multiple fixes related to user-downloadable asset files on GitHub for users of legacy browsers.\n- Fetch asset list for releases, as the code that should already have been responsible for that is too modern, and is thus never even attempted on legacy browsers.\n- Fix the timestamps on the release page(s), most of which are within asset lists.\n- Slightly changes normal behavior by automatically showing __all__ assets for the first release on the page, whether that's two assets or fourty assets."
+ "desc": "Multiple fixes related to user-downloadable asset files on GitHub for users of legacy browsers.\n- Fetch asset list for releases, as the code that should already have been responsible for that is too modern, and is thus never even attempted on legacy browsers.\n- Fix the timestamps on the release page(s), most of which are within asset lists.\n- Slightly changes normal behavior by automatically showing __all__ assets for the first release on the page, whether that's two assets or fourty assets.",
+ "version": "1.1.1"
},
{
"name": "GitHub Show Issue Comments Workaround",
"anchorString": "GSICW",
"path": "/legacy_browser_workarounds/github_show_issue_comments_workaround.user.js",
"license": "MIT",
- "autoUpdates": ":heavy_check_mark:",
+ "autoUpdates": true,
"created": "Nov 10, 2025",
- "updated": "Nov 10, 2025",
- "desc": "Manually display comments on Github issues using the JSON that's already on the page, which totally doesn't need React to accomplish."
+ "updated": "Mar 1, 2026",
+ "desc": "Manually display comments on Github issues using the JSON that's already on the page, which totally doesn't need React to accomplish.",
+ "version": "1.1.1"
},
{
"name": "StackExchange Legacy Comments Expander",
"anchorString": "SELCE",
"path": "/legacy_browser_workarounds/stackexchange_legacy_comments_expander.user.js",
"license": "MIT",
- "autoUpdates": ":heavy_check_mark:",
+ "autoUpdates": true,
"created": "Nov 6, 2022",
"updated": "Nov 2, 2023",
- "desc": "Replace 'Show X more comments' handler for StackExchange sites to better support older browsers; in particular, this enables showing all comments when using Chromium 72."
+ "desc": "Replace 'Show X more comments' handler for StackExchange sites to better support older browsers; in particular, this enables showing all comments when using Chromium 72.",
+ "version": "1.1.0"
}
]
-}
+}
\ No newline at end of file
diff --git a/build/data_files/main_script_manifest.json b/build/data_files/main_script_manifest.json
index 95058a6..5926c55 100644
--- a/build/data_files/main_script_manifest.json
+++ b/build/data_files/main_script_manifest.json
@@ -9,7 +9,7 @@
"created": "Apr 4, 2020",
"updated": "Oct 27, 2020",
"desc": "**This script should no longer be needed after Google's removal of overlay ads on April 6th, 2023.**
\n
\nYou know those little overlay advertisements that pop up on the bottom center of YouTube videos? If those really annoy you, this simple userscript (really just a userstyle wrapped into a userscript) will help by simply preventing them from rendering.
\nNote that this _does not_ affect other ads.",
- "version": "1.0"
+ "version": "1.0.0"
},
{
"name": "Fix YouTube Player Bottom Gradient",
@@ -20,7 +20,7 @@
"created": "Feb 26, 2021",
"updated": "Mar 30, 2021",
"desc": "This \"fixes\" the excessively large bottom gradient area that sometimes appears on the YouTube video player when the mouse cursor is within the player frame.
\n**So far, I've only seen the phenomenon that led me to write this while using Vivaldi.**",
- "version": "1.0"
+ "version": "1.0.0"
},
{
"name": "YouTube Channel Keyboard Protection",
@@ -31,7 +31,7 @@
"created": "Nov 13, 2021",
"updated": "May 1, 2022",
"desc": "Prevents YouTube from hijacking the Up/Down arrow keys on channel pages, as it likes to do sometimes (Left and Right arrow keys are okay though, because those don't control page scrolling).",
- "version": "1.1"
+ "version": "1.1.0"
},
{
"name": "Twitch Hide Content Disclosure",
@@ -73,9 +73,9 @@
"license": "MIT",
"autoUpdates": true,
"created": "May 19, 2021",
- "updated": "Dec 12, 2024",
+ "updated": "Feb 9, 2026",
"desc": "Makes the video stats overlay on Twitch.tv video player partially transparent, so as to avoid obscuring the stream so much.",
- "version": "1.1.1"
+ "version": "1.1.2"
},
{
"name": "GitHub Repo Network Tab",
@@ -84,9 +84,9 @@
"license": "MIT",
"autoUpdates": true,
"created": "Apr 6, 2020",
- "updated": "Feb 1, 2024",
+ "updated": "Feb 27, 2026",
"desc": "Adds a navigation tab for faster access to the 'Network' page of a repository.
\n
\nKnown bugs:\n- Occasionally the tab fails to be added, with no clear explanation or pattern. If this occurs, simply reload the page.\n- When switching between repository tabs, the network tab sometimes disappears, and something about the way GitHub does page navigation within a repository doesn't cause this script to be re-injected. Short of constantly checking state on a sub-second timer, or using a mutation observer, I don't know how else to solve this.",
- "version": "1.7.2"
+ "version": "1.8.0"
},
{
"name": "Bigger GitHub Network Graph",
@@ -97,7 +97,7 @@
"created": "Apr 12, 2020",
"updated": "Oct 28, 2021",
"desc": "Makes the timeline on the Network page of GitHub repositories utilize more of that available whitespace on the sides.
\nStill can't seem to make it use all the space on the right side though...
\n
\nEssentially a subset of [Wide GitHub](https://github.com/xthexder/wide-github) which, of course, I only realized after I'd written this.
\nOh well, someone will probably find this useful.",
- "version": "1.0"
+ "version": "1.0.0"
},
{
"name": "GitHub Notification Page Tweaks",
@@ -108,7 +108,7 @@
"created": "Oct 22, 2020",
"updated": "Aug 3, 2021",
"desc": "Why does GitHub's beta notifications inbox use a \"More\" dropdown when there's more than enough space for the 2 elements within?
\nI don't know, and I dislike having to open a dropdown just to mark something as \"read\", so I did something about it.",
- "version": "1.1"
+ "version": "1.1.0"
},
{
"name": "GitHub Sticky Editor Header",
@@ -119,7 +119,7 @@
"created": "Nov 24, 2021",
"updated": "Nov 24, 2021",
"desc": "Makes the header of the (text) file editor on GitHub sticky.
\nWritten because I got sick and tired of having to move up and down the page to change to and from the preview while editing this README.",
- "version": "1.0"
+ "version": "1.0.0"
},
{
"name": "GitLab Description In Title",
@@ -130,7 +130,7 @@
"created": "May 22, 2021",
"updated": "Aug 3, 2021",
"desc": "Attempts to improve the page titles on GitLab by including the contents of the page's description, if one is provided.
\nThis also replaces instances of Unicode character 0x00B7, \"Middle Dot\", in the title, as I've found that particular character\nhas strangely led some editors to erroneously read and write the text in undesired encodings, such as GB2312, instead of UTF-8.",
- "version": "1.1"
+ "version": "1.1.0"
},
{
"name": "Prettier Lib.rs Preformatted Code",
@@ -141,7 +141,7 @@
"created": "Jul 5, 2020",
"updated": "Mar 30, 2021",
"desc": "Makes `
` blocks on lib.rs look more like they do on crates.io; lib.rs is so much faster thanks to reduced JS use, but it's not as pretty.",
- "version": "1.0"
+ "version": "1.0.0"
},
{
"name": "Lib.rs Description In Title",
@@ -152,7 +152,7 @@
"created": "Apr 28, 2021",
"updated": "May 11, 2021",
"desc": "Replace the unhelpful part of the tab title on a lib.rs crate's page with the short description of the crate, if one is provided.
\nConvenient for bookmarking and tab-saving extensions, as pages are typically stored according to their titles.",
- "version": "1.0"
+ "version": "1.0.0"
},
{
"name": "Crates.io Description In Title",
@@ -174,7 +174,7 @@
"created": "Jun 19, 2020",
"updated": "Apr 5, 2021",
"desc": "Do you hate that Gmail shows a toast notification that blocks functional regions of the UI after you do something to any email? Me too!
\nThis little change should help mitigate the problem by moving the toast notification to the bottom center of the screen.",
- "version": "1.0"
+ "version": "1.0.0"
},
{
"name": "Google Meet Ignore Hardware Disabled",
@@ -185,7 +185,7 @@
"created": "Mar 3, 2023",
"updated": "Mar 3, 2023",
"desc": "A.K.A \"I know my hardware is disabled, Google\"
\nThanks Google, but I'm well aware that my browser hasn't given you permission to access my hardware; I don't need you showing a prompt that can't be closed with a keypress.",
- "version": "1.0"
+ "version": "1.0.0"
},
{
"name": "Wider Google Form Fields",
@@ -196,7 +196,7 @@
"created": "Sep 30, 2021",
"updated": "Aug 19, 2022",
"desc": "Widens the input fields in google forms from 50% to 100% of the question element (minus padding).",
- "version": "1.1"
+ "version": "1.1.0"
},
{
"name": "Correct Google Form Correctness",
@@ -207,7 +207,7 @@
"created": "Nov 9, 2021",
"updated": "Nov 9, 2021",
"desc": "Make fields that have been manually marked as correct take on the same styling as fields that exactly matched the preset correct answer.",
- "version": "1.0"
+ "version": "1.0.0"
},
{
"name": "Google Search Lean Query Updates",
@@ -227,9 +227,9 @@
"license": "MIT",
"autoUpdates": true,
"created": "Dec 30, 2023",
- "updated": "Dec 30, 2023",
+ "updated": "Dec 16, 2025",
"desc": "Hide the \"Location\" part of the footer on Google Search results, and don't show the email address of the current user.",
- "version": "1.1.0"
+ "version": "1.1.1"
},
{
"name": "Roll20 Nonscrolling Number Fields",
@@ -240,7 +240,7 @@
"created": "Jan 23, 2021",
"updated": "Apr 5, 2021",
"desc": "This should disable changing the value of any numeric fields on Roll20 character sheets by scrolling.
\nTODO: Replace the use of setTimeout with a MutationObserver.",
- "version": "1.0"
+ "version": "1.0.0"
},
{
"name": "Bypass Blogspot's Blogger IFrame",
@@ -251,7 +251,7 @@
"created": "Jun 2, 2021",
"updated": "May 1, 2022",
"desc": "Unhide the page body and hide obstructive injected iframes on some Blogspot pages, which use those methods for reasons like discouraging ad blocking.",
- "version": "1.1"
+ "version": "1.1.0"
},
{
"name": "Foxaholic Fixes",
@@ -262,7 +262,7 @@
"created": "Jun 2, 2021",
"updated": "Aug 27, 2021",
"desc": "Fix Foxaholic's deliberate breaking of context menus, keypresses, and text selection.",
- "version": "1.0"
+ "version": "1.0.0"
},
{
"name": "Mitigate Target \\_blank Risk",
@@ -273,7 +273,7 @@
"created": "Aug 27, 2021",
"updated": "Nov 23, 2021",
"desc": "Appends `rel=\"noopener noreferrer\"` to every link (HTMLAnchorElement, not to be confused with HTMLLinkElement) that has `target=\"_blank\"`, preventing a possible security risk.
\nThis **_will_** break links to some sites, likely any links that would otherwise have opened in a new tab by default.
\nUsers may choose to ignore links from additional url origins, by setting the `customAllowedOrigins` key in the script's value storage to a list of origins, delimited by a single space character, `' '`.
\ne.g. Setting `customAllowedOrigins` to `'http://wordpress.com https://stackexchange.com https://novelupdates.com'` will prevent this script from modifying links to those origins.",
- "version": "1.1"
+ "version": "1.1.0"
},
{
"name": "MSYS2 Package Description In Title",
@@ -282,9 +282,9 @@
"license": "MIT",
"autoUpdates": true,
"created": "Apr 28, 2021",
- "updated": "Sep 28, 2024",
- "desc": "Include the package description on the tab title for a package's page on packages.msys2.org/packages",
- "version": "1.2.0"
+ "updated": "Feb 9, 2026",
+ "desc": "Include the package description on the tab title for a package's page on packages.msys2.org",
+ "version": "1.2.1"
},
{
"name": "Minecraft CurseForge Title Tweaks",
@@ -295,7 +295,7 @@
"created": "Apr 20, 2022",
"updated": "Jun 18, 2022",
"desc": "Modifies the format of the page title for some of CurseForge's Minecraft pages.",
- "version": "1.1"
+ "version": "1.1.0"
},
{
"name": "Another \"Open In Steam\" Button",
@@ -306,7 +306,7 @@
"created": "Nov 25, 2022",
"updated": "Nov 25, 2022",
"desc": "As the name should imply, this is my own version of a script which adds a new button on Steam's steampowered and steamcommunity sites to open the current page in the Steam app.
\nSome of the CSS used was borrowed from https://greasyfork.org/en/scripts/454372-open-steam-url after I spent well over an hour fiddling with my own CSS in the pre-dawn hours, and decided I wasn't going to manage much better.",
- "version": "1.0"
+ "version": "1.0.0"
},
{
"name": "Ubuntu Packages Description In Title",
@@ -317,7 +317,7 @@
"created": "May 11, 2023",
"updated": "May 11, 2023",
"desc": "Try to provide a minimal, yet meaningful, page title that includes the package description on Ubuntu's package search/archive website.",
- "version": "1.0"
+ "version": "1.0.0"
},
{
"name": "Quietly Reject StackExchange Cookies",
@@ -328,7 +328,7 @@
"created": "May 14, 2023",
"updated": "May 14, 2023",
"desc": "Hide the pesky cookie permission requests on StackExchange sites, which don't actually appear to set even \"necessary\" cookies until the user responds to the permission prompt.
\nAlso hides a few other little things that just don't warrant another tiny script.",
- "version": "1.0"
+ "version": "1.0.0"
},
{
"name": "PyPI Description In Title",
@@ -339,7 +339,7 @@
"created": "May 31, 2023",
"updated": "Aug 15, 2024",
"desc": "Rewrite the page title for a PyPI package to include a brief summary, when available.
\n
\nAlso doesn't use that centered dot character as a separator.",
- "version": "1.0"
+ "version": "1.0.0"
},
{
"name": "Simple URL Tracker Cleaner",
@@ -361,7 +361,7 @@
"created": "Apr 8, 2022",
"updated": "Jul 2, 2023",
"desc": "Hide posts from arbitrary subreddits (unless specifically looking at them, of course).
\n
\nOnly works for old.reddit.com, not www.reddit.com, because not only does the latter use a DOM structure that makes it unsuitable for applying styles to entire post-elements by the CSS selector of the child element holding the subreddit, it _also_ commits the desktop (and frankly, even mobile) user-experience war-crime of infinite pagination (endless scrolling). **TLDR: Modern Reddit UI sucks, and supporting it would take more effort than I'm willing to put in to this for my own use.**",
- "version": "1.1"
+ "version": "1.1.0"
},
{
"name": "ScribbleHub Reading List Upgrades",
@@ -372,7 +372,7 @@
"created": "Oct 7, 2022",
"updated": "Jan 19, 2024",
"desc": "Allows hiding novels the user is caught up on from their reading lists, adds the current reading list name to the page title, and more planned.",
- "version": "1.2"
+ "version": "1.2.0"
},
{
"name": "NovelUpdates Reading List Upgrades",
@@ -383,7 +383,7 @@
"created": "Jul 8, 2022",
"updated": "Nov 16, 2022",
"desc": "Allows hiding novels the user is caught up on from their reading lists, adds the current reading list name to the page title, and more planned.",
- "version": "1.0"
+ "version": "1.0.0"
},
{
"name": "Softpedia Improvements",
@@ -450,6 +450,17 @@
"updated": "Aug 15, 2025",
"desc": "Adds app description to page titles where possible.",
"version": "1.0.0"
+ },
+ {
+ "name": "Better Greasyfork Page Titles",
+ "anchorString": "BGPT",
+ "path": "/greasyfork_better_page_titles.user.js",
+ "license": "MIT",
+ "autoUpdates": true,
+ "created": "Nov 15, 2025",
+ "updated": "Nov 15, 2025",
+ "desc": "Include userscript descriptions in page titles on Greasy Fork (and Sleazy Fork)",
+ "version": "1.0.0"
}
]
}
\ No newline at end of file
diff --git a/build/run.sh b/build/run.sh
index 1c18fac..b98c442 100644
--- a/build/run.sh
+++ b/build/run.sh
@@ -7,10 +7,12 @@ usage(){
printf '%s\n' \
'This script is used to generate updated versions of readme and manifest files.' \
'To "register" a new userscript for inclusion in the readme, add a new' \
- 'object with both `path` and `anchorString` values to the `scripts` array' \
- 'in `main_script_manifest.json` before running this script.' \
+ 'object with both `path` and `anchorString` values to the' \ '`scripts` array (or other such array where relevant)' \
+ 'in `main_script_manifest.json` (and/or `legacy_scripts.json`)' \
+ 'before running this script.' \
'Updated manifest and readme files will be created in the `build` directory,' \
- 'and named `newdata.json` and `output.md`, respectively.' \
+ 'and named `new_main_manifest.json`/`new_legacy_manifest.json`' \
+ 'and `output.md`, respectively.' \
'They can be manually copied over the originals.'
return 0
}
@@ -32,21 +34,19 @@ case "$1" in
;;
esac
+## POSIX way to determine the location of an executed shell script https://stackoverflow.com/a/29835459
+thisScriptLocation="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P)"
+## Ensure this script is run in the context of it's parent directory, which is within the git repository.
+if [ "$PWD" != "$thisScriptLocation" ]; then
+ exec env -C "$thisScriptLocation" "./${0##*/}" "$@"
+fi
+
repoRoot="$(git rev-parse --show-toplevel)"
dataDir="${repoRoot}/build/data_files"
templateDir="${repoRoot}/build/templates"
-OLDPWD="${PWD}"
-restorePWD(){
- cd "${OLDPWD}" >/dev/null 2>&1 || return
-}
-trap restorePWD EXIT
-
-# shellcheck disable=SC2164
-cd "${repoRoot}/build" >/dev/null 2>&1
-
-TEST_READER=1 python3 "${repoRoot}/build/update_script_manifest.py"
-jq -rs 'reduce .[] as $item ({}; . * $item)' "${dataDir}/general_url_references.json" "${dataDir}/legacy_scripts.json" newdata.json | \
+WRITE_FILES=1 python3 "${repoRoot}/build/update_script_manifest.py"
+jq -rs 'reduce .[] as $item ({}; . * $item)' "${dataDir}/general_url_references.json" new_legacy_manifest.json new_main_manifest.json | \
minijinja-cli --format json "${templateDir}/primary_template.md.j2" - > output.md
################
diff --git a/build/update_script_manifest.py b/build/update_script_manifest.py
index b9cf401..6ba36e7 100644
--- a/build/update_script_manifest.py
+++ b/build/update_script_manifest.py
@@ -83,7 +83,7 @@ def __init__(self, value: str):
self.minor: int = int(minor, 10)
self.patch: int = int(patch, 10) or 0
self.originalValue: str = value
- self.value: str = value + '.0' if patch == '' else value
+ self.value: str = f'{value}.{self.patch}' if value.count('.') < 2 else value
def __str__(self) -> str:
return self.value
@@ -228,25 +228,25 @@ def getUpdatedScriptData(self, scriptFileMeta: Dict[str, Union[str, bool]], scri
scriptItem['updated'] = scriptItem['created']
elif ('version' in scriptItem) and VersionString(scriptItem['version']) < scriptFileMeta['version']:
# The file's version info was increased since the last time the data file was updated by this script;
- # record the new "updated" date as the current date, and update the "version" to match the script's meta block
+ # record the new "updated" date as the current date
scriptItem['updated'] = self._missingCreatedValue
if self.__needsAttrForDataFile(scriptItem, 'license'):
# Get license field from script file's metadata
scriptItem['license'] = scriptFileMeta['license'] or self._missingLicenseValue
- # Unconditionally update the version field
+ # Unconditionally update the "version" field to match the script's meta block
scriptItem['version'] = str(VersionString(scriptFileMeta['version'])) or self._missingVersionValue
# Unconditionally update the autoUpdates field
scriptItem['autoUpdates'] = scriptFileMeta['autoUpdates'] or 'false'
return self.sortScriptMeta(scriptItem)
- def findScriptData(self) -> None:
+ def findScriptData(self, scriptArrayKey: str = 'scripts') -> None:
# Must be called after either `readJSONFile`/`readJSONStr`/`readJSONDict`
# FIXME: With the large blocks of data this function will eventually be producing and returning,
# it would probably make sense to turn this into a Generator method, or at least make the
# `for` loop into its own inner-function and make _that_ a Generator.
adjusted: List[Dict[str, Union[str, bool]]] = []
- for scriptItem in self.dataFileContents['scripts']:
+ for scriptItem in self.dataFileContents[scriptArrayKey]:
scriptItem: Dict[str, str] # Shut up linter wrongly complaining that scriptItem is a string.
if 'path' not in scriptItem:
# TODO: support automatic detection of version-controlled scripts not in a data file,
@@ -270,29 +270,35 @@ def findScriptData(self) -> None:
adjusted.append(self.getUpdatedScriptData(scriptFileMeta, scriptItem))
print(adjusted) # FIXME: Do something with this value other than just print it to stdout.
- self.dataFileContents['scripts'] = adjusted # TEMP
+ self.dataFileContents[scriptArrayKey] = adjusted # TEMP
- def writeTest(self) -> None: # TEMP
- with open('newdata.json', 'w', encoding=defaultFileEncoding) as outputFile:
+ def writeNew(self, toFile: str='new_main_manifest.json') -> None:
+ with open(toFile, 'w', encoding=defaultFileEncoding) as outputFile:
json.dump(self.dataFileContents, outputFile, indent="\t")
- print('Go diff main_script_manifest.json and newdata.json')
# TODO: ?Decide on some process to automatically assign an anchorString?
if __name__ == '__main__':
- from os import getenv
from functools import partial
- hasDebugVar: bool = bool(getenv('DEBUG'))
+ hasDebugVar: bool = bool(os.getenv('DEBUG'))
if hasDebugVar:
mayPrint = partial(print, file=sys.stderr) #noqa: F811, RUF100
- if bool(getenv('TEST_READER')):
- updater = DataUpdater()
- # TODO: utilize __file__ to make these paths relative to the script, rather than the working directory
- updater.readJSONFile('./data_files/main_script_manifest.json')
- here = os.getcwd()
- os.chdir('..')
- updater.findScriptData()
- os.chdir(here)
- updater.writeTest()
+ updater = DataUpdater()
+ # Get the parent directory of this script.
+ here = os.path.dirname(__file__)
+
+ updater.readJSONFile(here + '/data_files/main_script_manifest.json')
+ os.chdir(here + '/..')
+ updater.findScriptData()
+ if bool(os.getenv('WRITE_FILES')):
+ updater.writeNew(here + '/new_main_manifest.json')
+ print('Go diff main_script_manifest.json and new_main_manifest.json')
+
+ updater.readJSONFile(here + '/data_files/legacy_scripts.json')
+ os.chdir(here + '/..')
+ updater.findScriptData('legacy_scripts')
+ if bool(os.getenv('WRITE_FILES')):
+ updater.writeNew(here + '/new_legacy_manifest.json')
+ print('Go diff legacy_scripts.json and new_legacy_manifest.json')
diff --git a/fix_youtube_player_bottom_gradient.user.js b/fix_youtube_player_bottom_gradient.user.js
index aa99c45..52111ec 100644
--- a/fix_youtube_player_bottom_gradient.user.js
+++ b/fix_youtube_player_bottom_gradient.user.js
@@ -26,7 +26,7 @@
// `);
setTimeout(function wait(){
- const playerBottomGradient = document.getElementById('movie_player').querySelector('.ytp-gradient-bottom');
+ const playerBottomGradient = document.querySelector('#movie_player .ytp-gradient-bottom');
if (playerBottomGradient){
console.log('Fixing bottom player gradient height');
playerBottomGradient.style.removeProperty('height');
diff --git a/github_repo_network_tab.user.js b/github_repo_network_tab.user.js
index b4e93df..a230395 100644
--- a/github_repo_network_tab.user.js
+++ b/github_repo_network_tab.user.js
@@ -9,6 +9,7 @@
// @exclude-match https://github.com/enterprise*
// @exclude-match https://github.com/explore*
// @exclude-match https://github.com/features*
+// @exclude-match https://github.com/github-copilot/*
// @exclude-match https://github.com/login/*
// @exclude-match https://github.com/marketplace*
// @exclude-match https://github.com/new*
@@ -25,7 +26,7 @@
// @exclude-match https://github.com/topics*
// @exclude-match https://github.com/trending*
// @exclude-match https://github.com/users/*/projects/*
-// @version 1.7.2
+// @version 1.8.0
// @createdAt 4/06/2020
// @author StaticPH
// @description Adds a navigation tab for faster access to the 'Network' page of a repository.
@@ -48,16 +49,16 @@
return location.pathname.split('/', 3).slice(1).join('/');
})();
+ const networkIconSvgHTML = '';
+
/* Honestly, I feel like creating the HTML directly is less of a hassle than creating all the elements with JavaScript */
function createBigNetworkTabHTML(){
// Exclude analytical "data-ga-click" and "data-selected-links" attributes
return '\n' +
- ' \n' +
+ '\t' + networkIconSvgHTML + '\n' +
' \n' +
' Network\n' +
- ' \n' +
+ ' \n' +
'\n';
//TODO: Intelligently determine if the link element should have 'style="visibility:hidden;"' to start with?
}
@@ -81,6 +82,53 @@
'';
}
+ function maybeFixHighlightedTab(){
+ if (location.pathname.endsWith(here + '/network') || location.pathname.endsWith(here + '/network/')){
+ let networkTab = document.querySelector('[data-tab-item="i2_1network-tab"]');
+ let insightsTab = document.querySelector('[data-tab-item="i7insights-tab"]');
+
+ if (insightsTab /*&& insightsTab.hasAttribute('aria-current')*/){
+ insightsTab.removeAttribute('aria-current');
+ insightsTab.classList.remove('selected');
+ }
+ if (networkTab){
+ networkTab.setAttribute('aria-current', 'page');
+ networkTab.classList.add('selected');
+ }
+ }
+ }
+
+ function doesNeedModernTabVariant(){
+ // Thanks for making it more annoying to find or work with meaningful elements using CSS selectors, GitHub. /s
+ return document.querySelector('nav[class*="prc-components-UnderlineWrapper"]') !== null;
+ }
+
+ function modernUIAddNetworkTab(){
+ const prTabLink = document.querySelector(`li.prc-UnderlineNav-UnderlineNavItem-syRjR > a[href="/${here}/pulls"]`);
+ if (!prTabLink){ return false; } // Not ready yet... or GitHub changed shit again.
+
+ const dataAttrs = 'data-turbo-frame="repo-content-turbo-frame" data-discover="true"';
+ const isActiveTab = (location.pathname.endsWith('/network') || location.pathname.endsWith('/network/'));
+ const tabHTML = ' \n' +
+ `\n` +
+ ` ${networkIconSvgHTML}\n` +
+ // May want to skip adding actual text manually due to css attr magic setting value from data-content
+ ' Network\n' +
+ '\n' +
+ ' ';
+ prTabLink.parentElement.insertAdjacentHTML('afterend', tabHTML);
+
+ const networkTab = document.querySelector('#bigNetworkTab');
+ if (isActiveTab){
+ const falseActiveTab = document.querySelector('li.prc-UnderlineNav-UnderlineNavItem-syRjR > a[aria-current]');
+ if (falseActiveTab){
+ falseActiveTab.removeAttribute('aria-current');
+ }
+ networkTab.setAttribute('aria-current', 'page');
+ }
+ return networkTab;
+ }
+
//TODO: Consider insertion at Nth element position, rather than relative to PR tab.
setTimeout(function wait(){
/* Find the 'Pull Requests' tab; inserting the new Network tab immediately after it ensures consistent placement. */
@@ -91,11 +139,13 @@
// Wait until the page loads in enough to have the 'Pull Requests' tab in the repository header, so that it can be used as a point of reference for element insertion
if (repoPullsTab.length !== 0){
repoPullsTab[0].insertAdjacentHTML('afterend', createBigNetworkTabHTML());
- document.getElementById('bigNetworkTab') && console.debug('Added big Network tab.');
+ /* oxlint-disable no-unused-expressions */
+ document.querySelector('#bigNetworkTab') && console.debug('Added big Network tab.');
if (repoPullsTab.length > 1){
repoPullsTab[1].insertAdjacentHTML('afterend', createSmallNetworkTabHTML());
- document.getElementById('smallNetworkTab') && console.debug('Added small Network tab.');
+ /* oxlint-disable no-unused-expressions */
+ document.querySelector('#smallNetworkTab') && console.debug('Added small Network tab.');
}
// setTimeout(function foo(){
@@ -112,34 +162,45 @@
const pullsDropdownItem = document.querySelector('details-menu li[data-menu-item="i2pull-requests-tab"]');
if (pullsDropdownItem){
pullsDropdownItem.insertAdjacentHTML('afterend', createNetworkTabInDropdownHTML());
- document.getElementById('networkTabDropdown') && console.debug('Added Network tab item to dropdown.');
+ /* oxlint-disable no-unused-expressions */
+ document.querySelector('#networkTabDropdown') && console.debug('Added Network tab item to dropdown.');
}
else if (dropdownRetries >= dropdownRetryLimit){
console.log(`Number of attempts at adding Network tab to dropdown have exceeded the limit of ${dropdownRetryLimit} attempts. Giving up.`);
}
- else{
+ else {
console.log(`Waiting ${(dropdownRetries * 500) + 500}ms for page to load further before attempting insertion of dropdown-item.`);
dropdownRetries++;
setTimeout(waitmore, (dropdownRetries * 500) + 500);
}
});
- if (location.pathname.endsWith(here + '/network') || location.pathname.endsWith(here + '/network/')){
- let networkTab = document.querySelector('[data-tab-item="i2_1network-tab"]');
- let insightsTab = document.querySelector('[data-tab-item="i7insights-tab"]');
-
- if (insightsTab /*&& insightsTab.hasAttribute('aria-current')*/){
- insightsTab.removeAttribute('aria-current');
- insightsTab.classList.remove('selected');
- }
- if (networkTab){
- networkTab.classList.add('selected');
- }
+ maybeFixHighlightedTab();
+ }
+ else if (doesNeedModernTabVariant()){
+ if (modernUIAddNetworkTab()){
+ console.log('Added Network tab to modern nagivation menu.');
+ (async function(){
+ return await setInterval(function babysit(){
+ // Compensate for React's DOM fuckery often regenerating the navigation tabs (and who knows what else) shortly after document-idle;
+ // check back every 5 seconds.
+ if (!document.querySelector('#bigNetworkTab')){
+ console.log('Compensating for React DOM fuckery regenerating the navigation tabs. Re-adding Network tab');
+ if (modernUIAddNetworkTab()){
+ console.log('Network tab re-added to modern navigation menu.');
+ };
+ }
+ }, 5000);
+ })();
+ }
+ else {
+ console.log('Modern UI required; waiting 300ms for page to load further.');
+ return setTimeout(wait, 300);
}
}
- else{
+ else {
console.log('Waiting 300ms for page to load further.');
- setTimeout(wait, 300);
+ return setTimeout(wait, 300);
}
});
})();
diff --git a/google_search_footer_privacy.user.js b/google_search_footer_privacy.user.js
index 0eac588..2acd8fd 100644
--- a/google_search_footer_privacy.user.js
+++ b/google_search_footer_privacy.user.js
@@ -3,7 +3,7 @@
// @namespace https://github.com/StaticPH
// @match *://google.com/search
// @match *://*.google.com/search
-// @version 1.1.0
+// @version 1.1.1
// @createdAt 12/30/2023, 6:16:41 PM
// @author StaticPH
// @description Hide the "Location" part of the footer on Google Search results, and don't show the email address of the current user.
@@ -40,7 +40,7 @@
/* Hide email address of currently logged in user, if applicable */
/* #EOlPnc > :nth-last-child(2):not(:first-child) > :first-child:not(:last-child), */
/* Hide Location */
- #EOlPnc > .Srfpq, .dfB0uf {
+ #EOlPnc > .Srfpq, .dfB0uf, .HddGcc > .VYM29 {
display: none !important;
}
diff --git a/greasyfork_better_page_titles.user.js b/greasyfork_better_page_titles.user.js
new file mode 100644
index 0000000..79345c8
--- /dev/null
+++ b/greasyfork_better_page_titles.user.js
@@ -0,0 +1,50 @@
+// ==UserScript==
+// @name Better Greasy Fork Page Titles
+// @namespace https://github.com/StaticPH
+// @match https://greasyfork.org/*/scripts/*
+// @match https://sleazyfork.org/*/scripts/*
+// @version 1.0.0
+// @createdAt 11/15/2025, 3:33:58 PM
+// @author StaticPH
+// @description Include userscript descriptions in page titles on Greasy Fork and Sleazy Fork
+// @license MIT
+// @updateURL https://raw.githubusercontent.com/StaticPH/Userscripts/master/greasyfork_better_page_titles.user.js
+// @downloadURL https://raw.githubusercontent.com/StaticPH/Userscripts/master/greasyfork_better_page_titles.user.js
+// @homepageURL https://github.com/StaticPH/UserScripts
+// @supportURL https://github.com/StaticPH/UserScripts/issues
+// @icon https://greasyfork.org/vite/assets/blacklogo16-DftkYuVe.png
+// @grant none
+// @noframes
+// @run-at document-end
+// ==/UserScript==
+
+(function(){
+ "use strict";
+
+ // Get the description from the visible element, rather than from
+ // the meta tag in document.head; the latter changes based on
+ // which tab (Info/Code/History/Feedback/Stats) is active.
+ const descEle = document.querySelector('#script-description');
+ // Guard against a missing description element (which should never occur).
+ const descValue = descEle !== null ? descEle.textContent.trim() : 'No description';
+ // Guard against the description element's text being only whitespace or an empty string.
+ const desc = descValue !== '' ? descValue : 'No description';
+
+ // Get the script title from the visible element, rather than from
+ // the existing document title; the latter changes based on
+ // which tab (Info/Code/History/Feedback/Stats) is active.
+ const scriptTitleEle = document.querySelector('#script-info > header > h2');
+ // Fallback to actual page title only if necessary, which is less desirable because it makes extraKeyword seem out of place on all but the Info tab.
+ const scriptTitle = scriptTitleEle !== null ? scriptTitleEle.textContent.trim() : document.title;
+
+ // For the purposes of searching through bookmarks and the like,
+ // ensure that the keyword "userscript" is always present in the title
+ const extraKeyword = scriptTitle.toLowerCase().includes('userscript') ? '' : ' userscript';
+
+ // If not viewing the first tab (Info), include the name of the tab in the updated title.
+ const tabLabelEle = document.querySelector('#script-links > .current:not(:first-of-type)');
+ const tabLabel = tabLabelEle !== null ? ` (${tabLabelEle.textContent.trim().split(' ')[0]})` : '';
+
+ document.title = `${scriptTitle}${extraKeyword}${tabLabel} — ${desc}`;
+
+})();
diff --git a/izzyondroid_description_in_title.user.js b/izzyondroid_description_in_title.user.js
index 19ff31c..7b22e9b 100644
--- a/izzyondroid_description_in_title.user.js
+++ b/izzyondroid_description_in_title.user.js
@@ -23,7 +23,7 @@
const parts = document.title.split('- IzzyOnDroid');
const tail = parts.pop();
- let summary = document.getElementById('summary').textContent;
+ let summary = document.querySelector('#summary').textContent;
summary = summary[0].toLocaleUpperCase() + summary.slice(1);
parts.push('- ' + summary + ' | IzzyOnDroid' + tail);
document.title = parts.join('');
diff --git a/legacy_browser_workarounds/github_collapsed_details_workaround.user.js b/legacy_browser_workarounds/github_collapsed_details_workaround.user.js
index ba3a2ff..a71559e 100644
--- a/legacy_browser_workarounds/github_collapsed_details_workaround.user.js
+++ b/legacy_browser_workarounds/github_collapsed_details_workaround.user.js
@@ -9,6 +9,7 @@
// @exclude-match https://github.com/enterprise*
// @exclude-match https://github.com/explore*
// @exclude-match https://github.com/features*
+// @exclude-match https://github.com/github-copilot/*
// @exclude-match https://github.com/login/*
// @exclude-match https://github.com/marketplace*
// @exclude-match https://github.com/new*
@@ -25,7 +26,7 @@
// @exclude-match https://github.com/topics*
// @exclude-match https://github.com/trending*
// @exclude-match https://github.com/users/*/projects/*
-// @version 1.2
+// @version 1.2.1
// @createdAt 9/30/2022, 9:32:58 PM
// @author StaticPH
// @description Add simple onclick handlers to the collapsed details of commits on GitHub, as the normal behavior of expanding the ellipses to the full commit message when clicked seems to have broken on legacy browsers as a result of some change to the implementation. Also fixes some other instances of non-functioning collapsing elements.
@@ -64,9 +65,12 @@
// let commitRow = evnt.target.closest('.Details').querySelector('p+*');
let commitRow = evnt.target.closest('.Details').querySelector('.Details-content--hidden, .Details-content--on') || evnt.target.closest('.Details').querySelector('.text-small').parentElement;
commitRow.classList.toggle('Details-content--hidden');
- hasRefinedGithub && commitRow.closest('.rgh-dim-bot').classList.toggle('Details--on');
+ if (hasRefinedGithub){
+ commitRow.closest('.rgh-dim-bot').classList.toggle('Details--on');
+ }
}
+ /* oxlint-disable-next-line unicorn/prefer-add-event-listener */
document.querySelectorAll('.hidden-text-expander > button.ellipsis-expander.js-details-target').forEach(expander => expander.onclick = toggleShowDetails);
if (urlRelativeToRepoDefault.startsWith('/wiki')){
@@ -110,9 +114,9 @@
evnt.target.parentElement.querySelector('[data-target="annotation-message.showLessButton"]').toggleAttribute('hidden');
}
else { // Normally implies that classList contains 'annotation--expanded'
- // replace 'annotation--expanded' with 'annotation--contracted' if the former exists, otherwise just add the latter;
- // the second case is a fallback in case the class was previously just removed entirely, rather than replaced.
- annotationBody.classList.replace('annotation--expanded', 'annotation--contracted') || annotationBody.classList.add('annotation--contracted');
+ // Always attempt to remove the `annotation--expanded` class from annotationBody, and ensure it always has the `annotation--contracted` class.
+ annotationBody.classList.remove('annotation--expanded');
+ annotationBody.classList.add('annotation--contracted');
evnt.target.toggleAttribute('hidden');
evnt.target.parentElement.querySelector('[data-target="annotation-message.showMoreButton"]').toggleAttribute('hidden');
}
diff --git a/legacy_browser_workarounds/github_lazy_release_asset_workaround.user.js b/legacy_browser_workarounds/github_lazy_release_asset_workaround.user.js
index e23c952..738d91c 100644
--- a/legacy_browser_workarounds/github_lazy_release_asset_workaround.user.js
+++ b/legacy_browser_workarounds/github_lazy_release_asset_workaround.user.js
@@ -2,7 +2,7 @@
// @name GitHub Lazy Release Assets Legacy Workaround
// @namespace https://github.com/StaticPH
// @match https://github.com/*/*/releases*
-// @version 1.1
+// @version 1.1.2
// @createdAt 10/8/2022, 4:40:25 PM
// @author StaticPH
// @description Fixes a number of things related to user-downloadable asset files on GitHub for users of legacy browsers.
@@ -40,11 +40,12 @@
'method': 'GET',
'mode': 'cors'
};
- const timestampPreset = { month: 'short', year: 'numeric', day: 'numeric', hour: 'numeric', minute: 'numeric' };
+ const timestampPreset = new Intl.DateTimeFormat(navigator.language, { numberingSystem: 'ltn', calendar: 'gregory', year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', hour12: true, minute: 'numeric' });
async function localizeTimestamp(dateObj){
- return await dateObj.toLocaleString(navigator.language, timestampPreset);
+ return await timestampPreset.format(dateObj);
}
+ /*
// While on the topic of fixing things, fix all the timestamps on the page that don't seem to want to display.
// Use the same format as GitHub would be, by utilizing the attributes of the local-time elements.
async function fixDate(ele){
@@ -56,25 +57,29 @@
});
}
document.querySelectorAll('section local-time:first-of-type').forEach(fixDate);
+ */
// Similar to fixDate, but tailored for relative-time rather than local-time
// FIXME: the format for relative times should probably be different. Check and confirm.
async function fixTime(ele){
- ele.textContent = await new Date(ele.getAttribute('datetime')).toLocaleString(navigator.language, {
- month: ele.getAttribute('month') || undefined,
- day: ele.getAttribute('day') || undefined,
- year: ele.getAttribute('year') || undefined
- });
+ // ele.textContent = await new Date(ele.getAttribute('datetime')).toLocaleString(navigator.language, {
+ // month: ele.getAttribute('month') || undefined,
+ // day: ele.getAttribute('day') || undefined,
+ // year: ele.getAttribute('year') || undefined
+ // });
+ //// Screw it, just use absolute time for the locale.
+ ele.textContent = await localizeTimestamp(new Date(ele.getAttribute('datetime')));
}
+ document.querySelectorAll('section relative-time:first-of-type').forEach(fixTime);
- document.querySelectorAll('[data-view-component] include-fragment[loading="lazy"][src]:not([src=""])').forEach(async function(lazyPageFrag){
+ document.querySelectorAll('[data-view-component] include-fragment[loading="lazy"][src]:not([src=""]):not([data-target="select-panel.includeFragment"])').forEach(async function(lazyPageFrag){
try{
await fetch(lazyPageFrag.getAttribute('src'), requestTemplate).then(
resp => resp.text().then(
async function(html){
await lazyPageFrag.replaceChildren(...parser.parseFromString(html, 'text/html').querySelectorAll('body > [data-view-component]'));
- await lazyPageFrag.querySelectorAll('local-time').forEach(await fixDate);
- await lazyPageFrag.querySelectorAll('relative-time').forEach(await fixTime); // FIXME: the format for relative times should probably be different.
+ // await lazyPageFrag.querySelectorAll('local-time').forEach(await fixDate);
+ await lazyPageFrag.querySelectorAll('relative-time').forEach(await fixTime);
},
err => console.warn('There was a problem retrieving the response text', err)
),
@@ -91,8 +96,8 @@
resp => resp.text().then(
async function(html){
await deferredPageFrag.replaceChildren(...parser.parseFromString(html, 'text/html').querySelectorAll('body > [data-view-component]'));
- await deferredPageFrag.querySelectorAll('local-time').forEach(await fixDate);
- await deferredPageFrag.querySelectorAll('relative-time').forEach(await fixTime); // FIXME: the format for relative times should probably be different.
+ // await deferredPageFrag.querySelectorAll('local-time').forEach(await fixDate);
+ await deferredPageFrag.querySelectorAll('relative-time').forEach(await fixTime);
},
err => console.warn('There was a problem retrieving the response text', err)
),
diff --git a/legacy_browser_workarounds/github_show_issue_comments_workaround.user.js b/legacy_browser_workarounds/github_show_issue_comments_workaround.user.js
index adcbfee..535fc41 100644
--- a/legacy_browser_workarounds/github_show_issue_comments_workaround.user.js
+++ b/legacy_browser_workarounds/github_show_issue_comments_workaround.user.js
@@ -2,7 +2,7 @@
// @name GitHub Issue Comments Legacy Workaround
// @namespace https://github.com/StaticPH
// @match https://github.com/*/*/issues/*
-// @version 1.0.0
+// @version 1.1.1
// @createdAt 11/10/2025, 6:17:25 PM
// @author StaticPH
// @description Manually display comments on Github issues using the JSON that's already on the page, which totally doesn't need React to accomplish.
@@ -20,7 +20,9 @@
(function(){
"use strict";
- const reactionToolbarHTML = ``
+ const reactionIconHTML = '';
+ const reactionToolbarHTML = ``;
+
const fixedStyles = `
.dShPvE {
display: flex;
@@ -53,8 +55,19 @@
// Each *COMMENT* item in responseNodesData has the following properties (non-exhaustive): {author:{avatarUrl, id, login, name, profileUrl}, bodyHTML, bodyVersion, createdAt, databaseId, id, lastEditedAt, lastUserContentEdit:{editor:{id, login, url, __typename}, id}, reactionGroups, url}
const responseNodesData = responses.map(e => e.node);
+ const friendlyTimeFormatter = new Intl.DateTimeFormat(navigator.language, { numberingSystem: 'ltn', calendar: 'gregory', year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', hour12: true, minute: 'numeric', second: 'numeric' });
+
+ function buildTimeElement(timestamp){
+ return `${
+ // Until I come up with a way to make relative-time-element load
+ // (instead of being skipped due to earlier parsing errors),
+ // just use the absolute time in a friendly format
+ friendlyTimeFormatter.format(new Date(timestamp))
+ } `;
+ }
+
function buildCommentNode(commentNode){
- return `\t
+ return `\t
@@ -63,14 +76,14 @@
- ${commentNode.author.login} commented
+ ${commentNode.author.login} commented ${buildTimeElement(commentNode.createdAt)}
@@ -99,10 +112,9 @@
}
function fixIssueTimeline(){
- const commentContainer = document.querySelector('div.react-comments-container > div.IssueViewer-module__commentsContainer--H8wxg');
+ const commentContainer = document.querySelector('div.react-comments-container > div[class*="IssueViewer-module__commentsContainer"]');
const frag = document.createDocumentFragment();
- const substContainer = document.createElement('div');
- substContainer.className = 'IssueViewer-module__commentsContainer--H8wxg';
+ const substContainer = commentContainer.cloneNode();
// fix borked styles
substContainer.insertAdjacentHTML('beforeEnd', ``);
@@ -112,5 +124,69 @@
frag.append(substContainer);
commentContainer.replaceWith(frag);
}
+
+ function redoIssueLabelTooltips(){
+ // Add tooltips for long descriptions to Issue labels.
+ /*
+ document.querySelectorAll('[aria-describedby*="-tooltip"]').forEach(function(ele){
+ const tooltipIDSelector = ele.getAttribute('aria-describedby').split(' ').map(s => `[id="${s}"]`).join(',');
+ // Even if there may be multiple IDs, as the spec permits, take whatever is found first.
+ const descriptor = document.querySelector(tooltipIDSelector);
+
+ if(!descriptor){
+ console.warn(`ERROR: unable to find an element matching the selector "${tooltipIDSelector}"`);
+ return;
+ }
+ ele.title = descriptor.textContent.trim();
+ descriptor.remove();
+ ele.removeAttribute('aria-describedby');
+ });
+ */
+
+ // Has a similar overall effect, but less explicit DOM manipulation, I guess?
+ document.head.insertAdjacentHTML('beforeEnd', `
+ `);
+ // The attribute `role="tooltip"` isn't already added by GitHub, even though aria-describedby is.
+ // Not sure what was expected to happen like that.
+ document.querySelectorAll('.sr-only[id*="-tooltip"]').forEach(e => e.setAttribute('role', 'tooltip'));
+ }
+
fixIssueTimeline();
+ redoIssueLabelTooltips();
+
+ /*
+ document.querySelectorAll('[aria-label="Reactions"]').forEach(function(ele){
+ const btn = ele.querySelector('button[aria-labelledby=":rr:"]');
+ if (!btn || btn.title){ return; } // No need to substitute a tooltip
+ if (btn.ariaDescribedBy === ':rq:-loading-announcement){
+ btn.removeAttribute('aria-describedby');
+ }
+ const oldLabelEle = ele.querySelector('[id=":rr:"]');
+ if (!oldLabelEle){ return; } // No need to substitute a tooltip
+ btn.title = oldLabelEle.textContent;
+ oldLabelEle.remove(); // Will likely break the reaction menu popover, if I ever got around to fixing it in a way that resembles the modern standard behavior.
+ });
+ */ // Hardcoded simplification, since the dummy reactions buttons are just constant raw HTML being applied by this script anyways.
})();
diff --git a/legacy_browser_workarounds/stackexchange_legacy_comments_expander.user.js b/legacy_browser_workarounds/stackexchange_legacy_comments_expander.user.js
index e86bdbf..5411852 100644
--- a/legacy_browser_workarounds/stackexchange_legacy_comments_expander.user.js
+++ b/legacy_browser_workarounds/stackexchange_legacy_comments_expander.user.js
@@ -8,7 +8,7 @@
// @match https://stackexchange.com/*
// @match https://*.stackoverflow.com/*
// @match https://*.stackexchange.com/*
-// @version 1.1
+// @version 1.2.0
// @createdAt 11/6/2022, 1:17:14 AM
// @author StaticPH
// @description Replace 'Show X more comments' handler for StackExchange sites to better support older browsers; in particular, this enables showing all comments when using Chromium 72.
@@ -20,7 +20,7 @@
// @icon https://cdn.sstatic.net/Sites/stackoverflow/Img/favicon.ico
// @grant none
// @noframes
-// @run-at document-load
+// @run-at document-idle
// ==/UserScript==
(function(){
@@ -28,7 +28,7 @@
function replaceChildrenWithNodes(parentNode, ...newChildren){
while (parentNode.lastChild){
- parentNode.removeChild(parentNode.lastChild);
+ parentNode.lastChild.remove();
}
if (newChildren !== undefined){
const replacements = (newChildren.length === 1 && Array.isArray(newChildren[0])) ? newChildren[0] : newChildren;
@@ -44,6 +44,87 @@
return replaceChildrenWithNodes(parentEle, ...responseHtml.querySelectorAll('li'));
}
+ function replaceCommentsListFromFetched(insertInto, data){
+ const parser = new DOMParser();
+ const responseHtml = parser.parseFromString(`${data}
`, 'text/html');
+ return replaceChildrenWithNodes(insertInto, responseHtml.body.firstElementChild);
+ }
+
+ const replySVG = '';
+ const commentUpvoteSVG = '';
+ const ellipsesSVG = '';
+
+ async function buildReplyTemplate(reply, postID){
+ return await `
+
+
+
+
+
+
+
+
+
+
+
+ ${reply.user.displayName}
+
+
+
+
+
+
+
+
+
+
+
+ ${reply.htmlBody}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `;
+ }
+
function onExpandComments(evnt){
if (evnt.target.matches('.answer a.js-show-link, .question a.js-show-link')){
evnt.stopImmediatePropagation();
@@ -70,7 +151,38 @@
*/
.then(data => replaceChildrenWithFetched(insertInto, data) && evnt.target.parentElement.remove(), insertInto.setAttribute('data-remaining-comments-count', '0'));
}
+ else if (evnt.target.matches('.answer button.comments-link:not([data-so-test*="parent-answer"])')){
+ evnt.stopImmediatePropagation();
+ evnt.preventDefault();
+ const answer = evnt.target.closest('.answer');
+ const postID = answer.getAttribute('data-answerid') || answer.getAttribute('data-questionid');
+ const followupScriptEle = answer.querySelector('script[type="application/json"]');
+ const followupData = JSON.parse(followupScriptEle.textContent);
+ const insertInto = answer.querySelector(`#${followupData.containerElementId} [role="list"]`);
+ // for (reply of followupData.replies){
+ // insertInto.insertAdjacentHTML('beforeEnd', buildReplyTemplate(reply, postID)); // Doesn't fix the order to be chronological, and that seems like a hassle.
+ //}
+
+ fetch(`${document.location.origin}/posts/${postID}/comments`, {
+ 'headers': {
+ 'accept': 'text/html, */*; q=0.01',
+ },
+ 'referrerPolicy': 'strict-origin-when-cross-origin',
+ 'body': null,
+ 'method': 'GET',
+ 'mode': 'cors',
+ 'credentials': 'include'
+ }).then(resp => resp.text())
+ .then(data => requestAnimationFrame(function(){
+ replaceCommentsListFromFetched(insertInto, data) && evnt.target.parentElement.remove();
+ insertInto.setAttribute('data-remaining-comments-count', '0');
+ }));
+ }
}
+
+ // Relevant if using buildReplyTemplate
+ // document.head.insertAdjacentHTML('beforeEnd', '');
+
document.addEventListener('click', onExpandComments);
})();
diff --git a/mitigate_target_blank_risk.user.js b/mitigate_target_blank_risk.user.js
index 2923f66..be0b920 100644
--- a/mitigate_target_blank_risk.user.js
+++ b/mitigate_target_blank_risk.user.js
@@ -47,7 +47,7 @@
let splitRel;
function cleanse(){
// Unsure whether to prefer getElementByTagName('a')+if target=='_blank' OR querySeletorAll('a[target="_blank"]')
- Array.from(document.getElementsByTagName('a')).forEach( (link) => {
+ Array.from(document.querySelectorAll('a')).forEach( (link) => {
if (link.target == '_blank'){
if (allowedOrigins.includes(link.origin)){
console.log('Found link with allowed origin:"' + link.origin + '" and target="_blank". The link\'s "rel" attribute will not be modified.');
diff --git a/msys2_package_description_in_title.user.js b/msys2_package_description_in_title.user.js
index 2072e8e..28db64e 100644
--- a/msys2_package_description_in_title.user.js
+++ b/msys2_package_description_in_title.user.js
@@ -1,12 +1,13 @@
// ==UserScript==
// @name MSYS Packages Description In Title
// @namespace https://github.com/StaticPH
+// @include http*://packages.msys2.org/base/*
// @include http*://packages.msys2.org/package/*
// @include http*://packages.msys2.org/packages/*
-// @version 1.2.0
+// @version 1.2.1
// @createdAt 4/28/2021
// @author StaticPH
-// @description Include the package description on the tab title for a package's page on packages.msys2.org/packages.
+// @description Include the package description on the tab title for a package's page on packages.msys2.org.
// @license MIT
// @updateURL https://raw.githubusercontent.com/StaticPH/Userscripts/master/msys2_package_description_in_title.user.js
// @downloadURL https://raw.githubusercontent.com/StaticPH/Userscripts/master/msys2_package_description_in_title.user.js
@@ -22,12 +23,16 @@
setTimeout(function wait(){
const pkgName = document.querySelector('h4.card-title');
- const pkgDesc = document.querySelector('h6.card-subtitle, .card-body > dl > dd:nth-child(6)');
-
- //TODO: Modify script to also indicate which pkg subpage is currently loaded (if it isnt the readme subpage)
+ let pkgDesc = null;
+ if (document.location.pathname.startsWith('/base/')){
+ pkgDesc = document.querySelector('.card-body > dl > dd:nth-child(2)');
+ }
+ else{
+ pkgDesc = document.querySelector('h6.card-subtitle, .card-body > dl > dd:nth-child(6)');
+ }
if (pkgName && pkgDesc){
- document.title=`${pkgName.textContent.trim()} — ${pkgDesc.textContent.trim()}`;
+ document.title = `${pkgName.textContent.trim()} — ${pkgDesc.textContent.trim()}`;
}
else{
setTimeout(wait, 100); // Continue trying every 100ms until success
diff --git a/novelupdates_reading_list_upgrades.user.js b/novelupdates_reading_list_upgrades.user.js
index 0841a1a..8332f24 100644
--- a/novelupdates_reading_list_upgrades.user.js
+++ b/novelupdates_reading_list_upgrades.user.js
@@ -120,6 +120,7 @@
// document.querySelectorAll('.bmhide > :not(.nu_editnotes):first-child') // get not caught up
// document.querySelectorAll('.bmhide > span[class*="bm_hide_me"]:first-child') // alternative to get not caught up
const caughtUpHelper = {
+ /* oxlint-disable unicorn/prefer-query-selector */
mainTbl: document.getElementById('myTable read'),
hiddenTbl: (function(){
const tbl = document.createElement('table');
@@ -216,7 +217,9 @@
// Fuck tracking and analytics
document.querySelectorAll('script').forEach(s => (s.textContent.includes('urchinTracker') || s.src.includes('analytic')) && s.remove());
- settings.improvePageTitle && improveTitle();
+ if (settings.improvePageTitle){
+ improveTitle();
+ }
caughtUpHelper.init();
})();
\ No newline at end of file
diff --git a/scribblehub_reading_list_upgrades.user.js b/scribblehub_reading_list_upgrades.user.js
index 40166d4..dcea3f4 100644
--- a/scribblehub_reading_list_upgrades.user.js
+++ b/scribblehub_reading_list_upgrades.user.js
@@ -132,7 +132,7 @@
}
const caughtUpHelper = {
- mainTbl: document.getElementsByClassName('rl_table')[0],
+ mainTbl: document.querySelector('.rl_table'),
hiddenTbl: (function(){
const tbl = document.createElement('table');
tbl.id = 'hiddenTbl';
@@ -225,7 +225,9 @@
// Fuck tracking and analytics
document.querySelectorAll('script').forEach(s => (s.textContent.includes('urchinTracker') || s.src.includes('analytic')) && s.remove());
- settings.improvePageTitle && improveTitle();
+ if (settings.improvePageTitle){
+ improveTitle();
+ }
if (settings.ctrlEnterSavesNotes){
document.addEventListener('keyup', saveNotesKeybindHandler);
setTitleForFakeButton(document.querySelector('.rlnotes_btn.savenotes'), true, '', ' (Ctrl+Enter)');
diff --git a/twitch_transparent_video_stats.user.js b/twitch_transparent_video_stats.user.js
index 6090678..1ad2e4f 100644
--- a/twitch_transparent_video_stats.user.js
+++ b/twitch_transparent_video_stats.user.js
@@ -13,7 +13,7 @@
// @exclude-match https://www.twitch.tv/settings*
// @exclude-match https://www.twitch.tv/turbo*
// @exclude-match https://www.twitch.tv/annual-recap
-// @version 1.1.1
+// @version 1.1.2
// @createdAt 5/19/2021
// @author StaticPH
// @description Makes the video stats overlay 50% transparent
@@ -25,7 +25,7 @@
// @icon https://brand.twitch.tv/assets/logos/svg/glitch/purple.svg
// @grant GM.addStyle
// @grant GM_addStyle
-// @run-at document-start
+// @run-at document-idle
// ==/UserScript==
(function(){
@@ -46,7 +46,8 @@
}
GM.addStyle(`
- div.video-player__overlay div.simplebar-scroll-content > div.simplebar-content > div {
+ div.video-player__overlay div.simplebar-scroll-content > div.simplebar-content > div,
+ [data-a-target="player-overlay-video-stats"] {
opacity: 0.5;
}
`);
diff --git a/ubuntu_packages_description_in_title.user.js b/ubuntu_packages_description_in_title.user.js
index 5e1128b..eec846a 100644
--- a/ubuntu_packages_description_in_title.user.js
+++ b/ubuntu_packages_description_in_title.user.js
@@ -20,6 +20,7 @@
(function(){
'use strict';
+ /* oxlint-disable unicorn/no-array-reverse */
const [pkg, repo, sourceSegment, /*lang*/] = document.location.pathname.split('/').reverse();
setTimeout(function wait(){