Skip to content

Separate playlist management buttons#1076

Merged
VampireChicken12 merged 3 commits into
YouTube-Enhancer:devfrom
dnicolson:update-playlist-management-buttons
Nov 20, 2025
Merged

Separate playlist management buttons#1076
VampireChicken12 merged 3 commits into
YouTube-Enhancer:devfrom
dnicolson:update-playlist-management-buttons

Conversation

@dnicolson
Copy link
Copy Markdown
Contributor

@dnicolson dnicolson commented Nov 20, 2025

This implements features requested in #951 (comment).

Summary by CodeRabbit

Release Notes

  • New Features

    • Refactored playlist management controls into two independent settings. Users can now separately enable or disable the remove video button and mark as unwatched button on playlist items for more precise configuration.
  • Style

    • Enhanced action buttons with hover effects to provide improved visual feedback.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Nov 20, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

The PR refactors the playlist management buttons feature by splitting a single enablePlaylistManagementButtons configuration flag into two separate flags: enableRemoveVideoButton and enableMarkAsUnwatchedButton. This change propagates across localization files, type definitions, settings UI, feature implementation, and extension messaging.

Changes

Cohort / File(s) Summary
Localization: Single Flag Removal
public/locales/{ca-ES,cs-CZ,de-DE,en-GB,es-ES,fa-IR,fr-FR,he-IL,hi-IN,ja-JP,ko-KR,nl-NL,pl-PL,pt-BR,ru-RU,sv-SE,tr-TR,uk-UA,vi-VN,zh-CN,zh-TW}.json
Removed enablePlaylistManagementButtons entry with label and title across 21 locale files.
Localization: Flag Replacement
public/locales/en-US.json
Removed enablePlaylistManagementButtons and added two new entries: enableRemoveVideoButton and enableMarkAsUnwatchedButton, each with distinct label and title text.
Localization: TypeScript Definitions
public/locales/en-US.json.d.ts
Updated EnUS interface to replace enablePlaylistManagementButtons with enableMarkAsUnwatchedButton and enableRemoveVideoButton type definitions.
Configuration & Types
src/types/index.ts, src/utils/constants.ts
Replaced enable_playlist_management_buttons boolean with enable_playlist_remove_button and enable_playlist_reset_button in configuration type, default configuration, and import schema. Updated ExtensionSendOnlyMessageMappings to replace playlistManagementButtonsChange with playlistRemoveButtonChange and playlistResetButtonChange.
Settings UI
src/components/Settings/Settings.tsx
Replaced single checkbox for enable_playlist_management_buttons with a new "Playlist management settings" section containing two separate checkboxes for enable_playlist_remove_button and enable_playlist_reset_button.
Feature Implementation
src/features/playlistManagementButtons/index.ts
Refactored to use two separate enable flags, introduced debounced MutationObserver, added THUMBNAIL_OVERLAY_SELECTOR constant, and split button insertion logic to conditionally create remove and reset buttons based on individual flag states.
Feature Styling
src/features/playlistManagementButtons/ActionButton/index.css
Added hover styles for .yte-action-button with white background for default theme and semi-transparent white for dark mode.
Content Scripts
src/pages/content/index.ts
Replaced single enable_playlist_management_buttons storage change handler with two handlers for enable_playlist_remove_button and enable_playlist_reset_button, each sending distinct message types with button-specific payloads.
Embedded Page Messaging
src/pages/embedded/index.ts
Updated message switch to replace playlistManagementButtonsChange case with two new cases (playlistRemoveButtonChange and playlistResetButtonChange), both triggering full button reinitialization.

Sequence Diagram

sequenceDiagram
    participant User
    participant Settings as Settings UI
    participant Storage as Chrome Storage
    participant ContentPage as Content Page
    participant Embedded as Embedded Page
    participant Feature as Playlist Feature
    participant DOM as Playlist DOM

    User->>Settings: Toggle "Remove video button"
    Settings->>Storage: Save enable_playlist_remove_button
    Storage->>ContentPage: Storage change event
    ContentPage->>Embedded: Send playlistRemoveButtonChange message
    Embedded->>Feature: disablePlaylistManagementButtons()
    Embedded->>Feature: enablePlaylistManagementButtons()
    Feature->>DOM: addButtonToPlaylistItems()<br/>(checks enable_playlist_remove_button<br/>and enable_playlist_reset_button)
    DOM-->>Feature: Remove button inserted<br/>(if flag enabled)
    Feature-->>User: UI updated
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20–25 minutes

  • src/features/playlistManagementButtons/index.ts: Complex refactoring with new observer debouncing, selector constant introduction, and conditional button insertion logic based on two separate flags. Verify DOM selectors (THUMBNAIL_OVERLAY_SELECTOR) and button state detection work correctly.
  • Message flow consistency: Ensure playlistRemoveButtonChange and playlistResetButtonChange handlers in src/pages/embedded/index.ts properly reinitialize both buttons on either flag change.
  • Locale alignment: Confirm en-US localization keys (enableRemoveVideoButton, enableMarkAsUnwatchedButton) match all downstream code references and TypeScript definitions.

