Dev#1033
Conversation
…ve format can't be configured with 'Clipboard' save type
Bumps [semantic-release](https://github.com/semantic-release/semantic-release) from 24.2.7 to 24.2.9. - [Release notes](https://github.com/semantic-release/semantic-release/releases) - [Commits](semantic-release/semantic-release@v24.2.7...v24.2.9) --- updated-dependencies: - dependency-name: semantic-release dependency-version: 24.2.9 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [@rollup/rollup-linux-x64-gnu](https://github.com/rollup/rollup) from 4.52.0 to 4.52.3. - [Release notes](https://github.com/rollup/rollup/releases) - [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md) - [Commits](rollup/rollup@v4.52.0...v4.52.3) --- updated-dependencies: - dependency-name: "@rollup/rollup-linux-x64-gnu" dependency-version: 4.52.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [fs-extra](https://github.com/jprichardson/node-fs-extra) from 11.3.0 to 11.3.2. - [Changelog](https://github.com/jprichardson/node-fs-extra/blob/master/CHANGELOG.md) - [Commits](jprichardson/node-fs-extra@11.3.0...11.3.2) --- updated-dependencies: - dependency-name: fs-extra dependency-version: 11.3.2 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 19.1.13 to 19.1.15. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) --- updated-dependencies: - dependency-name: "@types/react" dependency-version: 19.1.15 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [@eslint/compat](https://github.com/eslint/rewrite/tree/HEAD/packages/compat) from 1.3.1 to 1.4.0. - [Release notes](https://github.com/eslint/rewrite/releases) - [Changelog](https://github.com/eslint/rewrite/blob/main/packages/compat/CHANGELOG.md) - [Commits](https://github.com/eslint/rewrite/commits/compat-v1.4.0/packages/compat) --- updated-dependencies: - dependency-name: "@eslint/compat" dependency-version: 1.4.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
…/dev/semantic-release-24.2.9
…/dev/rollup/rollup-linux-x64-gnu-4.52.3
…/dev/fs-extra-11.3.2
…/dev/types/react-19.1.15
…/dev/eslint/compat-1.4.0
Bumps [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js) from 9.36.0 to 9.37.0. - [Release notes](https://github.com/eslint/eslint/releases) - [Commits](https://github.com/eslint/eslint/commits/v9.37.0/packages/js) --- updated-dependencies: - dependency-name: "@eslint/js" dependency-version: 9.37.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [prettier](https://github.com/prettier/prettier) from 3.4.2 to 3.6.2. - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](prettier/prettier@3.4.2...3.6.2) --- updated-dependencies: - dependency-name: prettier dependency-version: 3.6.2 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [dotenv](https://github.com/motdotla/dotenv) from 17.2.1 to 17.2.3. - [Changelog](https://github.com/motdotla/dotenv/blob/master/CHANGELOG.md) - [Commits](motdotla/dotenv@v17.2.1...v17.2.3) --- updated-dependencies: - dependency-name: dotenv dependency-version: 17.2.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) from 8.42.0 to 8.45.0. - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.45.0/packages/typescript-eslint) --- updated-dependencies: - dependency-name: typescript-eslint dependency-version: 8.45.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [monaco-editor](https://github.com/microsoft/monaco-editor) from 0.52.2 to 0.53.0. - [Release notes](https://github.com/microsoft/monaco-editor/releases) - [Changelog](https://github.com/microsoft/monaco-editor/blob/main/CHANGELOG.md) - [Commits](microsoft/monaco-editor@v0.52.2...v0.53.0) --- updated-dependencies: - dependency-name: monaco-editor dependency-version: 0.53.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
…n/dev/zod-error-2.0.0
Bumps [zod](https://github.com/colinhacks/zod) from 4.1.11 to 4.1.12. - [Release notes](https://github.com/colinhacks/zod/releases) - [Commits](colinhacks/zod@v4.1.11...v4.1.12) --- updated-dependencies: - dependency-name: zod dependency-version: 4.1.12 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com>
…n/dev/zod-4.1.12
…n/dev/semantic-release-25.0.1
|
Caution Review failedFailed to post review comments WalkthroughThis PR introduces multiple new YouTube enhancement features (playlist management buttons, save-to-watch-later, automatically enable/maximize), replaces YouTube Data API with Innertube for playlist data, refactors maximize-player functionality, updates localization across 25 languages, upgrades dependencies and GitHub Actions workflows, and adds UI support for disabling conflicting options via enhanced Checkbox component. Changes
Sequence DiagramsequenceDiagram
participant User
participant ContentPage as Content Page<br/>(index.ts)
participant Feature as Feature Module
participant Storage as Storage
participant YouTube as YouTube DOM
autonumber
User->>Storage: Enable Feature (e.g., playlist_mgmt)
activate Storage
Storage-->>ContentPage: Storage Change Event
deactivate Storage
ContentPage->>ContentPage: Detect enable_playlist_management_buttons
ContentPage->>ContentPage: Build featuresChange message
ContentPage->>Feature: Send feature enable request
activate Feature
Feature->>Feature: Wait for options message
Feature->>YouTube: Scan video items (yt-lockup-view-model)
YouTube-->>Feature: Video items found
Feature->>Feature: Initialize Innertube client
Feature->>Feature: Inject remove/mark-unwatched buttons
User->>YouTube: Click remove button
YouTube->>Feature: Button click event
activate Feature
Feature->>Feature: Show spinner
Feature->>Feature: Call Innertube removeFromPlaylist()
Feature-->>YouTube: Remove item from DOM on success
Feature-->>YouTube: Show error tooltip on failure
deactivate Feature
deactivate Feature
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Multiple interrelated feature additions (playlist management, watch-later, auto-captions, auto-maximize), significant refactoring of maximize-player and playlist-length with API migration, 25 localization file updates, enhanced UI component logic for option conflicts, and dependencies upgrade require careful verification of control flow, type safety, Innertube API integration, and consistency across new UI surfaces. Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 41
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (6)
src/features/automaticallyDisableAmbientMode/index.ts (1)
81-81: Fragile attribute check.Checking
checked === ""is fragile and non-idiomatic. UsehasAttribute('checked')instead for more robust attribute presence detection.-const ambientModeEnabled = ambientModeItemToggle.getAttribute("checked") === ""; +const ambientModeEnabled = ambientModeItemToggle.hasAttribute("checked");public/locales/sv-SE.json (1)
2-2: Fix incorrect langCode value.The
langCodeis set to"en-US"but this is the Swedish (sv-SE) locale file. This mismatch could cause language detection or routing issues in the extension.Apply this fix:
- "langCode": "en-US", + "langCode": "sv-SE",src/utils/constants.ts (1)
241-241: Remove unused hardcoded YouTube Data API keyThe constant
YouTube_Enhancer_Public_Youtube_Data_API_V3_Keyat line 241 is exported but never imported or used anywhere in the codebase. While the active implementation correctly uses a user-configurable setting (src/components/Settings/Settings.tsx, lines 1234–1238), this hardcoded constant should be removed to eliminate unnecessary exposure of sensitive credentials in source code.public/locales/vi-VN.json (1)
2-3: Locale metadata is incorrect.
langCodeandlangNameshould reflect Vietnamese, not en-US.- "langCode": "en-US", - "langName": "English (US)", + "langCode": "vi-VN", + "langName": "Tiếng Việt (Việt Nam)",public/locales/he-IL.json (1)
292-315: All new feature strings are in English instead of Hebrew.Lines 292-315 contain entirely English text for the new automatic caption, maximize player, playlist management, and watch later features. None of these have been translated to Hebrew.
All labels and titles in this section need Hebrew translations to provide proper localization for Hebrew-speaking users. This includes:
- automaticallyEnableClosedCaptions (lines 292-295)
- automaticallyMaximizePlayer (lines 296-299)
- enablePlaylistManagementButtons (lines 308-311)
- enableSaveToWatchLaterButton (lines 312-315)
src/features/playlistLength/utils.ts (1)
349-353: URL parsing bug for videoId extraction.videoIdElement.href is already absolute on YouTube. Prepending https://youtube.com can yield an invalid URL.
- const url = new URL(`https://youtube.com${videoIdElement.href}`); + const url = new URL(videoIdElement.href, window.location.origin);
🧹 Nitpick comments (36)
src/deepDarkMaterialCSS.ts (6)
459-463: Prefer background-color over background.Avoid wiping potential background images/gradients on #frosted-glass.
Apply:
-#frosted-glass.with-chipbar.ytd-app { - background: var(--main-background) !important; -} +#frosted-glass.with-chipbar.ytd-app { + background-color: var(--main-background) !important; +}
482-491: Poll bar contrast: verify accessibility.Selected at 0.9 and others at 0.2 over dark surfaces may fall below 3:1. Consider 0.3–0.4 for non-selected or a tokenized alpha.
535-539: Tabs: added modern class variants and slider height.Looks correct. Optional: extract the slider height to a CSS var for easier tuning.
- .yt-tab-group-shape-wiz__slider, - .yt-tab-shape__tab-bar--tab-bar-selected { - background-color: var(--main-color) !important; - height: 2px !important; - } + :root { --dd-tab-indicator-height: 2px; } + .yt-tab-group-shape-wiz__slider, + .yt-tab-shape__tab-bar--tab-bar-selected { + background-color: var(--main-color) !important; + height: var(--dd-tab-indicator-height) !important; + }Also applies to: 543-546, 553-557
922-927: Use :first-child for clarity (optional).Semantically equivalent here; :first-child is a tad clearer than :nth-child(1).
-.yt-content-metadata-view-model__metadata-row:nth-child(1)>.yt-core-attributed-string { +.yt-content-metadata-view-model__metadata-row:first-child > .yt-core-attributed-string {
971-976: Background shorthand here can be narrowed (optional).To avoid unintended resets, prefer background-color when only color is set.
-#description:not([class*="yt-music"]):not([class*="ytd-channel-video-player-renderer"]), +#description:not([class*="yt-music"]):not([class*="ytd-channel-video-player-renderer"]), #bio, #right-column>yt-formatted-string { - background: var(--second-background) !important; + background-color: var(--second-background) !important; color: var(--dimmer-text) !important; }
2456-2460: SVG icon coloring: verify currentColor usage.Setting color on the SVG assumes icons inherit currentColor. If they use fills, this may no-op; consider targeting the shape element or ensuring currentColor is respected.
src/features/automaticallyDisableAmbientMode/index.ts (1)
51-51: Consider using a structured logging utility.The
console.logstatements for error conditions should use a centralized logging utility for consistency and proper log level management (e.g.,console.erroror a custom logger).Example:
// Instead of: return console.log("Menu button not found"); // Consider: console.error("Menu button not found"); // or use a custom logger if available: logger.error("Menu button not found");Also applies to: 64-64, 72-72, 78-78
public/locales/zh-CN.json (3)
138-140: Minor punctuation consistencyConsider ending both messages with a full-width period "。" to match surrounding CN style.
308-315: Playlist/watch‑later toggles: terminology consistencyYou use “移除” in failure messages below but “删除” here. Prefer one term (建议统一为“移除”).
503-510: Playlist management strings: LGTM, with tiny nit“正在移除视频…” vs “删除视频” above—align verbs for UX consistency.
public/locales/fr-FR.json (6)
105-105: Localize newly added error textCurrently English. Propose: "Cette option ne peut pas être activée lorsque « {{OPTION}} » est activée."
138-140: Localize specificOption helper textsThese are English; translate to maintain FR UX consistency.
292-299: Localize new auto featuresBoth keys are English; please provide FR translations.
308-315: Localize new playlist/save buttonsAdd FR translations for labels/titles.
503-510: Localize playlist management actionsThese remain English; translate for consistency.
511-514: Localize Watch Later textsTranslate “Failed to add…” and “Save to Watch Later”.
public/locales/es-ES.json (6)
105-105: Localize newly added error textCurrently English. Proposal: "Esta opción no se puede habilitar mientras '{{OPTION}}' esté habilitada."
138-140: Localize specificOption helper textsTranslate to ES for consistency.
292-299: Localize new auto featuresProvide ES translations for these two new toggles.
308-315: Localize new playlist/save buttonsBoth label/title strings are English; translate.
503-510: Localize playlist management actionsTranslate these action/status strings.
511-514: Localize Watch Later textsTranslate error and CTA.
src/features/playlistLength/index.ts (1)
39-47: Throttle resize re-initialization.Recreating playlist-length UI on every resize can be chatty. Debounce the callback or coalesce via
requestAnimationFrameto avoid repeated reflows.- resizeObserver = new ResizeObserver(async () => { - documentObserver = await initPlaylistLength({ pageType, playlistLengthGetMethod, playlistWatchTimeGetMethod }); - }); + let raf = 0; + resizeObserver = new ResizeObserver(() => { + cancelAnimationFrame(raf); + raf = requestAnimationFrame(async () => { + documentObserver = await initPlaylistLength({ pageType, playlistLengthGetMethod, playlistWatchTimeGetMethod }); + }); + });src/features/playlistManagementButtons/index.ts (3)
51-55: Ensure authenticated requests carry credentials.
fetchdefaults may omit cookies for cross-origin. Force credentials to include for Innertube.- const youtube = await Innertube.create({ - cookie: document.cookie, - fetch: (...args) => fetch(...args) - }); + const youtube = await Innertube.create({ + cookie: document.cookie, + fetch: (input, init) => fetch(input as RequestInfo, { credentials: "include", ...init }) + });
83-90: Tooltip id collision risk.Both buttons use the same tooltip id; showing both could clash. Add uniqueness (e.g., include video id + action).
- id: "yte-feature-playlistManagementButtons-tooltip", + id: `yte-feature-playlistManagementButtons-tooltip-${setVideoId}-remove`,and
- id: "yte-feature-playlistManagementButtons-tooltip", + id: `yte-feature-playlistManagementButtons-tooltip-${setVideoId}-reset`,Also applies to: 114-121
28-38: Detach listeners on disable to prevent leaks.
DOMContentLoadedlistener isn’t removed indisablePlaylistManagementButtons. Store the handler and remove it on disable.-let playlistObserver: MutationObserver | null = null; +let playlistObserver: MutationObserver | null = null; +let domReadyListener: ((this: Window, ev: Event) => any) | null = null; @@ export async function disablePlaylistManagementButtons() { if (playlistObserver) { playlistObserver.disconnect(); playlistObserver = null; } + if (domReadyListener) { + window.removeEventListener("DOMContentLoaded", domReadyListener); + domReadyListener = null; + } @@ - window.addEventListener("DOMContentLoaded", observePlaylist); + domReadyListener = observePlaylist; + window.addEventListener("DOMContentLoaded", domReadyListener);Also applies to: 149-153
src/features/screenshotButton/index.ts (1)
43-52: Consider using console.error for error logging.The error handling is well-structured, but line 51 uses
console.log(err)instead ofconsole.error(err), which would be more semantically appropriate for error logging.Apply this diff:
} catch (err) { remove(); - console.log(err); + console.error(err); }src/components/Inputs/CheckBox/CheckBox.tsx (1)
39-43: Consider accessibility for the disabled reason text.The disabled reason text is displayed but may not be programmatically associated with the checkbox for screen readers. Consider adding
aria-describedbyto link the checkbox with its disabled reason.<input checked={checked} className={cn("form-checkbox mt-0.5 size-3.5 text-indigo-600 transition duration-150 ease-in-out", { "!cursor-not-allowed": disabled })} disabled={disabled} + aria-describedby={disabled && disabledReason ? `${id}-reason` : undefined} id={id} onChange={onChange} type="checkbox" /> <div className={cn("min-w-0 max-w-[400px]", { "ml-2": direction === "ltr", "mr-2": direction === "rtl" })} > <label className="block text-sm text-black dark:text-white" htmlFor={id}> {label} </label> {disabled && disabledReason && ( - <span className="block cursor-default whitespace-normal break-words text-xs leading-tight text-gray-500 dark:text-gray-300"> + <span id={`${id}-reason`} className="block cursor-default whitespace-normal break-words text-xs leading-tight text-gray-500 dark:text-gray-300"> {disabledReason} </span> )} </div>src/features/saveToWatchLaterButton/index.ts (4)
58-66: Replace innerHTML swaps with safer DOM updates.Even if sources are controlled, repeated innerHTML assignments are unnecessary and flagged. Create SVG nodes and swap via replaceChild/append, or use a Trusted Types policy explicitly.
- saveButton.innerHTML = renderToString(React.createElement(AiOutlineVideoCameraAdd, { color: "white", size: 18 })); + const icon = document.createElementNS("http://www.w3.org/2000/svg", "svg"); + icon.setAttribute("width", "18"); icon.setAttribute("height", "18"); icon.setAttribute("viewBox", "0 0 24 24"); + const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); + path.setAttribute("fill", "white"); + path.setAttribute("d", "M21 7v10l-5-3-3 2V8l3 2 5-3z"); // camera-add path (pick the exact d you want) + icon.appendChild(path); + saveButton.replaceChildren(icon); ... - const { innerHTML: originalHTML } = saveButton; + const originalChild = saveButton.firstChild; ... - saveButton.innerHTML = renderToString(React.createElement(FaSpinner, { color: "gray", size: 18 })); + const spinner = document.createElementNS("http://www.w3.org/2000/svg", "svg"); + spinner.setAttribute("width", "18"); spinner.setAttribute("height", "18"); spinner.setAttribute("viewBox", "0 0 24 24"); + spinner.classList.add("yte-spinning"); + saveButton.replaceChildren(spinner); ... - saveButton.innerHTML = originalHTML; + if (originalChild) saveButton.replaceChildren(originalChild);Alternatively, if you must use HTML strings, create and use a named Trusted Types policy and assign TrustedHTML, not raw strings.
Also applies to: 80-83
27-36: Detach listeners/observers on disable().enableSaveToWatchLaterButton adds a DOMContentLoaded listener but disableSaveToWatchLaterButton doesn’t remove it. Store the handler and remove it in disable() to avoid leaks when toggling.
+let onDOMContentLoaded: (() => void) | null = null; ... - window.addEventListener("DOMContentLoaded", observePlaylist); + onDOMContentLoaded = observePlaylist; + window.addEventListener("DOMContentLoaded", onDOMContentLoaded); ... export async function disableSaveToWatchLaterButton() { if (videosObserver) { videosObserver.disconnect(); videosObserver = null; } + if (onDOMContentLoaded) { + window.removeEventListener("DOMContentLoaded", onDOMContentLoaded); + onDOMContentLoaded = null; + } document.querySelectorAll(".yte-save-to-watch-later-button").forEach((saveButton) => { removeSaveToWatchLaterButton(saveButton as HTMLElement); }); }Also applies to: 109-113
60-63: Prefer dataset.title for tooltip consistency and add aria-label.createTooltip reads element.dataset.title. Align by setting both.
- saveButton.title = window.i18nextInstance?.t("settings.sections.saveToWatchLaterButton.save"); + const label = window.i18nextInstance?.t("settings.sections.saveToWatchLaterButton.save") ?? "Save to Watch Later"; + saveButton.title = label; + saveButton.dataset.title = label; + saveButton.setAttribute("aria-label", label);
55-57: Defensive access to rawProps on yt-lockup-view-model.rawProps is not guaranteed. Add a guard to avoid exceptions on unexpected nodes.
- const { contentId: videoId } = (video as YTLockupViewModel).rawProps.data(); + const props = (video as Partial<YTLockupViewModel>)?.rawProps?.data?.(); + const videoId = props?.contentId; + if (!videoId) return;src/features/maximizePlayerButton/utils.ts (2)
26-29: Make theater-mode detection resilient; avoid brittle path-d string.Matching the exact SVG path d is fragile. Prefer DOM state (e.g., ytd-watch-flexy[theater]) or computed attribute/class.
- const inTheaterMode = document.querySelector<HTMLButtonElement>(`button.ytp-size-button svg path[d='${theaterModeButtonPathD}']`) === null; + const inTheaterMode = + document.querySelector("ytd-watch-flexy[theater]") !== null || + document.body.hasAttribute("theater"); // fallback if YouTube sets a body attrOptionally keep the path check as a last resort if both queries fail.
49-51: Avoid hard dependency on i18next presence.window.i18nextInstance may not be ready; add optional chaining/fallback to prevent crashes.
- window.i18nextInstance.t("pages.content.features.maximizePlayerButton.button.toggle.off") + window.i18nextInstance?.t("pages.content.features.maximizePlayerButton.button.toggle.off") ?? "Disable maximize"src/features/playlistLength/utils.ts (1)
1-4: Remove brittle internal type import from dist/src; use duck typing instead.youtubei.js does not expose a public PlaylistVideo type, making the import from
"youtubei.js/dist/src/parser/classes/PlaylistVideo"fragile across versions.Replace the import and both casts (lines 312 and 321) with duck typing:
-import type PlaylistVideo from "youtubei.js/dist/src/parser/classes/PlaylistVideo"; - import { Innertube } from "youtubei.js/web";Then in both loops:
-const playlistVideo = video as PlaylistVideo; -if (playlistVideo?.duration?.seconds) { - totalSeconds += playlistVideo.duration.seconds; +const seconds = (video as any)?.duration?.seconds; +if (typeof seconds === "number") { + totalSeconds += seconds;src/pages/embedded/index.ts (1)
586-586: Unnecessaryawaiton synchronous function.The function
isEndScreenCardsHidden()returnsboolean(notPromise<boolean>), so theawaitkeyword is unnecessary here. While this won't cause an error (the value will simply be immediately resolved), it's cleaner to remove it.Apply this diff:
-const endScreenCardsHidden = await isEndScreenCardsHidden(); +const endScreenCardsHidden = isEndScreenCardsHidden();
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (4)
assets/translation context screenshots/Miscellaneous Settings/Automatically_Enable_Closed_Captions.pngis excluded by!**/*.pngassets/translation context screenshots/Option Disabled/Deep_Dark_Custom_Theme.pngis excluded by!**/*.pngassets/translation context screenshots/Option Disabled/Set_Screenshot_Save_Type_To_FILE_To_Configure_This_Option.pngis excluded by!**/*.pngpackage-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (66)
.all-contributorsrc(1 hunks).github/workflows/release.yml(2 hunks)README.md(1 hunks)package.json(1 hunks)public/contentStyle.css(1 hunks)public/locales/ca-ES.json(5 hunks)public/locales/cs-CZ.json(5 hunks)public/locales/de-DE.json(5 hunks)public/locales/en-GB.json(5 hunks)public/locales/en-US.json(5 hunks)public/locales/en-US.json.d.ts(5 hunks)public/locales/es-ES.json(5 hunks)public/locales/fa-IR.json(5 hunks)public/locales/fr-FR.json(5 hunks)public/locales/he-IL.json(5 hunks)public/locales/hi-IN.json(5 hunks)public/locales/it-IT.json(5 hunks)public/locales/ja-JP.json(5 hunks)public/locales/ko-KR.json(5 hunks)public/locales/nl-NL.json(5 hunks)public/locales/pl-PL.json(14 hunks)public/locales/pt-BR.json(5 hunks)public/locales/ru-RU.json(7 hunks)public/locales/sv-SE.json(5 hunks)public/locales/tr-TR.json(7 hunks)public/locales/uk-UA.json(5 hunks)public/locales/vi-VN.json(5 hunks)public/locales/zh-CN.json(5 hunks)public/locales/zh-TW.json(5 hunks)src/components/Inputs/CheckBox/CheckBox.tsx(1 hunks)src/components/Settings/Settings.tsx(8 hunks)src/components/Settings/components/Setting.tsx(1 hunks)src/deepDarkMaterialCSS.ts(16 hunks)src/features/automaticallyDisableAmbientMode/index.ts(2 hunks)src/features/automaticallyEnableClosedCaptions/index.ts(1 hunks)src/features/automaticallyMaximizePlayer/index.ts(1 hunks)src/features/buttonPlacement/index.css(1 hunks)src/features/buttonPlacement/index.ts(1 hunks)src/features/buttonPlacement/utils.ts(1 hunks)src/features/featureMenu/index.ts(3 hunks)src/features/featureMenu/utils.ts(2 hunks)src/features/hideEndScreenCards/index.ts(3 hunks)src/features/hidePlayables/index.css(1 hunks)src/features/hideShorts/index.css(1 hunks)src/features/hideShorts/utils.ts(1 hunks)src/features/maximizePlayerButton/index.css(1 hunks)src/features/maximizePlayerButton/index.ts(2 hunks)src/features/maximizePlayerButton/utils.ts(1 hunks)src/features/playlistLength/index.ts(1 hunks)src/features/playlistLength/utils.ts(4 hunks)src/features/playlistManagementButtons/index.css(1 hunks)src/features/playlistManagementButtons/index.ts(1 hunks)src/features/restoreFullscreenScrolling/index.css(1 hunks)src/features/saveToWatchLaterButton/index.css(1 hunks)src/features/saveToWatchLaterButton/index.ts(1 hunks)src/features/screenshotButton/index.ts(2 hunks)src/global.d.ts(1 hunks)src/i18n/constants.ts(1 hunks)src/pages/content/index.ts(3 hunks)src/pages/embedded/index.ts(12 hunks)src/pages/options/index.css(1 hunks)src/pages/popup/index.css(1 hunks)src/types/index.ts(4 hunks)src/utils/EventManager.ts(1 hunks)src/utils/constants.ts(4 hunks)src/utils/utilities.ts(7 hunks)
🧰 Additional context used
🧬 Code graph analysis (14)
src/features/automaticallyMaximizePlayer/index.ts (2)
src/features/maximizePlayerButton/utils.ts (2)
minimizePlayer(60-66)maximizePlayer(14-58)src/utils/utilities.ts (1)
waitForSpecificMessage(775-802)
src/components/Inputs/CheckBox/CheckBox.tsx (2)
src/components/Settings/Settings.tsx (1)
useSettings(1564-1570)src/utils/utilities.ts (1)
cn(178-180)
src/features/saveToWatchLaterButton/index.ts (1)
src/utils/utilities.ts (4)
waitForSpecificMessage(775-802)isHomePage(521-524)isSubscriptionsPage(547-550)createTooltip(231-320)
src/features/maximizePlayerButton/index.ts (4)
src/features/buttonPlacement/index.ts (1)
addFeatureButton(11-35)src/features/buttonPlacement/utils.ts (2)
getFeatureButton(35-37)updateFeatureButtonTitle(176-180)src/utils/utilities.ts (1)
createTooltip(231-320)src/features/maximizePlayerButton/utils.ts (2)
maximizePlayer(14-58)minimizePlayer(60-66)
src/features/playlistManagementButtons/index.ts (2)
src/utils/utilities.ts (3)
waitForSpecificMessage(775-802)isPlaylistPage(539-542)createTooltip(231-320)src/features/playlistLength/utils.ts (1)
getPlaylistId(178-181)
src/components/Settings/Settings.tsx (1)
src/components/Settings/components/Setting.tsx (2)
parentSetting(15-27)Setting(42-74)
src/features/hideEndScreenCards/index.ts (5)
src/utils/utilities.ts (1)
modifyElementsClassList(560-562)src/types/index.ts (1)
ButtonPlacement(125-125)src/icons.ts (1)
ToggleIcon(5-5)src/features/featureMenu/utils.ts (1)
getFeatureMenuItem(120-123)src/features/buttonPlacement/utils.ts (3)
getFeatureButton(35-37)updateFeatureButtonIcon(171-175)updateFeatureButtonTitle(176-180)
src/features/screenshotButton/index.ts (1)
src/types/index.ts (1)
Nullable(29-29)
src/pages/content/index.ts (1)
src/utils/utilities.ts (1)
sendExtensionOnlyMessage(686-700)
src/features/maximizePlayerButton/utils.ts (5)
src/utils/EventManager.ts (1)
eventManager(63-167)src/features/buttonPlacement/utils.ts (4)
getFeatureButton(35-37)updateFeatureButtonIcon(171-175)modifyIconForLightTheme(119-134)updateFeatureButtonTitle(176-180)src/icons.ts (1)
getFeatureIcon(359-361)src/utils/utilities.ts (2)
ModifyElementAction(135-135)modifyElementsClassList(560-562)src/types/index.ts (1)
YouTubePlayerDiv(533-533)
src/features/featureMenu/index.ts (1)
src/utils/utilities.ts (2)
isWatchPage(551-554)createSVGElement(211-229)
src/features/automaticallyEnableClosedCaptions/index.ts (2)
src/utils/utilities.ts (4)
isWatchPage(551-554)isLivePage(525-528)waitForElement(749-766)waitForSpecificMessage(775-802)src/types/index.ts (1)
YouTubePlayerDiv(533-533)
src/features/playlistLength/utils.ts (1)
src/types/index.ts (1)
playlistLengthGetMethod(128-128)
src/pages/embedded/index.ts (7)
src/features/saveToWatchLaterButton/index.ts (2)
enableSaveToWatchLaterButton(38-113)disableSaveToWatchLaterButton(27-36)src/features/automaticallyEnableClosedCaptions/index.ts (2)
enableAutomaticallyEnableClosedCaptions(16-34)disableAutomaticallyEnableClosedCaptions(5-15)src/features/playlistManagementButtons/index.ts (2)
enablePlaylistManagementButtons(40-153)disablePlaylistManagementButtons(28-38)src/features/automaticallyMaximizePlayer/index.ts (2)
enableAutomaticallyMaximizePlayer(7-15)disableAutomaticallyMaximizePlayer(4-6)src/features/hideEndScreenCards/index.ts (4)
addHideEndScreenCardsButton(29-72)isEndScreenCardsHidden(78-81)enableHideEndScreenCards(18-28)updateHideEndScreenCardsButtonState(112-127)src/icons.ts (1)
getFeatureIcon(359-361)src/features/maximizePlayerButton/utils.ts (1)
minimizePlayer(60-66)
🪛 ast-grep (0.39.6)
src/features/saveToWatchLaterButton/index.ts
[warning] 58-58: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: saveButton.innerHTML = renderToString(React.createElement(AiOutlineVideoCameraAdd, { color: "white", size: 18 }))
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 64-64: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: saveButton.innerHTML = renderToString(React.createElement(FaSpinner, { color: "gray", size: 18 }))
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 80-80: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: saveButton.innerHTML = originalHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 58-58: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: saveButton.innerHTML = renderToString(React.createElement(AiOutlineVideoCameraAdd, { color: "white", size: 18 }))
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 64-64: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: saveButton.innerHTML = renderToString(React.createElement(FaSpinner, { color: "gray", size: 18 }))
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 80-80: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: saveButton.innerHTML = originalHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
src/features/playlistManagementButtons/index.ts
[warning] 68-68: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: removeButton.innerHTML = renderToString(React.createElement(FaTrash, { color: "red", size: 18 }))
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 75-75: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: removeButton.innerHTML = renderToString(React.createElement(FaSpinner, { color: "gray", size: 18 }))
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 91-91: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: removeButton.innerHTML = originalHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 98-98: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: resetButton.innerHTML = renderToString(React.createElement(FaUndoAlt, { color: "red", size: 18 }))
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 105-105: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: resetButton.innerHTML = renderToString(React.createElement(FaSpinner, { color: "gray", size: 18 }))
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 122-122: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: resetButton.innerHTML = originalHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 68-68: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: removeButton.innerHTML = renderToString(React.createElement(FaTrash, { color: "red", size: 18 }))
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 75-75: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: removeButton.innerHTML = renderToString(React.createElement(FaSpinner, { color: "gray", size: 18 }))
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 91-91: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: removeButton.innerHTML = originalHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 98-98: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: resetButton.innerHTML = renderToString(React.createElement(FaUndoAlt, { color: "red", size: 18 }))
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 105-105: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: resetButton.innerHTML = renderToString(React.createElement(FaSpinner, { color: "gray", size: 18 }))
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 122-122: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: resetButton.innerHTML = originalHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
🪛 Biome (2.1.2)
public/contentStyle.css
[error] 19-19: Unexpected shorthand property font after line-height
(lint/suspicious/noShorthandPropertyOverrides)
| .ytp-big-mode .yte-button-tooltip { | ||
| line-height: 22px !important; | ||
| padding: 8px 9px !important; | ||
| transform: translate(-50%, -75%) !important; | ||
| font: 20px !important; | ||
| } |
There was a problem hiding this comment.
Don’t use font shorthand after line-height; it resets line-height.
font: 20px !important; overrides and resets earlier line-height. Replace with font-size only.
.ytp-big-mode .yte-button-tooltip {
- line-height: 22px !important;
- font: 20px !important;
+ line-height: 22px !important;
+ font-size: 20px !important;
}Based on static analysis hints.
📝 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.
| .ytp-big-mode .yte-button-tooltip { | |
| line-height: 22px !important; | |
| padding: 8px 9px !important; | |
| transform: translate(-50%, -75%) !important; | |
| font: 20px !important; | |
| } | |
| .ytp-big-mode .yte-button-tooltip { | |
| line-height: 22px !important; | |
| font-size: 20px !important; | |
| } |
🧰 Tools
🪛 Biome (2.1.2)
[error] 19-19: Unexpected shorthand property font after line-height
(lint/suspicious/noShorthandPropertyOverrides)
🤖 Prompt for AI Agents
In public/contentStyle.css around lines 17 to 20, the rule uses the font
shorthand which resets the previously set line-height; replace "font: 20px
!important;" with an explicit "font-size: 20px !important;" (remove the
shorthand) so the line-height remains intact and the size is applied.
| "options": { | ||
| "notifications": { | ||
| "error": { | ||
| "optionConflict": "This option cannot be enabled while '{{OPTION}}' is enabled.", |
There was a problem hiding this comment.
🧩 Analysis chain
Verify German translations for new strings.
Several new localization entries appear to be in English rather than German:
- Line 105: "This option cannot be enabled while '{{OPTION}}' is enabled."
- Lines 292-315: Feature labels and titles
- Lines 503-514: Playlist management and watch later button strings
Ensure these are properly translated to German before release.
Also applies to: 138-139, 292-315, 503-514
🌐 Web query:
Are the following strings correctly translated to German in the de-DE locale file, or are they English placeholders that need translation?
💡 Result:
I can check — please paste the strings (or the de-DE locale file).
If you want to self-check first, quick checklist and methods:
- If entries are in a PO file: untranslated entries usually have an empty msgstr ("msgstr """). See GNU gettext docs. [1]
- Compare de-DE to the source (en) file: identical text usually means untranslated; use diff/merge tools (git diff, diff, or a translation tool). [2][3]
- Look for common placeholder markers: "TODO", "TBD", "EN:", "///", developer comments, or English phrases left in translations — those indicate placeholders.
- For JSON/JS locales: search for strings that are plain English or equal to the en locale value (grep/diff).
- Use localization tooling (Transifex/Smartling/gettext helpers) to list untranslated strings or export only untranslated entries. [2][4]
If you paste the strings (or a file snippet), I’ll mark which are already German and which appear to be English placeholders.
Sources: [1] GNU gettext manual — Untranslated entries. [2] Transifex Gettext guide (download untranslated). [3] gettext_ops example (list untranslated). [4] Smartling troubleshooting untranslated content.
I need to examine the locale files to verify which strings are untranslated. Let me retrieve the de-DE.json file and compare it with the English source.
🏁 Script executed:
#!/bin/bash
# Find the locale files
echo "=== Locating locale files ==="
fd -e json -path "*/locales/*" | head -20
echo -e "\n=== Checking de-DE.json content at specified lines ==="
# Extract lines 105, 138-139, 292-315, 503-514 from de-DE.json
if [ -f "public/locales/de-DE.json" ]; then
echo "Lines 105:"
sed -n '105p' public/locales/de-DE.json
echo -e "\nLines 138-139:"
sed -n '138,139p' public/locales/de-DE.json
echo -e "\nLines 292-315:"
sed -n '292,315p' public/locales/de-DE.json
echo -e "\nLines 503-514:"
sed -n '503,514p' public/locales/de-DE.json
fiLength of output: 2144
🏁 Script executed:
#!/bin/bash
# Check if English locale file exists and compare
echo "=== Looking for English locale file ==="
ls -la public/locales/ 2>/dev/null || echo "Directory not found"
echo -e "\n=== Extract full de-DE.json for context ==="
if [ -f "public/locales/de-DE.json" ]; then
head -150 public/locales/de-DE.json | tail -50
fiLength of output: 3535
🏁 Script executed:
#!/bin/bash
# Read both files to compare translations
echo "=== Comparing en and de-DE locales at line 105 ==="
if [ -f "public/locales/de-DE.json" ] && [ -f "public/locales/en-US.json" ]; then
echo "English (line 105):"
sed -n '105p' public/locales/en-US.json
echo "German (line 105):"
sed -n '105p' public/locales/de-DE.json
fi
echo -e "\n=== Full comparison of line 105 across all locale files ==="
for file in public/locales/*.json; do
echo "File: $(basename "$file")"
sed -n '105p' "$file" 2>/dev/null | head -1
doneLength of output: 2686
Let me do a targeted comparison of the feature sections to confirm the translation status:
🏁 Script executed:
#!/bin/bash
# Use jq to parse and compare translations more accurately
echo "=== Checking key sections in en-US vs de-DE ==="
# Check a sample key from lines 292-315 range
echo "Sample: 'automaticallyEnableClosedCaptions' label"
echo "English:"
jq '.settings.sections.automaticQuality.enable // .settings.sections.automaticFeatures // empty' public/locales/en-US.json 2>/dev/null | head -5 || echo "Structure may differ"
echo "German:"
jq '.settings.sections.automaticQuality.enable // .settings.sections.automaticFeatures // empty' public/locales/de-DE.json 2>/dev/null | head -5 || echo "Structure may differ"
# Compare optionDisabled section
echo -e "\n=== optionDisabled section (lines 138-139 area) ==="
echo "English:"
jq '.settings.optionDisabled' public/locales/en-US.json 2>/dev/null
echo "German:"
jq '.settings.optionDisabled' public/locales/de-DE.json 2>/dev/nullLength of output: 1456
🏁 Script executed:
#!/bin/bash
# Direct line-by-line comparison for the problem areas
echo "=== Detailed line comparison for reported sections ==="
echo "Lines 138-139 comparison:"
echo "en-US:"
sed -n '138,139p' public/locales/en-US.json
echo "de-DE:"
sed -n '138,139p' public/locales/de-DE.json
echo -e "\n=== Checking if 138-139 are identical (indicator of untranslated) ==="
if diff <(sed -n '138,139p' public/locales/en-US.json) <(sed -n '138,139p' public/locales/de-DE.json) > /dev/null; then
echo "Lines are IDENTICAL - untranslated (English text in German file)"
else
echo "Lines DIFFER - translation exists"
fi
echo -e "\n=== Checking if 292-315 sections have translations ==="
echo "Sampling line 295:"
echo "en-US: $(sed -n '295p' public/locales/en-US.json)"
echo "de-DE: $(sed -n '295p' public/locales/de-DE.json)"
if diff <(sed -n '295p' public/locales/en-US.json) <(sed -n '295p' public/locales/de-DE.json) > /dev/null; then
echo "→ IDENTICAL (untranslated)"
else
echo "→ DIFFERENT (translation may exist)"
fiLength of output: 930
Let me verify the exact structure and translation status for the feature sections:
🏁 Script executed:
#!/bin/bash
# Parse JSON to check specific keys mentioned in review comment
echo "=== Checking feature sections for translations ==="
# Check automaticallyEnableClosedCaptions
echo "automaticallyEnableClosedCaptions:"
echo "English:"
jq '.settings.sections.automaticallyEnableClosedCaptions' public/locales/en-US.json 2>/dev/null
echo "German:"
jq '.settings.sections.automaticallyEnableClosedCaptions' public/locales/de-DE.json 2>/dev/null
echo -e "\n=== enablePlaylistManagementButtons ==="
echo "English:"
jq '.settings.sections.enablePlaylistManagementButtons' public/locales/en-US.json 2>/dev/null
echo "German:"
jq '.settings.sections.enablePlaylistManagementButtons' public/locales/de-DE.json 2>/dev/nullLength of output: 180
🏁 Script executed:
#!/bin/bash
# Check the watch later section
echo "=== Checking saveToWatchLaterButton ==="
echo "English:"
jq '.notifications.playlistManagementButtons, .notifications.saveToWatchLaterButton' public/locales/en-US.json 2>/dev/null
echo -e "\nGerman:"
jq '.notifications.playlistManagementButtons, .notifications.saveToWatchLaterButton' public/locales/de-DE.json 2>/dev/null
# Also check if these exist at different paths
echo -e "\n=== Alternative path search ==="
jq 'keys' public/locales/de-DE.json 2>/dev/null | head -20Length of output: 180
Let me search for the exact location of these feature strings in the JSON structure:
🏁 Script executed:
#!/bin/bash
# Search for the feature keys in the entire JSON
echo "=== Searching for automaticallyEnableClosedCaptions in de-DE.json ==="
jq '.. | objects | select(has("automaticallyEnableClosedCaptions")) | keys' public/locales/de-DE.json 2>/dev/null | head -20
echo -e "\n=== Searching for the full path ==="
jq 'paths(select(type == "object" and has("automaticallyEnableClosedCaptions")))' public/locales/de-DE.json 2>/dev/null
echo -e "\n=== Get content where this key exists ==="
jq 'to_entries | map(select(.value | tostring | contains("automaticallyEnableClosedCaptions"))) | .[0]' public/locales/de-DE.json 2>/dev/nullLength of output: 21545
🏁 Script executed:
#!/bin/bash
# Simpler approach - search the JSON structure
echo "=== Full structure search in de-DE ==="
jq '.settings | keys' public/locales/de-DE.json 2>/dev/null
echo -e "\n=== All settings keys to understand structure ==="
jq '.settings | to_entries | map(.key)' public/locales/de-DE.json 2>/dev/nullLength of output: 247
🏁 Script executed:
#!/bin/bash
# Use grep to find the line context around the feature sections
echo "=== Grep for automaticallyEnableClosedCaptions context ==="
grep -n "automaticallyEnableClosedCaptions" public/locales/de-DE.json
echo -e "\n=== Grep for playlistManagementButtons context ==="
grep -n "playlistManagementButtons" public/locales/de-DE.jsonLength of output: 196
Translate untranslated strings in German locale file.
The German locale file contains multiple untranslated English strings that must be converted to German before release:
- Line 105:
optionConflicterror message - Lines 138-139:
featureMenuandscreenshotButtonFileFormatconfiguration messages - Lines 292-315: Feature labels and titles (
automaticallyEnableClosedCaptions,automaticallyMaximizePlayer,copyTimestampUrlButton,defaultToOriginalAudioTrack,enablePlaylistManagementButtons,enableSaveToWatchLaterButton) - Lines 503-514: Playlist management button strings (
failedToMarkAsUnwatched,failedToRemoveVideo,markAsUnwatched,removeVideo) and Watch Later button strings
| "options": { | ||
| "notifications": { | ||
| "error": { | ||
| "optionConflict": "This option cannot be enabled while '{{OPTION}}' is enabled.", |
There was a problem hiding this comment.
Missing Hebrew translation for error message.
Line 105 contains English text instead of Hebrew: "This option cannot be enabled while '{{OPTION}}' is enabled."
This error message should be translated to Hebrew to maintain consistency with the locale file's purpose.
🤖 Prompt for AI Agents
public/locales/he-IL.json around line 105: the value for "optionConflict" is in
English; replace it with a Hebrew translation — e.g. use: "אפשרות זו לא ניתנת
להפעלה כאשר '{{OPTION}}' מופעלת." — ensure you preserve the '{{OPTION}}'
interpolation token and maintain JSON string escaping and formatting.
| "playlistManagementButtons": { | ||
| "failedToMarkAsUnwatched": "Failed to mark video as unwatched", | ||
| "failedToRemoveVideo": "Failed to remove video", | ||
| "markAsUnwatched": "Mark as unwatched", | ||
| "markingAsUnwatched": "Marking as unwatched…", | ||
| "removeVideo": "Remove video", | ||
| "removingVideo": "Removing video…" | ||
| }, | ||
| "saveToWatchLaterButton": { | ||
| "error": "Failed to add video to Watch Later", | ||
| "save": "Save to Watch Later" | ||
| }, |
There was a problem hiding this comment.
Missing Hebrew translations for playlist management and watch later features.
Lines 503-514 contain all English strings for the new playlist management and watch later button features. These need to be translated to Hebrew.
Required translations include:
- playlistManagementButtons section (lines 503-510): All 6 strings
- saveToWatchLaterButton section (lines 511-514): Both strings
🤖 Prompt for AI Agents
In public/locales/he-IL.json around lines 503 to 514, replace the English
strings for playlistManagementButtons and saveToWatchLaterButton with Hebrew
translations; set playlistManagementButtons.failedToMarkAsUnwatched to "נכשל
בסימון הווידאו כלא נצפה", failedToRemoveVideo to "נכשל בהסרת הווידאו",
markAsUnwatched to "סמן כלא נצפה", markingAsUnwatched to "מסמן כלא נצפה…",
removeVideo to "הסר את הווידאו", removingVideo to "מסיר את הווידאו…", and set
saveToWatchLaterButton.error to "נכשל בהוספת הווידאו ל'צפה מאוחר יותר'" and save
to "שמור ל'צפה מאוחר יותר'".
| "enablePlaylistManagementButtons": { | ||
| "label": "Abilita i pulsanti di gestione della lista riproduzioni", | ||
| "title": "Adds buttons to playlist items to remove videos from the playlist or mark them as unwatched" | ||
| }, | ||
| "enableSaveToWatchLaterButton": { | ||
| "label": "Enable save to Watch Later button", | ||
| "title": "Adds a button to Home and Subscriptions videos to save the video to the 'Watch Later' playlist" | ||
| }, |
There was a problem hiding this comment.
Incomplete Italian translations for new features.
Lines 310 and 314 contain English text in the title fields instead of Italian translations:
- Line 310: "Adds buttons to playlist items..." (should be Italian)
- Line 314: "Adds a button to Home and Subscriptions..." (should be Italian)
These should be translated to Italian to maintain consistency with the rest of the locale file and provide proper user experience for Italian-speaking users.
🤖 Prompt for AI Agents
In public/locales/it-IT.json around lines 308 to 315, the "title" values for
enablePlaylistManagementButtons and enableSaveToWatchLaterButton are still in
English; replace them with Italian translations: for
enablePlaylistManagementButtons set the title to "Aggiunge pulsanti agli
elementi della playlist per rimuovere video dalla playlist o segnare i video
come non visti" and for enableSaveToWatchLaterButton set the title to "Aggiunge
un pulsante nelle pagine Home e Iscrizioni per salvare il video nella playlist
'Guarda più tardi'"; ensure proper JSON string escaping and keep
quotation/commas consistent with surrounding entries.
| @keyframes yte-spin { | ||
| from { | ||
| transform: rotate(0deg); | ||
| } | ||
| to { | ||
| transform: rotate(360deg); | ||
| } | ||
| } | ||
|
|
||
| .yte-spinning { | ||
| animation: yte-spin 2s linear infinite; | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Extract duplicated animation to shared CSS.
The yte-spin animation (lines 1-8) and .yte-spinning class (lines 10-12) are identical to the definitions in src/features/playlistManagementButtons/index.css (lines 18-29). This duplication creates a maintenance burden if the animation needs updates.
Consider extracting the shared animation to a common CSS file (e.g., src/styles/animations.css or src/features/shared.css) and importing it in both feature modules:
/* src/styles/animations.css */
@keyframes yte-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.yte-spinning {
animation: yte-spin 2s linear infinite;
}Then import in both files:
import "@/src/styles/animations.css";🤖 Prompt for AI Agents
In src/features/saveToWatchLaterButton/index.css lines 1-12 (and the duplicate
in src/features/playlistManagementButtons/index.css lines ~18-29), the yte-spin
keyframes and .yte-spinning class are duplicated; extract these rules into a new
shared stylesheet (e.g., src/styles/animations.css or src/features/shared.css)
containing the @keyframes yte-spin and .yte-spinning rule, remove the duplicated
rules from both feature CSS files, and update the corresponding feature entry
points (TypeScript/JS files that currently import those CSS files) to import the
new shared stylesheet (import "@/src/styles/animations.css") so both features
use the single shared animation definition.
| saveButton.classList.add("yte-spinning"); | ||
| try { | ||
| saveButton.classList.add("yte-spinning"); | ||
| await youtube.playlist.addVideos("WL", [videoId]); |
There was a problem hiding this comment.
Duplicate classList.add("yte-spinning").
Redundant add at Lines 66 and 68. Remove one.
- saveButton.classList.add("yte-spinning");
try {
- saveButton.classList.add("yte-spinning");
+ // already spinning📝 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.
| saveButton.classList.add("yte-spinning"); | |
| try { | |
| saveButton.classList.add("yte-spinning"); | |
| await youtube.playlist.addVideos("WL", [videoId]); | |
| saveButton.classList.add("yte-spinning"); | |
| try { | |
| await youtube.playlist.addVideos("WL", [videoId]); |
🤖 Prompt for AI Agents
In src/features/saveToWatchLaterButton/index.ts around lines 66 to 69, there is
a duplicate call to saveButton.classList.add("yte-spinning") (both before and
inside the try block); remove one of the two so the class is only added once
(keep the single call in the most appropriate place — e.g., before the try or
inside it — and delete the other duplicate) to eliminate redundancy.
| heading.style.paddingRight = "40px"; | ||
| const buttonClasses = Array.from(buttons.children[0].classList).join(" "); | ||
| saveButton.className += " " + buttonClasses; | ||
| buttons.prepend(saveButton); | ||
| Array.from(buttons.children).forEach((child) => { |
There was a problem hiding this comment.
Guard on buttons.children[0] before copying classes.
buttons.children[0] can be undefined and cause a runtime error.
- const buttonClasses = Array.from(buttons.children[0].classList).join(" ");
- saveButton.className += " " + buttonClasses;
+ const firstChild = buttons.children[0] as HTMLElement | undefined;
+ if (firstChild) {
+ const buttonClasses = Array.from(firstChild.classList).join(" ");
+ saveButton.className += " " + buttonClasses;
+ }📝 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.
| heading.style.paddingRight = "40px"; | |
| const buttonClasses = Array.from(buttons.children[0].classList).join(" "); | |
| saveButton.className += " " + buttonClasses; | |
| buttons.prepend(saveButton); | |
| Array.from(buttons.children).forEach((child) => { | |
| heading.style.paddingRight = "40px"; | |
| const firstChild = buttons.children[0] as HTMLElement | undefined; | |
| if (firstChild) { | |
| const buttonClasses = Array.from(firstChild.classList).join(" "); | |
| saveButton.className += " " + buttonClasses; | |
| } | |
| buttons.prepend(saveButton); | |
| Array.from(buttons.children).forEach((child) => { |
🤖 Prompt for AI Agents
In src/features/saveToWatchLaterButton/index.ts around lines 89 to 93, the code
assumes buttons.children[0] exists when copying classes which can throw if it's
undefined; guard that access by checking that buttons.children[0] (or
buttons.firstElementChild) is truthy before reading its classList, and only
concatenate/join and assign classes when it exists (otherwise skip copying or
use a safe default string), then proceed to prepend saveButton and iterate
children as before.
| function removeSaveToWatchLaterButton(button: HTMLElement) { | ||
| const heading = button.closest("yt-lockup-view-model")!.querySelector("h3") as HTMLElement; | ||
| heading.style.paddingRight = "0"; | ||
| button.style.display = "none"; | ||
| } |
There was a problem hiding this comment.
Avoid non-null assertion; guard and fully remove the button.
button.closest("yt-lockup-view-model") or its h3 can be null; the current non-null assertion can throw. Also prefer removing the node (and restoring original padding) over display: none to prevent leaks.
-function removeSaveToWatchLaterButton(button: HTMLElement) {
- const heading = button.closest("yt-lockup-view-model")!.querySelector("h3") as HTMLElement;
- heading.style.paddingRight = "0";
- button.style.display = "none";
-}
+function removeSaveToWatchLaterButton(button: HTMLElement) {
+ const host = button.closest("yt-lockup-view-model");
+ const heading = host?.querySelector("h3") as HTMLElement | null;
+ if (heading) {
+ const original = heading.dataset.yteOriginalPaddingRight ?? "0";
+ heading.style.paddingRight = original;
+ delete heading.dataset.yteOriginalPaddingRight;
+ }
+ // Detach listeners implicitly and free the node
+ button.remove();
+}And when adding the button, persist original padding before overriding:
- if (heading && buttons) {
- heading.style.paddingRight = "40px";
+ if (heading && buttons) {
+ if (!heading.dataset.yteOriginalPaddingRight) {
+ heading.dataset.yteOriginalPaddingRight = getComputedStyle(heading).paddingRight || "0";
+ }
+ heading.style.paddingRight = "40px";Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/features/saveToWatchLaterButton/index.ts around lines 115 to 119, avoid
the non-null assertion and fully remove the button: first guard that
button.closest("yt-lockup-view-model") and its h3 exist before touching them,
restore the original heading padding (which must have been persisted when the
button was added—store original padding on the button or parent via a data-*
attribute at add time), then remove the button node from the DOM (e.g.,
button.remove()) instead of setting display:none; if required, clean up any
dataset keys you used to persist the padding.
| trustedTypes?: { | ||
| createPolicy(name: string, options: { createHTML: (input: string) => string }); | ||
| defaultPolicy; | ||
| }; |
There was a problem hiding this comment.
Incomplete Trusted Types API definition.
The type definition for trustedTypes is missing critical information:
createPolicyhas no return type (should returnTrustedTypePolicy)defaultPolicyhas no type annotation (should beTrustedTypePolicy | null)- The
optionsparameter is incomplete (can also includecreateScriptandcreateScriptURL)
Consider using the @types/trusted-types package or referencing the standard TypeScript DOM types instead of manually defining this API, which would provide complete and accurate type definitions.
If you prefer to keep the manual definition, apply this diff for a more complete type:
- trustedTypes?: {
- createPolicy(name: string, options: { createHTML: (input: string) => string });
- defaultPolicy;
- };
+ trustedTypes?: {
+ createPolicy(
+ name: string,
+ options: {
+ createHTML?: (input: string) => string;
+ createScript?: (input: string) => string;
+ createScriptURL?: (input: string) => string;
+ }
+ ): TrustedTypePolicy;
+ defaultPolicy: TrustedTypePolicy | null;
+ };You'll also need to define the TrustedTypePolicy interface or import it from @types/trusted-types.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/global.d.ts around lines 111 to 114, the trustedTypes definition is
incomplete: update createPolicy to return TrustedTypePolicy, type defaultPolicy
as TrustedTypePolicy | null, and expand the options parameter to include
createScript and createScriptURL (in addition to createHTML); either import
TrustedTypePolicy from @types/trusted-types or add a local TrustedTypePolicy
interface and use it for the return type and defaultPolicy to ensure full,
accurate typings (or alternatively remove the manual block and depend on the
standard TypeScript DOM/@types/trusted-types definitions).
|
🎉 This PR is included in version 1.29.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
Summary by CodeRabbit
Release Notes
New Features
Improvements
Localization
Dependencies