Translate GitHub sign-in and hide-clutter messages, refine Polish wording, and localize the author credit. Preserve message keys and dynamic substitutions.
Add Belarusian locale resources and language navigation. Preserve analytics substitutions, refine translation accuracy, and cover translated counts and durations with regression tests.
List Android - Morphe with the existing third-party integrations and disclaimer. Add the label to website locales, preserve translator credits, and make the contact email clickable.
Use rendered widths when inline reaction wrappers report non-numeric computed widths or zero client widths. Preserve existing block-control measurements in the extension and userscript, with generated-artifact browser regression coverage.
Add Firefox data consent, repair contributor sign-in, and introduce optional Hide clutter buttons. Harden video navigation, voting, and action layouts with shared browser regression coverage and reproducible release packaging.
Harden Shorts controls, reaction state, and navigation; share voting logic
with the extension; improve ratio-bar rendering; and add comprehensive Jest,
Playwright, artifact, and live-browser regression coverage.
getLikeButton() / getDislikeButton() now cache a single getButtons() result per call instead of invoking it multiple times within the same expression, avoiding inconsistent reads if the DOM mutates between calls, and return null early if getButtons() itself returns nothing. Audited all direct callers of these three functions and added ?. / early-return guards where a null return wasn't already handled: isVideoLiked, isVideoNotLiked, getLikeTextContainer, setLikes on the like side; mobile isVideoDisliked, and getDislikeTextContainer's fallback branch on the dislike side (both previously used ?. on the outer button but still dereferenced a possibly-null nested querySelector("button") result unguarded). Fixed getDislikeTextContainer short-circuiting to undefined instead of null when dislikeButton is missing, which meant its result === null fallback check never triggered and it could try appending to a null button. Fixed setDislikes checking _container?.innerText !== dislikesCount (safe) but then assigning _container.innerText = ... unguarded in the branch body, which still threw when _container was null.
No behavior change when the expected elements are present — this only prevents uncaught exceptions when they're not, letting the existing polling loop (checkForJS_Finish, 111ms interval) retry on the next tick instead of the script erroring out.
Here's a description you could use for a commit message, PR, or changelog entry:
Harden button-lookup functions against missing/changed DOM elements
getButtons(), getLikeButton(), and getDislikeButton() previously assumed YouTube's like/dislike DOM structure was always present and would throw a TypeError (e.g. "Cannot read properties of null/undefined") whenever the expected elements weren't found — for example during early page load, mid-navigation, or after a YouTube layout change breaks a selector.
Changes:
getLikeButton() / getDislikeButton() now cache a single getButtons() result per call instead of invoking it multiple times within the same expression, avoiding inconsistent reads if the DOM mutates between calls, and return null early if getButtons() itself returns nothing.
Audited all direct callers of these three functions and added ?. / early-return guards where a null return wasn't already handled: isVideoLiked, isVideoNotLiked, getLikeTextContainer, setLikes on the like side; mobile isVideoDisliked, and getDislikeTextContainer's fallback branch on the dislike side (both previously used ?. on the outer button but still dereferenced a possibly-null nested querySelector("button") result unguarded).
Fixed getDislikeTextContainer short-circuiting to undefined instead of null when dislikeButton is missing, which meant its result === null fallback check never triggered and it could try appending to a null button.
Fixed setDislikes checking _container?.innerText !== dislikesCount (safe) but then assigning _container.innerText = ... unguarded in the branch body, which still threw when _container was null.
No behavior change when the expected elements are present — this only prevents uncaught exceptions when they're not, letting the existing polling loop (checkForJS_Finish, 111ms interval) retry on the next tick instead of the script erroring out.
* Deferr showing changelog untill browser restart.
Make changelog easier to identify with the extension (better title, logo)
* Deferr showing changelog untill browser restart.
Make changelog easier to identify with the extension (better title, logo)
* auth works
* Works after review.
* switched to auth flow
* all works well in chrome
* all works well in chrome and firefox
* translations harmonized
* translations harmonized
* website translations
* Translations harmonized.
* Everything kinda works
* Almost works
* all works
* all works
* all works #2
* Streamline premium analytics range handling
* Restore premium analytics selection flow
* Fix analytics zoom interactions
* Add per-section expansion controls to premium analytics
* Adjust map legend visibility for premium analytics
* Add missing country synonyms for premium map
* Remove legacy premium badge overlay
* Polish premium analytics controls for light theme
* Refresh map legend when toggling sections
* Improve light-mode styling for range selectors
* Fix map mode switching regression
* Fix for panel expand
* Typo fix
* webpack fix
* before next steps.
* split, zero-buckets
* bucket size indicated on UI
* all works
* first/last toggle
* breadcrumbs
* release
* Add localization to the tooltip-porcentage-mode fields, as well the Spanish translation
* Fix Spanish translation for showTooltipPercentage
* Fix plural Spanish translation