From ecab1e69c6a0bd9e3144ea69d59413b9210b7dff Mon Sep 17 00:00:00 2001 From: Lawrence Hook Date: Tue, 21 Oct 2025 22:49:04 -0400 Subject: [PATCH 01/17] update version --- src/chrome_manifest.json | 2 +- src/firefox_manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/chrome_manifest.json b/src/chrome_manifest.json index 070f049..b333955 100644 --- a/src/chrome_manifest.json +++ b/src/chrome_manifest.json @@ -3,7 +3,7 @@ "description": "Spend less time on YouTube. Customize YouTube's user interface to be less engaging.", "homepage_url": "https://github.com/lawrencehook/remove-youtube-suggestions", "manifest_version": 3, - "version": "4.3.64", + "version": "4.3.65", "icons": { "16": "/images/16.png", diff --git a/src/firefox_manifest.json b/src/firefox_manifest.json index c8c99bb..71f757a 100644 --- a/src/firefox_manifest.json +++ b/src/firefox_manifest.json @@ -3,7 +3,7 @@ "description": "Spend less time on YouTube. Customize YouTube's user interface to be less engaging.", "homepage_url": "https://github.com/lawrencehook/remove-youtube-suggestions", "manifest_version": 2, - "version": "4.3.64", + "version": "4.3.65", "icons": { "16": "/images/16.png", From b46281aeefd2b9663af52baab63e39e05c49e597 Mon Sep 17 00:00:00 2001 From: Jagan Date: Wed, 24 Dec 2025 02:07:39 +0530 Subject: [PATCH 02/17] fix(sidebar): correct mapping of sidebar section (#186) Youtube changed the structure of the sidebar, this caused several toggle target issues in the extension. -> Hiding you/library removed the section contents but left a small empty container. -> Hiding other sections hid the wrong ones. Explore hid you/library completely, more from youtube hid explore, settings hid more from youtube. And couldn't hide settings at all. This commit updates the CSS to match the new DOM structure. -> Replaced nth-child with nth-of type -> Realigned indexes to match new structure. - `remove_sub_section` targets the 2nd section (Subscriptions) - `remove_quick_links_section` targets the 3rd section (You / Library) - `remove_explore_section` targets the 4th section (Explore) - `remove_more_section` targets the 5th section (More from YouTube) - `remove_settings_section` targets the 6th section (Settings) - `remove_footer_section` still hides `#guide-renderer > #footer` -> Modified the order of the buttons in src/content-script/main.css to match the order in the sidebar. Resolves: #185 --- src/content-script/main.css | 12 ++++++------ src/shared/main.js | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/content-script/main.css b/src/content-script/main.css index 8083d51..4757ab6 100644 --- a/src/content-script/main.css +++ b/src/content-script/main.css @@ -43,12 +43,12 @@ html[global_enable="true"][remove_shorts_link="true"] ytd-mini-guide-entry-rende html[global_enable="true"][remove_subscriptions_link="true"] a[href="/feed/subscriptions"], /* Left Bar Navigation Sections */ -html[global_enable="true"][remove_quick_links_section="true"] ytd-guide-collapsible-section-entry-renderer, -html[global_enable="true"][remove_sub_section="true"] ytd-guide-section-renderer.style-scope:nth-child(2), -html[global_enable="true"][remove_explore_section="true"] ytd-guide-section-renderer.style-scope:nth-child(3), -html[global_enable="true"][remove_more_section="true"] ytd-guide-section-renderer.style-scope:nth-child(4), -html[global_enable="true"][remove_settings_section="true"] ytd-guide-section-renderer.style-scope:nth-child(5), -html[global_enable="true"][remove_footer_section="true"] #guide-renderer > div#footer, +html[global_enable="true"][remove_sub_section="true"] ytd-guide-section-renderer.style-scope:nth-of-type(2), +html[global_enable="true"][remove_quick_links_section="true"] ytd-guide-section-renderer.style-scope:nth-of-type(3), +html[global_enable="true"][remove_explore_section="true"] ytd-guide-section-renderer.style-scope:nth-of-type(4), +html[global_enable="true"][remove_more_section="true"] ytd-guide-section-renderer.style-scope:nth-of-type(5), +html[global_enable="true"][remove_settings_section="true"] ytd-guide-section-renderer.style-scope:nth-of-type(6), +html[global_enable="true"][remove_footer_section="true"] #guide-renderer > div#footer, /* Homepage */ diff --git a/src/shared/main.js b/src/shared/main.js index e6a04f4..74255f2 100644 --- a/src/shared/main.js +++ b/src/shared/main.js @@ -174,18 +174,18 @@ const SECTIONS = [ name: "Left Navigation Bar Sections", tags: "Left Navbar", options: [ - { - name: "Hide section - You/Library", - id: "remove_quick_links_section", - defaultValue: false, - effects: { true: { only_show_playlists: false }} - }, { name: "Hide section - Subscriptions", id: "remove_sub_section", defaultValue: false, effects: { false: { only_show_playlists: false }} }, + { + name: "Hide section - You/Library", + id: "remove_quick_links_section", + defaultValue: false, + effects: { true: { only_show_playlists: false }} + }, { name: "Hide section - Explore", id: "remove_explore_section", From 89d7b0962941a1a0defe994433bf58ea5f03b69c Mon Sep 17 00:00:00 2001 From: Lawrence Hook Date: Tue, 23 Dec 2025 15:41:38 -0500 Subject: [PATCH 03/17] fix whitespace --- src/content-script/main.css | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/content-script/main.css b/src/content-script/main.css index 4757ab6..0d1b1ae 100644 --- a/src/content-script/main.css +++ b/src/content-script/main.css @@ -43,12 +43,12 @@ html[global_enable="true"][remove_shorts_link="true"] ytd-mini-guide-entry-rende html[global_enable="true"][remove_subscriptions_link="true"] a[href="/feed/subscriptions"], /* Left Bar Navigation Sections */ -html[global_enable="true"][remove_sub_section="true"] ytd-guide-section-renderer.style-scope:nth-of-type(2), -html[global_enable="true"][remove_quick_links_section="true"] ytd-guide-section-renderer.style-scope:nth-of-type(3), -html[global_enable="true"][remove_explore_section="true"] ytd-guide-section-renderer.style-scope:nth-of-type(4), -html[global_enable="true"][remove_more_section="true"] ytd-guide-section-renderer.style-scope:nth-of-type(5), -html[global_enable="true"][remove_settings_section="true"] ytd-guide-section-renderer.style-scope:nth-of-type(6), -html[global_enable="true"][remove_footer_section="true"] #guide-renderer > div#footer, +html[global_enable="true"][remove_sub_section="true"] ytd-guide-section-renderer.style-scope:nth-of-type(2), +html[global_enable="true"][remove_quick_links_section="true"] ytd-guide-section-renderer.style-scope:nth-of-type(3), +html[global_enable="true"][remove_explore_section="true"] ytd-guide-section-renderer.style-scope:nth-of-type(4), +html[global_enable="true"][remove_more_section="true"] ytd-guide-section-renderer.style-scope:nth-of-type(5), +html[global_enable="true"][remove_settings_section="true"] ytd-guide-section-renderer.style-scope:nth-of-type(6), +html[global_enable="true"][remove_footer_section="true"] #guide-renderer > div#footer, /* Homepage */ From e6418d6aaa996e255182954daa79645b42b0a9a3 Mon Sep 17 00:00:00 2001 From: Lawrence Hook Date: Tue, 23 Dec 2025 15:48:47 -0500 Subject: [PATCH 04/17] update versions --- src/chrome_manifest.json | 2 +- src/firefox_manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/chrome_manifest.json b/src/chrome_manifest.json index b333955..fe1498f 100644 --- a/src/chrome_manifest.json +++ b/src/chrome_manifest.json @@ -3,7 +3,7 @@ "description": "Spend less time on YouTube. Customize YouTube's user interface to be less engaging.", "homepage_url": "https://github.com/lawrencehook/remove-youtube-suggestions", "manifest_version": 3, - "version": "4.3.65", + "version": "4.3.66", "icons": { "16": "/images/16.png", diff --git a/src/firefox_manifest.json b/src/firefox_manifest.json index 71f757a..b1b1e35 100644 --- a/src/firefox_manifest.json +++ b/src/firefox_manifest.json @@ -3,7 +3,7 @@ "description": "Spend less time on YouTube. Customize YouTube's user interface to be less engaging.", "homepage_url": "https://github.com/lawrencehook/remove-youtube-suggestions", "manifest_version": 2, - "version": "4.3.65", + "version": "4.3.66", "icons": { "16": "/images/16.png", From 8ca3247d68c0e77b7fa93e6b72b67c394987eb62 Mon Sep 17 00:00:00 2001 From: Lawrence Hook Date: Tue, 30 Dec 2025 17:41:59 -0500 Subject: [PATCH 05/17] claude recommends some TODOs --- TODO.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 TODO.md diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..31f62d6 --- /dev/null +++ b/TODO.md @@ -0,0 +1,41 @@ +# TODO + +## Quick Wins + +| Issue | Effort | +|-------|--------| +| Remove debug `console.log` in `options/main.js:174-175` | 1 min | +| Sync manifest versions (4.3.65 vs 4.3.66) | 1 min | +| Delete unused `TEMPLATE_FIELDSET` in `options/main.js:8` | 1 min | +| Delete dead `setCookie`/`getCookie` in `content-script/main.js:657-678` | 1 min | +| Fix `SEC_IN_WEEK` naming in `utils.js:69` (it's actually minutes) | 1 min | +| Move duplicated regexes to `shared/utils.js` | 5 min | +| Delete commented-out code blocks in `content-script/main.js` | 5 min | +| Add Page Visibility API to pause polling when tab is hidden | 10 min | + +## Code Quality + +- [ ] Auto-generate `idToShortId` map from `SECTIONS` instead of manual maintenance +- [ ] Add storage validation/migration for schema changes between versions +- [ ] Improve error handling beyond `console.log(error)` +- [ ] Clean up event listeners in `options/main.js` to prevent memory leaks + +## i18n / Localization + +Hardcoded English strings that break for non-English YouTube: +- `content-script/main.js:171` — `'for you'` comparison +- `content-script/main.js:211` — `'Streamed'` check +- `content-script/main.js:467-468` — `'All'`, `'Related'` chip names + +## Incomplete Features + +- `utils.js:14` — TODO: validate start time < end time for schedules +- `utils.js:32` — TODO: force endTime to come after startTime + +## Fragility + +YouTube frequently changes their DOM structure. Monitor these selectors: +- `ytd-rich-grid-row` +- `#metadata-line span` +- `ytd-thumbnail-overlay-time-status-renderer` +- `yt-chip-cloud-chip-renderer` From 3b5f50fa8224028f3975fba75d3488b107730c1e Mon Sep 17 00:00:00 2001 From: Lawrence Hook <6572030+lawrencehook@users.noreply.github.com> Date: Sat, 24 Jan 2026 20:37:53 -0500 Subject: [PATCH 06/17] feat(extension): revamp donate/feedback pages and polish options UI (#189) - Redesign donors page with modern card-based tier system - Redesign feedback page with contact grid and cleaner layout - Both pages now fit within 600px without scrolling - Apply subtle UI improvements to main options page: - System fonts, softer borders, hover states - Improved sidebar, menus, and footer styling - Denser option spacing for info-density Co-authored-by: Claude Opus 4.5 --- src/options/body.css | 21 +- src/options/donors/donors.css | 453 ++++++++++++++++++----------- src/options/donors/donors.html | 128 ++++---- src/options/donors/donors.js | 4 +- src/options/feedback/feedback.css | 343 ++++++++++++++++++---- src/options/feedback/feedback.html | 132 +++++++-- src/options/footer.css | 18 +- src/options/general.css | 6 +- src/options/header.css | 68 +++-- src/options/options.css | 30 +- 10 files changed, 838 insertions(+), 365 deletions(-) diff --git a/src/options/body.css b/src/options/body.css index b19d143..447a2d1 100644 --- a/src/options/body.css +++ b/src/options/body.css @@ -439,29 +439,30 @@ html[foo=bar] { #sidebar { height: calc(6px + var(--body-height)); - width: 150px; - padding: 10px 3px; + width: 140px; + padding: 8px 6px; overflow: hidden; box-sizing: border-box; user-select: none; - border-radius: 1px; - border-right: 1.5px solid var(--box-shadow-color); -/* box-shadow: 0px 0px 1px 0.5px var(--box-shadow-color);*/ + border-right: 1px solid var(--box-shadow-color); display: flex; flex-direction: column; - gap: 0px; + gap: 2px; } #sidebar .sidebar_section { - padding: 7px; - border-radius: var(--border-radius); + padding: 8px 10px; + border-radius: 6px; box-sizing: border-box; - font-size: 16px; + font-size: 14px; + font-weight: 500; + transition: background-color 0.15s ease; } #sidebar .sidebar_section:hover { + background-color: var(--box-shadow-color); } #sidebar .sidebar_section[selected] { background-color: var(--label-color); @@ -478,5 +479,5 @@ html[foo=bar] { display: flex; flex-direction: column; - gap: 3px; + gap: 1px; } diff --git a/src/options/donors/donors.css b/src/options/donors/donors.css index c253fca..56bcca2 100644 --- a/src/options/donors/donors.css +++ b/src/options/donors/donors.css @@ -1,210 +1,317 @@ :root { - --header-height: 120px; - --footer-height: 60px; -} - -html[dark_mode='false'], html:not([dark_mode]) { - --body-color: #161616; - --body-background-color: #FAFAFA; - --label-color: #101010; - --box-shadow-color: rgba(0, 0, 0, 0.30); + --accent: #E91E63; + --accent-light: #FCE4EC; } html[dark_mode='true'] { - --body-color: #DDDDDD; - --body-background-color: #393939; - --label-color: #E9E9E9; - --box-shadow-color: rgba(127, 127, 127, 0.90); + --body-color: #E0E0E0; + --body-background-color: #1a1a2e; + --card-background: #16213e; + --card-border: #0f3460; + --label-color: #E9E9E9; + --box-shadow-color: rgba(0, 0, 0, 0.4); + --accent-light: rgba(233, 30, 99, 0.15); +} + +html[dark_mode='false'], html:not([dark_mode]) { + --body-color: #333; + --body-background-color: #f8f9fa; + --card-background: #fff; + --card-border: #e9ecef; + --label-color: #212529; + --box-shadow-color: rgba(0, 0, 0, 0.08); +} + +* { + box-sizing: border-box; +} + +html, body { + margin: 0; + padding: 0; + height: 600px; + overflow: hidden; } body { - padding: var(--header-height) 10px calc(30px + var(--footer-height)); - - display: flex; - justify-content: center; - align-items: center; -} - -html, html * { - font-family: serif; -} - -#back-button { - position: absolute; - top: 5px; - left: 5px; - - text-decoration: none; -} -#info { -/* position: relative;*/ -/* left: 30px;*/ -} - -header { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: var(--header-height); - background-color: var(--body-background-color); - border-bottom: 1px solid var(--label-color); - box-sizing: border-box; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif; + color: var(--body-color); + line-height: 1.4; +} +.page-container { + height: 600px; + max-width: 640px; + margin: 0 auto; + padding: 12px 16px; display: flex; flex-direction: column; - justify-content: center; +} + +/* Back Button */ +#back-button { + display: inline-flex; align-items: center; + gap: 4px; + padding: 5px 10px; + text-decoration: none; + color: var(--body-color); + background: var(--card-background); + border: 1px solid var(--card-border); + border-radius: 6px; + font-size: 12px; + font-weight: 500; + transition: all 0.2s ease; + width: fit-content; +} + +#back-button:hover { + background: var(--accent-light); + border-color: var(--accent); + color: var(--accent); +} + +#back-button svg { + width: 14px; + height: 14px; +} + +/* Header */ +header { + flex-shrink: 0; + margin-bottom: 16px; +} + +.header-content { + text-align: center; + margin-top: 12px; +} + +.header-icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 44px; + height: 44px; + background: var(--accent-light); + border-radius: 50%; + margin-bottom: 10px; +} + +.header-icon svg { + width: 22px; + height: 22px; + color: var(--accent); } header h1 { - margin-top: 10px; - margin-bottom: 10px; - font-size: 26px; + margin: 0 0 6px; + font-size: 22px; + font-weight: 700; + color: var(--label-color); } -header p { - margin-top: 2px; - margin-bottom: 2px; -} - - -main { - width: 500px; - margin-bottom: 20px; - - display: flex; - flex-direction: column; - gap: 30px; -} - -.donors-list { - padding-left: 20px; - padding-right: 20px; - - display: flex; - flex-direction: row; - flex-wrap: wrap; - column-gap: 25px; - row-gap: 10px; -} - -.donors-list p { +.subtitle { margin: 0; + font-size: 13px; + color: var(--body-color); + opacity: 0.85; } - -footer { - position: fixed; - bottom: 0; - left: 0; - width: 100%; - max-height: var(--footer-height); - - padding: 5px 20px; - - background-color: var(--body-background-color); - border-top: 1px solid var(--label-color); - box-sizing: border-box; - - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - gap: 20px; -} -footer p { - margin: 0; - font-size: 1rem; - text-align: center; +.subtitle.secondary { + margin-top: 4px; + font-size: 12px; + opacity: 0.7; } -#button-container { - width: 100%; - - display: flex; - flex-direction: row; - justify-content: space-around; - align-items: center; -} - -#info { - position: relative; -} -#tooltip { - visibility: hidden; - width: 300px; - - position: absolute; - bottom: 100%; - left: 0; - transform: translateX(-50%); - z-index: 10; - opacity: 0.98; - - padding: 10px; - - background-color: var(--body-color); - color: var(--body-background-color); - - font-weight: normal; - font-size: 1rem; -} -#info:hover #tooltip, #tooltip:hover { - visibility: visible; -} - -#donate_button { - padding: 3px 10px; - font-size: 1rem; - +.subtitle a { + color: var(--accent); text-decoration: none; - background-color: white; - color: black; - - border: 1px solid rgba(127, 127, 127, 0.90); - outline: none; + font-weight: 500; } + +.subtitle a:hover { + text-decoration: underline; +} + +/* Main content */ +main { + flex: 1; + display: flex; + flex-direction: column; + gap: 12px; + min-height: 0; +} + +/* Tier Cards */ +.tier { + flex: 1; + background: var(--card-background); + border: 1px solid var(--card-border); + border-radius: 12px; + padding: 14px 16px; + box-shadow: 0 1px 4px var(--box-shadow-color); + display: flex; + align-items: flex-start; + gap: 14px; +} + +.tier-header { + display: flex; + align-items: center; + gap: 10px; + flex-shrink: 0; +} + +.tier-badge { + display: flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + border-radius: 50%; + flex-shrink: 0; + background: var(--accent); + color: white; + font-size: 15px; + font-weight: 700; +} + +.tier-info h3 { + margin: 0; + font-size: 14px; + font-weight: 600; + color: var(--label-color); +} + +.tier-amount { + font-size: 12px; + color: var(--body-color); + opacity: 0.7; +} + +/* Donors List */ +.donors-list { + flex: 1; + display: flex; + flex-wrap: wrap; + gap: 8px; + align-items: center; + align-content: center; +} + +.donor-name { + display: inline-block; + padding: 5px 12px; + background: var(--body-background-color); + border-radius: 16px; + font-size: 13px; + color: var(--body-color); +} + +.donor-placeholder { + font-size: 13px; + color: var(--body-color); + opacity: 0.5; + font-style: italic; +} + +/* Footer */ +footer { + flex-shrink: 0; + background: var(--card-background); + border: 1px solid var(--card-border); + border-radius: 12px; + padding: 14px 18px; + margin-top: 16px; +} + +.footer-content { + display: flex; + justify-content: space-between; + align-items: center; + gap: 12px; +} + +.footer-text h3 { + margin: 0; + font-size: 15px; + font-weight: 600; + color: var(--label-color); +} + +/* Donate Button */ +#donate_button { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 10px 24px; + background: linear-gradient(135deg, var(--accent) 0%, #C2185B 100%); + color: white; + border: none; + border-radius: 24px; + font-size: 15px; + font-weight: 600; + cursor: pointer; + transition: all 0.2s ease; + box-shadow: 0 3px 10px rgba(233, 30, 99, 0.3); + flex-shrink: 0; +} + #donate_button:hover { - --accent-color: #F5D5D7; - - background-color: var(--accent-color); - border: 1px solid var(--accent-color); - box-shadow: 0px 0px 1px 1px var(--accent-color); + transform: translateY(-1px); + box-shadow: 0 5px 16px rgba(233, 30, 99, 0.4); } +#donate_button svg { + width: 18px; + height: 18px; +} +/* Donate Modal */ #donate-links-container { position: fixed; top: 0; left: 0; - width: 100vw; - height: 100vh; - z-index: 20; - - background-color: rgba(0, 0, 0, 0.5); -} -#donate-links { - position: absolute; - padding: 0; - z-index: 25; - - background-color: var(--body-background-color); - border: 1px solid var(--body-color); - + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + backdrop-filter: blur(4px); + z-index: 1000; display: flex; - flex-direction: column; + align-items: center; justify-content: center; - align-items: stretch; } -#donate-links > * { - padding: 10px 20px; + +#donate-links { + background: var(--card-background); + border-radius: 12px; + padding: 6px; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); + min-width: 220px; +} + +#donate-links a { + display: flex; + align-items: center; + gap: 10px; + padding: 12px 16px; + color: var(--body-color); text-decoration: none; - box-sizing: border-box; + border-radius: 8px; + font-size: 14px; + font-weight: 500; + transition: background 0.2s ease; +} + +#donate-links a:hover { + background: var(--accent-light); + color: var(--accent); +} + +#donate-links a svg { + width: 20px; + height: 20px; + flex-shrink: 0; } -#donate-links > *:hover { - --accent-color: #F5D5D7; - background-color: var(--accent-color); - color: #303030; -} \ No newline at end of file diff --git a/src/options/donors/donors.html b/src/options/donors/donors.html index 3325f5e..bd130c1 100644 --- a/src/options/donors/donors.html +++ b/src/options/donors/donors.html @@ -23,63 +23,89 @@ +
+
+ + + Back + -
- Back - -

Donors

-

RYS is made possible by donations from people like you.

-

You can also show your support by sharing it with a friend or leaving a review :)

- -
- -
- - - -
-

Tier 1 • $25

-
-

None yet, be the first

+
+
+ +
+

Supporters

+

RYS is made possible by generous donations from people like you.

+

You can also support by sharing with a friend or leaving a review

-
+
-
-

Tier 2 • $10

-
-

None yet, be the first

+
+ + +
+
+
1
+
+

Tier 1

+ $25+ +
+
+
+ Be the first +
+
+ +
+
+
2
+
+

Tier 2

+ $10+ +
+
+
+ Be the first +
+
+ +
+
+
3
+
+

Tier 3

+ $5+ +
+
+
+ Be the first +
+
+ +
+ +
+ -
- -
-

Tier 3 • $5

-
-

None yet, be the first

-
-
- - - - - - - - - - - + +
diff --git a/src/options/donors/donors.js b/src/options/donors/donors.js index 80831ff..c7c9a1b 100644 --- a/src/options/donors/donors.js +++ b/src/options/donors/donors.js @@ -74,8 +74,8 @@ sendGetDonorsRequest().then(x => { const appendDonorNode = name => { const donorNode = templateDonor.cloneNode(true); - const pNode = donorNode.querySelector('p'); - pNode.innerText = name; + const nameNode = donorNode.querySelector('.donor-name') || donorNode.querySelector('p'); + nameNode.innerText = name; donorList.appendChild(donorNode); } diff --git a/src/options/feedback/feedback.css b/src/options/feedback/feedback.css index fd713ca..e8d2ade 100644 --- a/src/options/feedback/feedback.css +++ b/src/options/feedback/feedback.css @@ -1,65 +1,304 @@ :root { - --header-height: 60px; -} - -html[dark_mode='false'], html:not([dark_mode]) { - --body-color: #161616; - --body-background-color: #FAFAFA; - --label-color: #101010; - --box-shadow-color: rgba(0, 0, 0, 0.30); + --accent: #2196F3; + --accent-light: #E3F2FD; + --github: #333; + --discord: #5865F2; + --form: #4CAF50; + --email: #FF5722; } html[dark_mode='true'] { - --body-color: #DDDDDD; - --body-background-color: #393939; - --label-color: #E9E9E9; - --box-shadow-color: rgba(127, 127, 127, 0.90); + --body-color: #E0E0E0; + --body-background-color: #1a1a2e; + --card-background: #16213e; + --card-border: #0f3460; + --label-color: #E9E9E9; + --box-shadow-color: rgba(0, 0, 0, 0.4); + --accent-light: rgba(33, 150, 243, 0.15); + --github: #E0E0E0; +} + +html[dark_mode='false'], html:not([dark_mode]) { + --body-color: #333; + --body-background-color: #f8f9fa; + --card-background: #fff; + --card-border: #e9ecef; + --label-color: #212529; + --box-shadow-color: rgba(0, 0, 0, 0.08); +} + +* { + box-sizing: border-box; +} + +html, body { + margin: 0; + padding: 0; + height: 600px; + overflow: hidden; } body { - padding: var(--header-height) 10px 0; - - display: flex; - justify-content: center; - align-items: center; -} - -html, html * { - font-family: serif; -} - -#back-button { - position: absolute; - top: 5px; - left: 5px; - - text-decoration: none; -} -header { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: var(--header-height); - background-color: var(--body-background-color); - border-bottom: 1px solid var(--label-color); - box-sizing: border-box; - - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; - gap: 15px; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif; + color: var(--body-color); + line-height: 1.4; } -header h1 { - font-size: 26px; -} - -main { - width: 500px; - +.page-container { + height: 600px; + max-width: 640px; + margin: 0 auto; + padding: 12px 16px; display: flex; flex-direction: column; } + +/* Back Button */ +#back-button { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 5px 10px; + text-decoration: none; + color: var(--body-color); + background: var(--card-background); + border: 1px solid var(--card-border); + border-radius: 6px; + font-size: 12px; + font-weight: 500; + transition: all 0.2s ease; + width: fit-content; +} + +#back-button:hover { + background: var(--accent-light); + border-color: var(--accent); + color: var(--accent); +} + +#back-button svg { + width: 14px; + height: 14px; +} + +/* Header */ +header { + flex-shrink: 0; + margin-bottom: 12px; +} + +.header-content { + text-align: center; + margin-top: 8px; +} + +.header-icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + background: var(--accent-light); + border-radius: 50%; + margin-bottom: 6px; +} + +.header-icon svg { + width: 18px; + height: 18px; + color: var(--accent); +} + +header h1 { + margin: 0; + font-size: 18px; + font-weight: 700; + color: var(--label-color); +} + +.subtitle { + display: none; +} + +/* Main content */ +main { + flex: 1; + display: flex; + flex-direction: column; + gap: 10px; + min-height: 0; +} + +/* Section Cards */ +.section-card { + background: var(--card-background); + border: 1px solid var(--card-border); + border-radius: 10px; + padding: 12px 14px; + box-shadow: 0 1px 4px var(--box-shadow-color); +} + +.section-card h2 { + display: flex; + align-items: center; + gap: 6px; + margin: 0 0 8px; + font-size: 14px; + font-weight: 600; + color: var(--label-color); +} + +.section-card h2 svg { + width: 16px; + height: 16px; + color: var(--accent); + flex-shrink: 0; +} + +.section-card > p { + margin: 0 0 10px; + font-size: 12px; + color: var(--body-color); + opacity: 0.85; +} + +/* Contact Grid */ +.contact-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 8px; +} + +.contact-card { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 6px; + padding: 10px 6px; + background: var(--body-background-color); + border-radius: 8px; + text-decoration: none; + transition: all 0.2s ease; +} + +.contact-card:hover { + transform: translateY(-1px); + box-shadow: 0 3px 10px var(--box-shadow-color); +} + +.contact-icon { + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + border-radius: 8px; + flex-shrink: 0; +} + +.contact-icon svg { + width: 18px; + height: 18px; +} + +.contact-icon.github { + background: rgba(51, 51, 51, 0.1); + color: var(--github); +} + +.contact-icon.form { + background: rgba(76, 175, 80, 0.1); + color: var(--form); +} + +.contact-icon.discord { + background: rgba(88, 101, 242, 0.1); + color: var(--discord); +} + +.contact-icon.email { + background: rgba(255, 87, 34, 0.1); + color: var(--email); +} + +.contact-info { + display: flex; + flex-direction: column; + align-items: center; +} + +.contact-name { + font-size: 11px; + font-weight: 600; + color: var(--label-color); + text-align: center; +} + +.contact-desc { + display: none; +} + +/* Troubleshooting */ +.troubleshoot-steps { + margin-bottom: 8px; +} + +.troubleshoot-steps p { + margin: 0 0 4px; + font-size: 12px; + color: var(--body-color); +} + +.troubleshoot-steps ol { + margin: 0; + padding-left: 18px; +} + +.troubleshoot-steps li { + margin-bottom: 2px; + font-size: 12px; + color: var(--body-color); +} + +.divider { + height: 1px; + background: var(--card-border); + margin: 8px 0; +} + +/* Help Request */ +.help-request p { + margin: 0 0 6px; + font-size: 12px; + color: var(--body-color); +} + +.info-box { + background: var(--body-background-color); + border-radius: 8px; +} + +.info-row { + display: flex; + justify-content: space-between; + align-items: center; + padding: 6px 12px; +} + +.info-row:not(:last-child) { + border-bottom: 1px solid var(--card-border); +} + +.info-label { + font-size: 12px; + font-weight: 600; + color: var(--label-color); +} + +.info-value { + font-size: 12px; + color: var(--body-color); + opacity: 0.8; +} diff --git a/src/options/feedback/feedback.html b/src/options/feedback/feedback.html index 7bada4e..b7535b0 100644 --- a/src/options/feedback/feedback.html +++ b/src/options/feedback/feedback.html @@ -21,39 +21,115 @@ -
- Back -

Feedback

-
+
+
+ + + Back + -
+
+
+ +
+

Feedback & Support

+

We'd love to hear from you

+
+
-
-

Have a feature idea?

-

I'd love to hear it. Here are some ways to get in touch:

-

GithubGoogle FormDiscord • or simply email me

-
+
-
-

Is RYS not working?

+
+

+ + Have a feature idea? +

+

Share your thoughts through any of these channels:

-

Try the following:

-
    -
  • refresh the YouTube page
  • -
  • disable and re-enable RYS
  • -
  • uninstall and reinstall RYS
  • -
+
+ +
+ +
+
+ GitHub Issues + Report bugs or request features +
+
-

If that doesn't work, email me at lawrencehook@gmail.com.

-

To help me diagnose the issue, please include the following:

-
    -
  • description of the issue
  • -
  • screenshot or video
  • -
  • browser: ex. firefox
  • -
  • version of RYS: 1.0.0
  • -
-
+ +
+ +
+
+ Google Form + Quick anonymous feedback +
+
-
+ +
+ +
+
+ Discord + Join our community +
+
+ + + +
+ Email + lawrencehook@gmail.com +
+
+
+ + +
+

+ + RYS not working? +

+ +
+

Try these quick fixes:

+
    +
  1. Refresh the YouTube page
  2. +
  3. Disable and re-enable RYS
  4. +
  5. Uninstall and reinstall RYS
  6. +
+
+ +
+ +
+

Still having issues? Email us with the following details:

+
+
+ Description + What's happening? +
+
+ Screenshot + A visual helps a lot +
+
+ Browser + detecting... +
+
+ RYS Version + detecting... +
+
+
+
+ + + diff --git a/src/options/footer.css b/src/options/footer.css index d116cce..e3a6a75 100644 --- a/src/options/footer.css +++ b/src/options/footer.css @@ -12,29 +12,33 @@ html[dark_mode='true'] #donate_button .footer-path { position: relative; text-align: center; height: var(--footer-height); + padding: 0 8px; - border-top: 1.5px solid var(--box-shadow-color); - /*border: 1px groove gray;*/ + border-top: 1px solid var(--box-shadow-color); + background-color: var(--body-background-color); display: flex; align-items: center; - /*justify-content: center;*/ + gap: 4px; } #footer svg { width: 20px; - /*height: 90%;*/ cursor: pointer; } #footer a { flex: 1; - flex-shrink: 1; - flex-grow: 1; + padding: 4px 8px; + border-radius: var(--border-radius); + font-size: 0.85rem; + font-weight: 500; + transition: background-color 0.15s ease; } #footer a:hover { - cursor: pointer; + cursor: pointer; + background-color: var(--box-shadow-color); } #donate_button:hover > svg > .footer-path { diff --git a/src/options/general.css b/src/options/general.css index 64c6289..ff6e358 100644 --- a/src/options/general.css +++ b/src/options/general.css @@ -2,9 +2,9 @@ --header-height: 30px; --header-padding: 5px; --body-height: 400px; - --footer-height: 25px; + --footer-height: 28px; - --border-radius: 1px; + --border-radius: 4px; --red-color: hsl(358 100% 49%); --blue-color: hsl(238 100% 49%); @@ -70,7 +70,7 @@ body { } body, body * { - font-family: Helvetica; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif; } .hidden { diff --git a/src/options/header.css b/src/options/header.css index 967e3bc..ee75725 100644 --- a/src/options/header.css +++ b/src/options/header.css @@ -16,16 +16,17 @@ html[dark_mode='false'] #header-light { display: none } #header { height: var(--header-height); - padding: var(--header-padding); + padding: var(--header-padding) 8px; - border-bottom: 1.5px solid var(--box-shadow-color); + border-bottom: 1px solid var(--box-shadow-color); + background-color: var(--body-background-color); display: flex; justify-content: space-between; align-items: center; - gap: 7px; + gap: 10px; - --icon-dim: 30px; + --icon-dim: 28px; } #header-logo-link { @@ -47,15 +48,18 @@ html[dark_mode='false'] #header-light { display: none } margin-right: auto; } #search_bar input { - padding: 3px 10px; - font-size: 1rem; + padding: 4px 12px; + font-size: 0.9rem; background-color: var(--body-background-color); border: 1px solid var(--box-shadow-color); - border-radius: var(--border-radius); - box-shadow: 0px 0px 0px 0px var(--box-shadow-color); - + border-radius: 6px; + transition: border-color 0.15s ease, box-shadow 0.15s ease; +} +#search_bar input:focus { + border-color: var(--light-blue); + box-shadow: 0 0 0 2px rgba(117, 102, 204, 0.15); } #power-icon { @@ -99,36 +103,39 @@ html[schedule='true'] #power-icon:hover #power-pop-up { max-width: var(--power-icon-dim); } #power-options { - font-size: 16px; + font-size: 14px; position: fixed; z-index: 10; top: calc(5px + var(--header-height) + 2 * var(--header-padding)); - padding: 3px 1px; + padding: 6px; user-select: none; background-color: var(--body-background-color); color: var(--body-color); - border-radius: var(--border-radius); - box-shadow: 0px 0px 6px 3px var(--box-shadow-color); + border-radius: 8px; + border: 1px solid var(--box-shadow-color); + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12); display: flex; flex-direction: column; } #power-options hr { - width: 92%; - margin: 3px auto; + width: 100%; + margin: 4px 0; + border: none; + border-top: 1px solid var(--box-shadow-color); } #power-options > *:not(hr) { - border-radius: var(--border-radius); - margin: 1px 3px; - padding: 4px 6px; + border-radius: 6px; + padding: 6px 10px; cursor: default; + transition: background-color 0.15s ease; } #power-options > *:not(hr):hover, #power-options > *:not(hr):hover * { - background-color: #0600FB; + background-color: var(--blue-color); color: white !important; } #power-options a, #power-options span { @@ -167,35 +174,38 @@ html[foo=bar] max-width: var(--icon-dim); } #settings-menu { - font-size: 16px; + font-size: 14px; position: fixed; z-index: 10; top: calc(5px + var(--header-height) + 2 * var(--header-padding)); - padding: 3px 1px; + padding: 6px; user-select: none; background-color: var(--body-background-color); color: var(--body-color); - border-radius: var(--border-radius); - box-shadow: 0px 0px 6px 3px var(--box-shadow-color); + border-radius: 8px; + border: 1px solid var(--box-shadow-color); + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12); display: flex; flex-direction: column; } #settings-menu hr { - width: 92%; - margin: 3px auto; + width: 100%; + margin: 4px 0; + border: none; + border-top: 1px solid var(--box-shadow-color); } #settings-menu > *:not(hr) { - border-radius: var(--border-radius); - margin: 1px 3px; - padding: 4px 6px; + border-radius: 6px; + padding: 6px 10px; cursor: default; + transition: background-color 0.15s ease; } #settings-menu > *:not(hr):hover { - background-color: #0600FB; + background-color: var(--blue-color); color: white; } #settings-menu a { diff --git a/src/options/options.css b/src/options/options.css index aa83ad0..3012f5c 100644 --- a/src/options/options.css +++ b/src/options/options.css @@ -1,24 +1,24 @@ #primary_options .section_container { - margin: 0px 2px; - padding: 5px; + margin: 0px 4px; + padding: 4px 6px; box-sizing: border-box; background-color: var(--body-background-color); - border-radius: 1px; -/* box-shadow: 0px 0px 0.5px 0.5px var(--box-shadow-color);*/ + border-radius: 6px; display: flex; flex-direction: column; - gap: 1px; + gap: 0px; } #primary_options .section_label { - margin-bottom: 3px; - margin-left: 35px; + margin-bottom: 2px; + margin-left: 33px; width: fit-content; - font-size: 1rem; + font-size: 0.9rem; + font-weight: 600; text-align: left; color: var(--label-color); @@ -34,7 +34,17 @@ div.option { cursor: default; display: flex; align-items: flex-start; - padding-left: 2px; + padding: 1px 4px; + border-radius: 4px; + transition: background-color 0.1s ease; +} + +div.option:hover { + background-color: rgba(0, 0, 0, 0.03); +} + +html[dark_mode='true'] div.option:hover { + background-color: rgba(255, 255, 255, 0.03); } /* Visual cues when the extension is enabled vs. disabled */ @@ -56,7 +66,7 @@ div.option { position: relative; top: 2px; margin-left: 8px; - font-size: 0.95rem; + font-size: 0.9rem; } .noselect { From 3d021923e6610036abb9d9c7fde9df4509615c45 Mon Sep 17 00:00:00 2001 From: Lawrence Hook Date: Sat, 24 Jan 2026 20:43:36 -0500 Subject: [PATCH 07/17] update versions --- src/chrome_manifest.json | 2 +- src/firefox_manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/chrome_manifest.json b/src/chrome_manifest.json index fe1498f..5959113 100644 --- a/src/chrome_manifest.json +++ b/src/chrome_manifest.json @@ -3,7 +3,7 @@ "description": "Spend less time on YouTube. Customize YouTube's user interface to be less engaging.", "homepage_url": "https://github.com/lawrencehook/remove-youtube-suggestions", "manifest_version": 3, - "version": "4.3.66", + "version": "4.3.67", "icons": { "16": "/images/16.png", diff --git a/src/firefox_manifest.json b/src/firefox_manifest.json index b1b1e35..5ed7252 100644 --- a/src/firefox_manifest.json +++ b/src/firefox_manifest.json @@ -3,7 +3,7 @@ "description": "Spend less time on YouTube. Customize YouTube's user interface to be less engaging.", "homepage_url": "https://github.com/lawrencehook/remove-youtube-suggestions", "manifest_version": 2, - "version": "4.3.66", + "version": "4.3.67", "icons": { "16": "/images/16.png", From b19351609679acbb88fbb8b81a1e491f3620eec0 Mon Sep 17 00:00:00 2001 From: Lawrence Hook Date: Tue, 27 Jan 2026 19:57:25 -0500 Subject: [PATCH 08/17] feat: add announcement banner for upcoming premium features Adds a dismissible banner at the top of the options page announcing upcoming premium features with a link to the explainer page. Co-Authored-By: Claude Opus 4.5 --- src/options/general.css | 43 +++++++++++++++++++++++++++++++++++++++++ src/options/main.html | 6 ++++++ src/options/main.js | 22 +++++++++++++++++++++ 3 files changed, 71 insertions(+) diff --git a/src/options/general.css b/src/options/general.css index ff6e358..9fec6b6 100644 --- a/src/options/general.css +++ b/src/options/general.css @@ -99,3 +99,46 @@ input:focus { ::-webkit-scrollbar-thumb { background: var(--body-color); } + +/* Announcement Banner */ +#announcement_banner { + display: flex; + align-items: center; + gap: 8px; + padding: 10px 12px; + margin: 8px; + background: var(--blue-color); + border-radius: var(--border-radius); + font-size: 13px; +} + +#announcement_banner * { + color: #fff; +} + +#announcement_banner span { + flex: 1; +} + +#announcement_banner a { + text-decoration: underline; + opacity: 0.9; +} + +#announcement_banner a:hover { + opacity: 1; +} + +#announcement_banner button { + background: none; + border: none; + font-size: 18px; + cursor: pointer; + padding: 0 4px; + opacity: 0.7; + line-height: 1; +} + +#announcement_banner button:hover { + opacity: 1; +} diff --git a/src/options/main.html b/src/options/main.html index 7eb5fdd..ccbbf0c 100644 --- a/src/options/main.html +++ b/src/options/main.html @@ -230,6 +230,12 @@ + +
diff --git a/src/options/main.js b/src/options/main.js index 56ddd5d..564e2b7 100644 --- a/src/options/main.js +++ b/src/options/main.js @@ -35,6 +35,11 @@ document.addEventListener("DOMContentLoaded", () => { return acc; }, {}); + // Show logging opt-in modal if user hasn't responded yet + if (!localSettings.log_prompt_answered) { + showLogPrompt(); + } + browser.tabs.query({ currentWindow: true, active: true }, tabs => { if (!tabs || tabs.length === 0) return; const [{ url }] = tabs; @@ -439,3 +444,28 @@ function initAnnouncementBanner() { })); }); } + + +// Logging opt-in banner +function showLogPrompt() { + const banner = document.getElementById('log_prompt_banner'); + const yesBtn = document.getElementById('log_prompt_yes'); + const noBtn = document.getElementById('log_prompt_no'); + const dismissBtn = document.getElementById('log_prompt_dismiss'); + + banner.hidden = false; + + yesBtn.addEventListener('click', () => { + browser.storage.local.set({ log_enabled: true, log_prompt_answered: true }); + banner.hidden = true; + }); + + noBtn.addEventListener('click', () => { + browser.storage.local.set({ log_enabled: false, log_prompt_answered: true }); + banner.hidden = true; + }); + + dismissBtn.addEventListener('click', () => { + banner.hidden = true; + }); +} diff --git a/src/shared/analytics.js b/src/shared/analytics.js index 66aa761..7598b6e 100644 --- a/src/shared/analytics.js +++ b/src/shared/analytics.js @@ -4,7 +4,7 @@ for(h=0;h Date: Wed, 4 Feb 2026 19:22:30 -0500 Subject: [PATCH 15/17] chore: clean up dead code and fix naming MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove debug console.log statements in options/main.js - Remove unused TEMPLATE_FIELDSET variable - Fix SEC_IN_WEEK → MIN_IN_WEEK (it was actually minutes) - Remove dead setCookie/getCookie functions Co-Authored-By: Claude Opus 4.5 --- src/content-script/main.js | 23 ----------------------- src/options/main.js | 3 --- src/shared/utils.js | 4 ++-- 3 files changed, 2 insertions(+), 28 deletions(-) diff --git a/src/content-script/main.js b/src/content-script/main.js index f67d69f..75822c6 100644 --- a/src/content-script/main.js +++ b/src/content-script/main.js @@ -696,29 +696,6 @@ function handleNewPage() { requestRunDynamicSettings(); } -function setCookie(cname, cvalue, exdays=370) { - const d = new Date(); - d.setTime(d.getTime() + (exdays*24*60*60*1000)); - let expires = "expires="+ d.toUTCString(); - document.cookie = cname + "=" + cvalue + ";" + expires + ";domain=.youtube.com;path=/"; -} - -function getCookie(cname) { - let name = cname + "="; - let decodedCookie = decodeURIComponent(document.cookie); - let ca = decodedCookie.split(';'); - for (let i = 0; i < ca.length; i++) { - let c = ca[i]; - while (c.charAt(0) == ' ') { - c = c.substring(1); - } - if (c.indexOf(name) == 0) { - return c.substring(name.length, c.length); - } - } - return ""; -} - function updateSetting(id, value) { HTML.setAttribute(id, value); cache[id] = value; diff --git a/src/options/main.js b/src/options/main.js index 564e2b7..935940b 100644 --- a/src/options/main.js +++ b/src/options/main.js @@ -5,7 +5,6 @@ const HTML = document.documentElement; const SIDEBAR = document.getElementById('sidebar'); const TEMPLATE_SIDEBAR_SECTION = document.getElementById('template_sidebar_section'); const OPTIONS_LIST = document.getElementById('primary_options'); -const TEMPLATE_FIELDSET = document.getElementById('template_fieldset'); const TEMPLATE_SECTION = document.getElementById('template_section'); const TEMPLATE_OPTION = document.getElementById('template_option'); const TIMER_CONTAINER = document.getElementById('timer_container'); @@ -195,8 +194,6 @@ function populateOptions(SECTIONS, headerSettings, SETTING_VALUES) { const input = qs('input', LOCK_CODE_CONTAINER); qs('div#code', LOCK_CODE_CONTAINER).innerText = code; input.addEventListener('input', e => { - console.log(input.value); - console.log(); if (input.value === code) { HTML.removeAttribute('entering_lock_code', ''); } diff --git a/src/shared/utils.js b/src/shared/utils.js index 34c95a2..2191b5b 100644 --- a/src/shared/utils.js +++ b/src/shared/utils.js @@ -66,12 +66,12 @@ function nextScheduleChange(times, days) { if (!times || !days) return; const current = checkSchedule(times, days); - const SEC_IN_WEEK = 10_080; + const MIN_IN_WEEK = 10_080; let testDate = new Date(); testDate.setSeconds(0); let next = checkSchedule(times, days, testDate); let i = 0; - while (current === next && i < SEC_IN_WEEK) { + while (current === next && i < MIN_IN_WEEK) { i += 1; testDate = new Date(testDate.getTime() + 60_000); next = checkSchedule(times, days, testDate); From 77d36b13604436da622bd667886c148ce696665b Mon Sep 17 00:00:00 2001 From: Lawrence Hook Date: Wed, 4 Feb 2026 19:26:10 -0500 Subject: [PATCH 16/17] chore: complete remaining quick wins from TODO - Move duplicated YouTube URL regexes to shared/utils.js - Delete commented-out code blocks (theater mode, playback speed, cookie manipulation, debug vars) - Add Page Visibility API to pause polling when tab is hidden - Update TODO.md to reflect completed items Co-Authored-By: Claude Opus 4.5 --- TODO.md | 19 ++---------- src/content-script/main.js | 61 +++++++------------------------------- src/options/main.js | 6 ---- src/shared/utils.js | 10 ++++++- 4 files changed, 22 insertions(+), 74 deletions(-) diff --git a/TODO.md b/TODO.md index 31f62d6..223cb7f 100644 --- a/TODO.md +++ b/TODO.md @@ -1,18 +1,5 @@ # TODO -## Quick Wins - -| Issue | Effort | -|-------|--------| -| Remove debug `console.log` in `options/main.js:174-175` | 1 min | -| Sync manifest versions (4.3.65 vs 4.3.66) | 1 min | -| Delete unused `TEMPLATE_FIELDSET` in `options/main.js:8` | 1 min | -| Delete dead `setCookie`/`getCookie` in `content-script/main.js:657-678` | 1 min | -| Fix `SEC_IN_WEEK` naming in `utils.js:69` (it's actually minutes) | 1 min | -| Move duplicated regexes to `shared/utils.js` | 5 min | -| Delete commented-out code blocks in `content-script/main.js` | 5 min | -| Add Page Visibility API to pause polling when tab is hidden | 10 min | - ## Code Quality - [ ] Auto-generate `idToShortId` map from `SECTIONS` instead of manual maintenance @@ -23,9 +10,9 @@ ## i18n / Localization Hardcoded English strings that break for non-English YouTube: -- `content-script/main.js:171` — `'for you'` comparison -- `content-script/main.js:211` — `'Streamed'` check -- `content-script/main.js:467-468` — `'All'`, `'Related'` chip names +- `content-script/main.js` — `'for you'` comparison +- `content-script/main.js` — `'Streamed'` check +- `content-script/main.js` — `'All'`, `'Related'` chip names ## Incomplete Features diff --git a/src/content-script/main.js b/src/content-script/main.js index 75822c6..4a6449a 100644 --- a/src/content-script/main.js +++ b/src/content-script/main.js @@ -10,12 +10,6 @@ const REDIRECT_URLS = { "redirect_to_library": 'https://www.youtube.com/feed/library', }; -const resultsPageRegex = new RegExp('.*://.*youtube\.com/results.*', 'i'); -const homepageRegex = new RegExp('.*://(www|m)\.youtube\.com(/)?$', 'i'); -const shortsRegex = new RegExp('.*://.*youtube\.com/shorts.*', 'i'); -const videoRegex = new RegExp('.*://.*youtube\.com/watch\\?v=.*', 'i'); -const channelRegex = new RegExp('.*://.*youtube\.com/(@|channel)', 'i'); -const subsRegex = new RegExp(/\/feed\/subscriptions$/, 'i'); // Dynamic settings variables const cache = {}; @@ -49,7 +43,7 @@ let theaterClicked = false, hyper = false; let onResultsPage = resultsPageRegex.test(url); let onHomepage = homepageRegex.test(url); let onShorts = shortsRegex.test(url); -let onVideo = videoRegex.test(url); +let onVideo = videoPageRegex.test(url); let onChannel = channelRegex.test(url); let onSubs = subsRegex.test(url); let settingsInit = false @@ -57,8 +51,6 @@ let settingsInit = false let dynamicIters = 0; let frameRequested = false; let isRunning = false; -// let lastRun = Date.now(); -// let counter = 0; let lastScheduleCheck; const scheduleInterval = 2_000; // 2 seconds let lastRedirect; @@ -113,8 +105,6 @@ document.addEventListener("DOMContentLoaded", e => handleNewPage()); // Dynamic settings (i.e. js instead of css) function runDynamicSettings() { if (isRunning) return; - // console.log('runDynamicSettings', Date.now() - lastRun); - // lastRun = Date.now(); isRunning = true; dynamicIters += 1; const on = cache['global_enable'] === true; @@ -340,16 +330,6 @@ function runDynamicSettings() { } } - // // Enable theater mode - // if (cache['enable_theater'] === true && !theaterClicked) { - // const theaterButton = qsa('button[title="Theater mode (t)"]')?.[0]; - // if (theaterButton && theaterButton.display !== 'none') { - // console.log('theaterButton.click();') - // theaterButton.click(); - // theaterClicked = true; - // } - // } - // Skip through ads if (cache['auto_skip_ads'] === true) { @@ -409,10 +389,6 @@ function runDynamicSettings() { } } - // if (cache['change_playback_speed']) { - // document.getElementsByTagName("video")[0].playbackRate = Number(cache['change_playback_speed']); - // } - // Hide all but the timestamped comments if (cache['remove_non_timestamp_comments']) { const timestamps = qsa('yt-formatted-string:not(.published-time-text).ytd-comment-renderer > a.yt-simple-endpoint[href^="/watch"]'); @@ -422,30 +398,6 @@ function runDynamicSettings() { }); } - // // Disable play on hover - // if (dynamicIters % 10 === 0) { - // const prefCookie = getCookie('PREF'); - // const prefObj = prefCookie?.split('&')?.reduce((acc, x) => { - // const [ key, value ] = x.split('='); - // acc[key] = value; - // return acc; - // }, {}); - // if (prefObj) { - // const f7 = prefObj['f7'] || '0'; - // const playOnHoverEnabled = f7[f7.length-1] === '0'; - - // if (cache['disable_play_on_hover'] && playOnHoverEnabled) { - // prefObj['f7'] = f7.substring(0, f7.length-1) + '1'; - // const newPref = Object.entries(prefObj).map(([key, value]) => `${key}=${value}`).join('&'); - // setCookie('PREF', newPref); - // } else if (!cache['disable_play_on_hover'] && !playOnHoverEnabled) { - // prefObj['f7'] = f7.substring(0, f7.length-1) + '0'; - // const newPref = Object.entries(prefObj).map(([key, value]) => `${key}=${value}`).join('&'); - // setCookie('PREF', newPref); - // } - // } - // } - // Show description if (cache['expand_description'] || cache['remove_comments']) { const expandButton = qsa('#description #expand.button'); @@ -577,11 +529,18 @@ function runDynamicSettings() { function requestRunDynamicSettings() { if (frameRequested || isRunning) return; + if (document.hidden) return; // Pause polling when tab is hidden frameRequested = true; - // setTimeout(() => runDynamicSettings(), 50); setTimeout(() => runDynamicSettings(), Math.min(100, 50 + 10 * dynamicIters)); } +// Resume polling when tab becomes visible +document.addEventListener('visibilitychange', () => { + if (!document.hidden) { + requestRunDynamicSettings(); + } +}); + function injectAnnouncementBanners() { if (!onHomepage) return; @@ -637,7 +596,7 @@ function handleNewPage() { onResultsPage = resultsPageRegex.test(url); onHomepage = homepageRegex.test(url); onShorts = shortsRegex.test(url); - onVideo = videoRegex.test(url); + onVideo = videoPageRegex.test(url); onChannel = channelRegex.test(url); onSubs = subsRegex.test(url); settingsInit = false; diff --git a/src/options/main.js b/src/options/main.js index 935940b..4212939 100644 --- a/src/options/main.js +++ b/src/options/main.js @@ -12,12 +12,6 @@ const LOCK_CODE_CONTAINER = document.getElementById('lock_code_container'); let openedTime = Date.now(); let currentUrl; -const resultsPageRegex = new RegExp('.*://.*youtube\\.com/results.*', 'i'); -const videoPageRegex = new RegExp('.*://(www|m)\\.youtube\\.com/watch\\?v=.*', 'i'); -const homepageRegex = new RegExp('.*://(www|m)\\.youtube\\.com/$', 'i'); -const channelRegex = new RegExp('.*://.*youtube\.com/(@|channel)', 'i'); -const shortsRegex = new RegExp('.*://.*youtube\.com/shorts.*', 'i'); -const subsRegex = new RegExp(/\/feed\/subscriptions$/, 'i'); document.addEventListener("DOMContentLoaded", () => { recordEvent('Page View: Options'); diff --git a/src/shared/utils.js b/src/shared/utils.js index 2191b5b..f9cd021 100644 --- a/src/shared/utils.js +++ b/src/shared/utils.js @@ -3,6 +3,14 @@ function uniq(array) { return Array.from(new Set(array)) } function qs(query, root=document) { return root.querySelector(query) } function qsa(query, root=document) { return Array.from(root.querySelectorAll(query)) } +/* YouTube URL Regexes */ +const resultsPageRegex = new RegExp('.*://.*youtube\\.com/results.*', 'i'); +const homepageRegex = new RegExp('.*://(www|m)\\.youtube\\.com(/)?$', 'i'); +const shortsRegex = new RegExp('.*://.*youtube\\.com/shorts.*', 'i'); +const videoPageRegex = new RegExp('.*://(www|m)\\.youtube\\.com/watch\\?v=.*', 'i'); +const channelRegex = new RegExp('.*://.*youtube\\.com/(@|channel)', 'i'); +const subsRegex = new RegExp(/\/feed\/subscriptions$/, 'i'); + /* Scheduling */ function timeIsValid(times) { @@ -77,7 +85,7 @@ function nextScheduleChange(times, days) { next = checkSchedule(times, days, testDate); } - if (i > SEC_IN_WEEK) return; + if (i >= MIN_IN_WEEK) return; return testDate; } From c3c7db3315a188a45f731b432f0782f81ae1ca62 Mon Sep 17 00:00:00 2001 From: Lawrence Hook Date: Wed, 4 Feb 2026 20:06:14 -0500 Subject: [PATCH 17/17] perf: optimize polling and reduce CPU usage Options page: - Pause timeLoop when popup is hidden (Page Visibility API) - Use { once: true } for log prompt button listeners - Remove stray console.log in settings import Content script: - Slow polling from 100ms to 500ms after page settles (was stuck at 100ms) - Stop playlist autoplay interval once manager is found - Only check dismiss buttons every 5th iteration - Only check reveal boxes for first 30 iterations - Only query clip button DOM if setting is enabled - Skip subscriptions page queries if no sub-settings enabled - Fix ad skip button selector for current YouTube UI Co-Authored-By: Claude Opus 4.5 --- TODO.md | 1 - src/content-script/main.js | 52 +++++++++++++++++++++--------------- src/options/main.js | 27 ++++++++++++++++--- src/options/settings-menu.js | 1 - 4 files changed, 53 insertions(+), 28 deletions(-) diff --git a/TODO.md b/TODO.md index 223cb7f..5c3432a 100644 --- a/TODO.md +++ b/TODO.md @@ -5,7 +5,6 @@ - [ ] Auto-generate `idToShortId` map from `SECTIONS` instead of manual maintenance - [ ] Add storage validation/migration for schema changes between versions - [ ] Improve error handling beyond `console.log(error)` -- [ ] Clean up event listeners in `options/main.js` to prevent memory leaks ## i18n / Localization diff --git a/src/content-script/main.js b/src/content-script/main.js index 4a6449a..1272e0c 100644 --- a/src/content-script/main.js +++ b/src/content-script/main.js @@ -194,8 +194,10 @@ function runDynamicSettings() { } } - // Subscriptions page options - if (onSubs) { + // Subscriptions page options (only if any sub-page settings are enabled) + const subsSettingsEnabled = cache['remove_sub_shorts'] || cache['remove_sub_live'] || + cache['remove_sub_upcoming'] || cache['remove_sub_premiere'] || cache['remove_sub_vods']; + if (onSubs && subsSettingsEnabled) { const badgeSelector = 'ytd-badge-supported-renderer'; const upcomingBadgeSelector = 'ytd-thumbnail-overlay-time-status-renderer[overlay-style="UPCOMING"]'; const shortsBadgeSelector = 'ytd-thumbnail-overlay-time-status-renderer[overlay-style="SHORTS"]'; @@ -263,13 +265,15 @@ function runDynamicSettings() { }); } - // Click on "dismiss" buttons - const dismissButtons = qsa('#dismiss-button'); - dismissButtons.forEach(dismissButton => { - if (dismissButton && dismissButton.offsetParent) { - dismissButton.click(); - } - }) + // Click on "dismiss" buttons (only check occasionally, not every iteration) + if (dynamicIters % 5 === 0) { + const dismissButtons = qsa('#dismiss-button'); + dismissButtons.forEach(dismissButton => { + if (dismissButton && dismissButton.offsetParent) { + dismissButton.click(); + } + }); + } // Disable autoplay if (cache['disable_autoplay'] === true) { @@ -341,10 +345,7 @@ function runDynamicSettings() { }); // Click on "Skip ad" button - const skipButtons = qsa('.ytp-ad-skip-button'). - concat(qsa('.ytp-ad-skip-button-modern')). - concat(qsa('.ytp-skip-ad-button')). - concat(qsa(CSS.escape("button#skip-button:2"))); + const skipButtons = qsa('.ytp-ad-skip-button, .ytp-ad-skip-button-modern, .ytp-skip-ad-button, .ytp-skip-ad button'); const skippableAd = skipButtons?.some(button => button.offsetParent); if (skippableAd) { @@ -460,8 +461,8 @@ function runDynamicSettings() { }); } - // Reveal suggestions boxes - REVEAL_BOX_CONFIGS.forEach(({ containerSelector, boxId, message, showAction, revealSetting }) => { + // Reveal suggestions boxes (only check on early iterations) + if (dynamicIters <= 30) REVEAL_BOX_CONFIGS.forEach(({ containerSelector, boxId, message, showAction, revealSetting }) => { if (closedRevealBoxes.has(boxId)) return; if (qs(`#${boxId}`)) return; @@ -513,9 +514,11 @@ function runDynamicSettings() { // Video Player: hide the 'clip' button. // The path[d=...] selector selects scissor SVGs. - qsa('path[d^="M8 7c0 .55-.45 1-1 1s-1-.45-1-1"]'). - map(path => path.closest('#menu button')). - forEach(b => b.setAttribute('scissor_button', '')); + if (cache['remove_clip_button']) { + qsa('path[d^="M8 7c0 .55-.45 1-1 1s-1-.45-1-1"]'). + map(path => path.closest('#menu button')). + forEach(b => b?.setAttribute('scissor_button', '')); + } } catch (error) { console.log(error); @@ -531,7 +534,9 @@ function requestRunDynamicSettings() { if (frameRequested || isRunning) return; if (document.hidden) return; // Pause polling when tab is hidden frameRequested = true; - setTimeout(() => runDynamicSettings(), Math.min(100, 50 + 10 * dynamicIters)); + // Start fast (100ms), slow down to 500ms after page settles + const delay = dynamicIters < 20 ? 100 : Math.min(500, 100 + dynamicIters * 10); + setTimeout(() => runDynamicSettings(), delay); } // Resume polling when tab becomes visible @@ -571,13 +576,16 @@ function injectScripts() { script.type = "text/javascript"; script.innerText = ` (function() { -let pm; +let pm, intervalId; function f() { if (!pm) pm = document.querySelector('yt-playlist-manager'); - if (pm) pm.canAutoAdvance_ = false; + if (pm) { + pm.canAutoAdvance_ = false; + if (intervalId) clearInterval(intervalId); + } } f(); -setInterval(f, 100); +intervalId = setInterval(f, 100); })() `; document.body?.appendChild(script); diff --git a/src/options/main.js b/src/options/main.js index 4212939..666ee4e 100644 --- a/src/options/main.js +++ b/src/options/main.js @@ -391,7 +391,12 @@ function timerLoop() { // For timedChanged and scheduling +let timeLoopId = null; + function timeLoop() { + // Don't run if page is hidden + if (document.hidden) return; + const { schedule, scheduleTimes, scheduleDays, nextTimedChange, nextTimedValue @@ -416,9 +421,23 @@ function timeLoop() { } updateTimeInfo(); - setTimeout(() => timeLoop(), 2_000); + timeLoopId = setTimeout(() => timeLoop(), 2_000); } +// Pause/resume timeLoop based on visibility +document.addEventListener('visibilitychange', () => { + if (document.hidden) { + if (timeLoopId) { + clearTimeout(timeLoopId); + timeLoopId = null; + } + } else { + if (!timeLoopId) { + timeLoop(); + } + } +}); + // Announcement banners function initAnnouncementBanner() { @@ -449,14 +468,14 @@ function showLogPrompt() { yesBtn.addEventListener('click', () => { browser.storage.local.set({ log_enabled: true, log_prompt_answered: true }); banner.hidden = true; - }); + }, { once: true }); noBtn.addEventListener('click', () => { browser.storage.local.set({ log_enabled: false, log_prompt_answered: true }); banner.hidden = true; - }); + }, { once: true }); dismissBtn.addEventListener('click', () => { banner.hidden = true; - }); + }, { once: true }); } diff --git a/src/options/settings-menu.js b/src/options/settings-menu.js index ff90d8f..d5c443a 100644 --- a/src/options/settings-menu.js +++ b/src/options/settings-menu.js @@ -294,7 +294,6 @@ importSubmit.addEventListener('click', e => { try { const settingsObj = settingsStrToObj(settingsStr); - console.log(settingsObj); browser.storage.local.set(settingsObj).then(x => { updateSettings(settingsObj);