Possibly related PRs

  • Add playlist management buttons #951: Introduces the original single enable_playlist_management_buttons flag and related feature module that this PR refactors into two separate flags.
  • Dev #1072: Modifies playlist management buttons DOM observer and initialization logic that overlaps with this PR's refactored addButtonToPlaylistItems implementation.
  • Version 1.30.0 release #1058: Updates the same playlist management feature modules (settings, locales, feature code) with overlapping configuration and messaging changes.

Poem

🐰 Two buttons bloom where one did dwell,
Remove and mark—they work so well!
Observers debounce with springy care,
Playlist magic floats through the air! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Separate playlist management buttons' directly and accurately describes the main change: splitting a single enable_playlist_management_buttons feature flag into two separate flags (enable_playlist_remove_button and enable_playlist_reset_button) across all locale files, components, and types.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 57b89de and e54bc3c.

📒 Files selected for processing (31)
  • public/locales/ca-ES.json (0 hunks)
  • public/locales/cs-CZ.json (0 hunks)
  • public/locales/de-DE.json (0 hunks)
  • public/locales/en-GB.json (0 hunks)
  • public/locales/en-US.json (1 hunks)
  • public/locales/en-US.json.d.ts (1 hunks)
  • public/locales/es-ES.json (0 hunks)
  • public/locales/fa-IR.json (0 hunks)
  • public/locales/fr-FR.json (0 hunks)
  • public/locales/he-IL.json (0 hunks)
  • public/locales/hi-IN.json (0 hunks)
  • public/locales/it-IT.json (1 hunks)
  • public/locales/ja-JP.json (0 hunks)
  • public/locales/ko-KR.json (0 hunks)
  • public/locales/nl-NL.json (0 hunks)
  • public/locales/pl-PL.json (0 hunks)
  • public/locales/pt-BR.json (0 hunks)
  • public/locales/ru-RU.json (0 hunks)
  • public/locales/sv-SE.json (0 hunks)
  • public/locales/tr-TR.json (0 hunks)
  • public/locales/uk-UA.json (0 hunks)
  • public/locales/vi-VN.json (0 hunks)
  • public/locales/zh-CN.json (0 hunks)
  • public/locales/zh-TW.json (0 hunks)
  • src/components/Settings/Settings.tsx (1 hunks)
  • src/features/playlistManagementButtons/ActionButton/index.css (1 hunks)
  • src/features/playlistManagementButtons/index.ts (5 hunks)
  • src/pages/content/index.ts (1 hunks)
  • src/pages/embedded/index.ts (1 hunks)
  • src/types/index.ts (2 hunks)
  • src/utils/constants.ts (2 hunks)
💤 Files with no reviewable changes (21)
  • public/locales/vi-VN.json
  • public/locales/pl-PL.json
  • public/locales/ko-KR.json
  • public/locales/fr-FR.json
  • public/locales/cs-CZ.json
  • public/locales/es-ES.json
  • public/locales/uk-UA.json
  • public/locales/ru-RU.json
  • public/locales/hi-IN.json
  • public/locales/ja-JP.json
  • public/locales/en-GB.json
  • public/locales/he-IL.json
  • public/locales/fa-IR.json
  • public/locales/tr-TR.json
  • public/locales/pt-BR.json
  • public/locales/nl-NL.json
  • public/locales/de-DE.json
  • public/locales/sv-SE.json
  • public/locales/zh-TW.json
  • public/locales/zh-CN.json
  • public/locales/ca-ES.json
🧰 Additional context used
🧬 Code graph analysis (4)
src/pages/embedded/index.ts (1)
src/features/playlistManagementButtons/index.ts (2)
  • disablePlaylistManagementButtons (29-39)
  • enablePlaylistManagementButtons (41-142)
src/components/Settings/Settings.tsx (3)
src/components/Settings/components/SettingSection.tsx (1)
  • SettingSection (9-24)
src/components/Settings/components/SettingTitle.tsx (1)
  • SettingTitle (3-6)
src/components/Settings/components/Setting.tsx (1)
  • Setting (42-74)
src/features/playlistManagementButtons/index.ts (3)
src/utils/utilities.ts (2)
  • waitForSpecificMessage (833-860)
  • IsDarkMode (538-541)
src/features/playlistManagementButtons/ActionButton/index.ts (1)
  • createActionButton (24-69)
src/features/playlistLength/utils.ts (1)
  • getPlaylistId (178-181)
src/pages/content/index.ts (1)
src/utils/utilities.ts (1)
  • sendExtensionOnlyMessage (744-758)
🔇 Additional comments (6)
src/types/index.ts (1)

270-271: Types for new playlist flags and messages are consistent across the codebase

configuration now exposes enable_playlist_remove_button and enable_playlist_reset_button, and ExtensionSendOnlyMessageMappings defines playlistRemoveButtonChange / playlistResetButtonChange with payloads that match their use in src/pages/content/index.ts and src/pages/embedded/index.ts. No issues spotted here.

Also applies to: 443-444

src/features/playlistManagementButtons/ActionButton/index.css (1)

14-20: Hover styling for action buttons looks good

The added hover backgrounds (white in light mode, semi-transparent in dark mode) are consistent with the existing button sizing and icon color logic; nothing problematic here.

src/pages/embedded/index.ts (1)

937-941: Playlist management buttons refresh path is wired correctly

On playlistRemoveButtonChange / playlistResetButtonChange you fully tear down and re-enable playlist management buttons, letting enablePlaylistManagementButtons re-read the latest options and rebuild DOM/observers. This matches how the feature is structured and should keep the buttons in sync with the two toggles.

src/utils/constants.ts (1)

86-87: Defaults and import schema for new playlist flags are aligned

defaultConfiguration and configurationImportSchema now both expose enable_playlist_remove_button and enable_playlist_reset_button as optional booleans with sane defaults. This keeps runtime config, validation, and types in sync.

Also applies to: 195-196

public/locales/en-US.json (1)

312-319: New English strings for playlist management toggles are clear and consistent

The enableRemoveVideoButton and enableMarkAsUnwatchedButton entries under settings.sections.miscellaneous.features accurately describe the new toggles and align with the playlist management behavior implemented elsewhere. No issues here.

src/pages/content/index.ts (1)

424-433: Storage change handling for new playlist flags is correctly wired

When enable_playlist_remove_button or enable_playlist_reset_button changes, you emit playlistRemoveButtonChange / playlistResetButtonChange with the expected *Enabled booleans. These match the mapped types in ExtensionSendOnlyMessageMappings and the handlers in src/pages/embedded/index.ts, so the end‑to‑end flow from config → UI looks sound.

Comment thread public/locales/it-IT.json
Comment on lines +309 to +310
"label": "Default to original audio track",
"title": "Always default to the original audio track"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Italian translation for defaultToOriginalAudioTrack has been replaced by English

The label and title for settings.sections.miscellaneous.features.defaultToOriginalAudioTrack are now English while the rest of the locale is Italian. This looks unintentional and will create an inconsistent UI in it-IT.

Recommend restoring an Italian translation (or reusing the previous one) instead of the English text.

🤖 Prompt for AI Agents
In public/locales/it-IT.json around lines 309-310, the "label" and "title" for
settings.sections.miscellaneous.features.defaultToOriginalAudioTrack are in
English and must be restored to Italian; replace "Default to original audio
track" and "Always default to the original audio track" with appropriate Italian
translations (e.g., "Usa di default la traccia audio originale" for the label
and "Imposta sempre come predefinita la traccia audio originale" for the title)
so the locale is consistent.

Comment on lines +1143 to +1160
<SettingSection title="Playlist management settings">
<SettingTitle />
<Setting
checked={settings.enable_playlist_remove_button?.toString() === "true"}
label={t("settings.sections.miscellaneous.features.enableRemoveVideoButton.label")}
onChange={setCheckboxOption("enable_playlist_remove_button")}
parentSetting={null}
title="Adds a button to remove videos from the playlist"
type="checkbox"
/>
<Setting
checked={settings.enable_playlist_reset_button?.toString() === "true"}
label={t("settings.sections.miscellaneous.features.enableMarkAsUnwatchedButton.label")}
onChange={setCheckboxOption("enable_playlist_reset_button")}
parentSetting={null}
title="Adds a button to mark videos as unwatched"
type="checkbox"
/>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Nov 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

New playlist management settings bypass i18n (hard‑coded English strings)

The new section and its tooltips are not localized:

  • Section title is a raw string: "Playlist management settings".
  • Setting titles are raw English strings even though matching *.title keys already exist in en-US.json.

Every other section uses t(...) for both section title and setting title, so this is inconsistent and will show English text for non‑English locales.

You can align with the existing pattern by wiring titles through i18n, e.g.:

-				<SettingSection title="Playlist management settings">
+				<SettingSection title={t("settings.sections.playlistManagementButtons.title")}>
 					<SettingTitle />
 					<Setting
 						checked={settings.enable_playlist_remove_button?.toString() === "true"}
 						label={t("settings.sections.miscellaneous.features.enableRemoveVideoButton.label")}
-						onChange={setCheckboxOption("enable_playlist_remove_button")}
-						parentSetting={null}
-						title="Adds a button to remove videos from the playlist"
+						onChange={setCheckboxOption("enable_playlist_remove_button")}
+						parentSetting={null}
+						title={t("settings.sections.miscellaneous.features.enableRemoveVideoButton.title")}
 						type="checkbox"
 					/>
 					<Setting
 						checked={settings.enable_playlist_reset_button?.toString() === "true"}
 						label={t("settings.sections.miscellaneous.features.enableMarkAsUnwatchedButton.label")}
-						onChange={setCheckboxOption("enable_playlist_reset_button")}
-						parentSetting={null}
-						title="Adds a button to mark videos as unwatched"
+						onChange={setCheckboxOption("enable_playlist_reset_button")}
+						parentSetting={null}
+						title={t("settings.sections.miscellaneous.features.enableMarkAsUnwatchedButton.title")}
 						type="checkbox"
 					/>
 				</SettingSection>

You’d also need to add settings.sections.playlistManagementButtons.title (or similar) to the locales alongside en‑US.

🤖 Prompt for AI Agents
In src/components/Settings/Settings.tsx around lines 1143-1160 the section title
and Setting title props are hard-coded English strings; replace them with i18n
keys (e.g. t("settings.sections.playlistManagementButtons.title") for the
Section and
t("settings.sections.playlistManagementButtons.enableRemoveVideoButton.title") /
t("settings.sections.playlistManagementButtons.enableMarkAsUnwatchedButton.title")
for each Setting title) and keep labels as they are using t(...); additionally
add corresponding keys and translations to the locale files (e.g. en-US.json and
other locales) so the section and tooltips are localized.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dnicolson strings should be accessed with the i18nInstance.t function. Some strings are hard coded in this recent code.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line #1143 Line #1150 Line #1158

Comment on lines +48 to +51
if (
(!enable_playlist_remove_button && !enable_playlist_reset_button) ||
!document.querySelector("ytd-playlist-video-list-renderer #sort-filter-menu:not(:empty)")
) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don't bail out before the playlist renders

This early return now depends on #sort-filter-menu already having content. On many playlist loads that menu stays empty for a moment, so we exit before registering listeners or observers. Result: the remove/reset buttons never appear unless the menu happened to be populated in time, regressing existing behavior.

Drop this synchronous DOM gate (or defer it until after the observer has a chance to run) so we still observe the playlist and inject the buttons once YouTube finishes rendering.

-	if (
-		(!enable_playlist_remove_button && !enable_playlist_reset_button) ||
-		!document.querySelector("ytd-playlist-video-list-renderer #sort-filter-menu:not(:empty)")
-	) {
+	if (!enable_playlist_remove_button && !enable_playlist_reset_button) {
 		return;
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (
(!enable_playlist_remove_button && !enable_playlist_reset_button) ||
!document.querySelector("ytd-playlist-video-list-renderer #sort-filter-menu:not(:empty)")
) {
if (!enable_playlist_remove_button && !enable_playlist_reset_button) {
return;
}
🤖 Prompt for AI Agents
In src/features/playlistManagementButtons/index.ts around lines 48 to 51, remove
the synchronous check that bails out when "#sort-filter-menu" is empty and
instead always register the playlist observers/listeners; move any querySelector
check for non-empty menu into the MutationObserver/observer callback (or defer
the check with a short timeout) so the code still observes the DOM and injects
the remove/reset buttons once YouTube finishes rendering the menu.

@VampireChicken12 VampireChicken12 changed the base branch from main to dev November 20, 2025 22:22
@VampireChicken12 VampireChicken12 merged commit 047b3cb into YouTube-Enhancer:dev Nov 20, 2025
1 check passed
@github-project-automation github-project-automation Bot moved this from Todo to Done - Released in YouTube Enhancer Nov 20, 2025
VampireChicken12 added a commit that referenced this pull request Dec 19, 2025
Co-authored-by: Nathan <45531575+VampireChicken12@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 1.32.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Done - Released

Development

Successfully merging this pull request may close these issues.

2 participants