Skip to content

Frontend Overhaul#1292

Open
CaiCheng-Li wants to merge 1 commit into
YouTube-Enhancer:devfrom
CaiCheng-Li:dev
Open

Frontend Overhaul#1292
CaiCheng-Li wants to merge 1 commit into
YouTube-Enhancer:devfrom
CaiCheng-Li:dev

Conversation

@CaiCheng-Li
Copy link
Copy Markdown

@CaiCheng-Li CaiCheng-Li commented May 23, 2026

Summary by CodeRabbit

  • New Features

    • Added tabbed settings interface (Basic, Advanced, Appearance tabs) with organized sidebar navigation
    • Introduced first-time setup welcome modal with recommended feature selection
    • Added UI theme customization with multiple presets (System, Light, Dark, OLED Black, Purple Night, Ocean Blue) and custom accent colors
    • Added quick settings popup for rapid feature toggling in the browser extension
    • Reorganized settings into logical categories (Automatic Behaviors, Content Filtering, Playback Controls)
  • Documentation

    • Updated feature list structure and counts to reflect new organizational categories

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 23, 2026

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.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 0e9c422f-8bc6-403f-9026-572aa6c263af

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

Use the checkbox below for a quick retry:

  • ✅ Review completed - (🔄 Check again to review again)
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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
Member

Choose a reason for hiding this comment

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

This should be exported from the src/components/Inputs/index.ts file

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.

Switch should be imported from '@/src/components/Inputs'

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.

Strings should not be hard coded, this isn't the first place I see that happening

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.

Image

The text color on some of these buttons isn't right

More hardcoded strings

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.

I don't like how so many features are gated behind "Advanced settings" that feels like a downgrade compared to the last version, Some users may be confused to the point of thinking features were removed.

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.

More hardcoded strings

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.

I'm not a fan of this at all, showing only a limited amount of features in the popup is a regression, If a user wants to quickly adjust a value for a feature they should be able to do it with the popup

Comment thread src/pages/popup/Popup.tsx
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.

Basically same comment as for PopupView

Comment thread README.md
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.

I'm not a big fan of the "Content filtering" title, some might thing it changes what comes up in recommendations or something. Most users aren't the brightest let's be honest

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: 7

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/components/Settings/components/SettingsFooter.tsx (2)

27-33: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

clearData leaves extension-owned state behind.

This only overwrites configuration keys. Imported state:* entries in browser.storage.local and the new localStorage-backed welcome/theme state from this stack remain untouched, so “clear data” is incomplete and stale behavior can survive the reset.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Settings/components/SettingsFooter.tsx` around lines 27 - 33,
The clearData function currently only overwrites configuration keys; update
clearData to also remove extension-owned state by (1) fetching all keys from
browser.storage.local inside clearData, (2) removing any keys that start with
the "state:" prefix and any specific keys used for welcome/theme state, (3)
awaiting browser.storage.local.remove(...) for those keys (in addition to
setting defaultConfiguration), and (4) clearing corresponding localStorage
entries for welcome/theme before calling refreshSettings and addNotification so
no extension-owned stale state survives the reset; reference clearData,
defaultConfiguration, refreshSettings, and addNotification when making the
changes.

27-33: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Await browser.storage.local.set() before reloading settings.

browser.storage.local.set(...) is async (Promise-returning) in webextension-polyfill, and the code currently calls refreshSettings() immediately, risking a read-after-write race and silently ignoring write failures.

  • clearData (27-33): await the storage write before refreshSettings().
  • Confirm reset handler (59-67): same.
Suggested fix
 function clearData() {
 	void (async () => {
 		const userHasConfirmed = window.confirm(t((translations) => translations.pages.options.extras.clearData.confirmAlert));
 		if (userHasConfirmed) {
-			void browser.storage.local.set(defaultConfiguration);
+			await browser.storage.local.set(defaultConfiguration);
 			await refreshSettings();
 			addNotification("success", (translations) => translations.pages.options.extras.clearData.allDataDeleted);
 		}
 	})();
 }
 ...
 						onClick={() => {
 							void (async () => {
 								const notificationToRemove = notifications.find((n) => n.action === "reset_settings");
 								if (notificationToRemove) {
 									removeNotification(notificationToRemove);
 								}
-								void browser.storage.local.set({ ...defaultConfiguration, ...{ rememberVolume: settings.rememberVolume } });
+								await browser.storage.local.set({ ...defaultConfiguration, rememberVolume: settings.rememberVolume });
 								await refreshSettings();
 								addNotification("success", (translations) => translations.pages.options.notifications.success.saved);
 							})();
 						}}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Settings/components/SettingsFooter.tsx` around lines 27 - 33,
The storage write is currently not awaited which can cause a read-after-write
race and swallow failures; in clearData (function clearData) and in the reset
confirmation handler (the confirm/reset handler around lines 59-67), await the
Promise returned by browser.storage.local.set(...) before calling
refreshSettings() or addNotification, and propagate or log any errors so
refreshSettings() runs only after the write completes; locate calls to
browser.storage.local.set, refreshSettings, and addNotification to update those
flows.
♻️ Duplicate comments (4)
src/pages/popup/Popup.tsx (1)

8-16: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

QueryClient recreated on every render.

The QueryClient is instantiated inside the component body, so it's recreated on every render. This discards the cache and defeats React Query's memoization. Move the client to module scope or wrap it in useState(() => new QueryClient(...)).

♻️ Proposed fix
+const queryClient = new QueryClient({
+	defaultOptions: {
+		queries: {
+			refetchInterval: 500,
+			refetchOnWindowFocus: true,
+			staleTime: 250
+		}
+	}
+});
+
 export default function Popup(): JSX.Element {
-	const client = new QueryClient({
-		defaultOptions: {
-			queries: {
-				refetchInterval: 500,
-				refetchOnWindowFocus: true,
-				staleTime: 250
-			}
-		}
-	});
 	return (
-		<QueryClientProvider client={client}>
+		<QueryClientProvider client={queryClient}>
 			<PopupView />
 		</QueryClientProvider>
 	);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/popup/Popup.tsx` around lines 8 - 16, The QueryClient instance
named client in Popup.tsx is being recreated on every render; move its creation
out of the component (module scope) or initialize it once with useState to
preserve React Query cache. Concretely, either declare a top-level const
queryClient = new QueryClient({ defaultOptions: { queries: { refetchInterval:
500, refetchOnWindowFocus: true, staleTime: 250 } } }) and use that inside the
Popup component, or inside the Popup component replace the direct new
QueryClient(...) with const [client] = useState(() => new QueryClient({
defaultOptions: { queries: { refetchInterval: 500, refetchOnWindowFocus: true,
staleTime: 250 } } })); then keep using client where currently referenced.
src/components/Settings/components/WelcomeModal.tsx (1)

16-24: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Move modal copy and recommended feature text into i18n resources.

This modal still hardcodes user-facing English strings, so non-English users will get mixed-language UI in first-time setup.

Also applies to: 93-100, 129-137

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Settings/components/WelcomeModal.tsx` around lines 16 - 24,
The hardcoded user-facing strings in WelcomeModal.tsx (the array of feature
objects and the modal title/body/button copy used in the WelcomeModal component)
must be moved into i18n resources and replaced with translation lookups; locate
the feature list object in WelcomeModal (the entries with keys like
"hideShorts.enabled", "rememberVolume.enabled", "removeRedirect.enabled", etc.)
and replace each description/label with a call to the project's i18n function
(e.g., t('welcomeModal.features.hideShorts.description') and
t('welcomeModal.features.hideShorts.label')), and also extract the modal title,
descriptive paragraphs, and button labels (the hardcoded strings referenced
around the other modal usages) into i18n keys and use t(...) there so all
visible text uses localization keys. Ensure unique i18n keys per feature and per
modal string and update any tests or snapshots that expect the English text.
src/components/Settings/tabs/AppearanceTab.tsx (1)

17-34: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Localize Appearance tab labels and button text.

The tab contains hardcoded UI strings (theme names, headings, titles, reset text), which breaks localization consistency.

Also applies to: 75-76, 99-100, 122-147

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Settings/tabs/AppearanceTab.tsx` around lines 17 - 34, The
Appearance tab currently hardcodes UI strings in the color-label array and
THEME_CARDS (and the headings/buttons at lines referenced), so replace the
literal labels ("Blue", "Purple", "System", "Light", "Dark", "OLED Black",
"Purple Night", "Ocean Blue", headings, reset text, etc.) with calls to the
app's localization function (e.g., t('appearance.theme.blue') or similar) inside
the AppearanceTab component; update the color constants and THEME_CARDS
definitions to store translated labels (call t from within the component scope
or map keys to t(...) at render time) and add corresponding i18n keys for each
theme name and any button/heading text mentioned at lines 75-76, 99-100, and
122-147 so the UI uses localized strings instead of hardcoded English.
src/components/Settings/SettingsGenerator.tsx (1)

38-61: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Internationalize section descriptions.

These hardcoded English descriptions break multilingual support. The project supports 16 locales, so all user-facing strings must use i18n translation keys like SECTION_TITLE_FALLBACKS does.

🌐 Recommended approach

Move these descriptions to locale files and use translation selectors:

-const SECTION_DESCRIPTIONS: Partial<Record<SettingsSectionId, string>> = {
-	automaticBehaviors: "Actions that trigger automatically when you load or watch a video",
-	buttonPlacement: "Configure where feature buttons appear in the player",
+const SECTION_DESCRIPTIONS: Partial<Record<SettingsSectionId, TSelectFunc>> = {
+	automaticBehaviors: (t) => t((tr) => tr.settings.sections.automaticBehaviors.description),
+	buttonPlacement: (t) => t((tr) => tr.settings.sections.buttonPlacement.description),
	// ... etc for all 24 entries
 };

Then update line 340 to evaluate the function:

-	description={SECTION_DESCRIPTIONS[sectionId]}
+	description={SECTION_DESCRIPTIONS[sectionId]?.(t)}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Settings/SettingsGenerator.tsx` around lines 38 - 61,
SECTION_DESCRIPTIONS contains hardcoded English strings — move each description
into the locale files as translation keys (mirroring how SECTION_TITLE_FALLBACKS
is handled), replace SECTION_DESCRIPTIONS with a function or object factory that
accepts the i18n translator (e.g., getSectionDescriptions(t) or
SECTION_DESCRIPTION_FALLBACKS) and returns translated strings, and update the
consumer in SettingsGenerator (the same place SECTION_TITLE_FALLBACKS is
evaluated) to call that function with the current t selector so the UI renders
locale-specific descriptions.
🟡 Minor comments (17)
README.md-48-48 (1)

48-48: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix "You Tube" → "YouTube" in feature names.

Three feature names incorrectly spell YouTube as two words. The brand name is "YouTube" (one word).

✏️ Proposed typography fixes
-- **Open You Tube Settings On Hover**: Opens the YouTube settings menu when you hover over the settings button
+- **Open YouTube Settings On Hover**: Opens the YouTube settings menu when you hover over the settings button
-- **Remove You Tube /redirect URLs**: Replaces YouTube redirect by actual URLs, skipping redirect warning dialogue
+- **Remove YouTube /redirect URLs**: Replaces YouTube redirect by actual URLs, skipping redirect warning dialogue
-- **Shorten You Tube Video Share Link**: Shortens YouTube video share link (youtu.be) by stripping si/feature parameters from it
+- **Shorten YouTube Video Share Link**: Shortens YouTube video share link (youtu.be) by stripping si/feature parameters from it

Also applies to: 50-50, 52-52

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` at line 48, Locate the three feature name strings that currently
spell "You Tube" (for example "**Open You Tube Settings On Hover**" and the two
other similar feature lines) and change the typography to the correct brand name
"YouTube" (e.g., "**Open YouTube Settings On Hover**"); update all three
occurrences so the feature names consistently use "YouTube" as one word.
src/components/Settings/components/ImportExportSection.tsx-155-184 (1)

155-184: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Localize the new section copy.

These three strings are hardcoded English, so the new UI will regress in non-English locales even though the surrounding controls already use t(...).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Settings/components/ImportExportSection.tsx` around lines 155
- 184, The Import & Export section has hardcoded English strings ("Import &
Export" heading, "or drop a .json file", "saves as JSON") that must be
localized; update the JSX in ImportExportSection.tsx to replace these literals
with t(...) calls using appropriate translation keys (e.g.
pages.options.extras.importExportSettings.heading, .importButton.hint
orDropJson, .exportButton.hint savesAsJson) so the heading and the two
small-span helper texts use the same i18n pattern as the existing button labels;
ensure you add corresponding entries to the translation files.
src/pages/popup/Popup.tsx-11-13 (1)

11-13: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

staleTime shorter than refetchInterval creates a stale data window.

With staleTime: 250 and refetchInterval: 500, queries become stale at 250ms but don't refetch until 500ms, leaving a 250ms window where the UI shows stale data. For a popup syncing with storage changes, consider setting staleTime: 0 or matching it to refetchInterval to avoid displaying outdated toggle states.

⚙️ Proposed fix
 		defaultOptions: {
 			queries: {
 				refetchInterval: 500,
 				refetchOnWindowFocus: true,
-				staleTime: 250
+				staleTime: 0
 			}
 		}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/popup/Popup.tsx` around lines 11 - 13, The query options in
Popup.tsx currently set refetchInterval: 500 and staleTime: 250 which creates a
250ms window of stale UI; update the query configuration used in the popup (the
object containing refetchInterval and staleTime) to either set staleTime: 0 or
set staleTime to the same value as refetchInterval (500) so queries are not
considered stale before the next background refetch and the toggle state never
briefly shows outdated data.
src/components/Settings/SettingsGenerator.tsx-343-343 (1)

343-343: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Empty string fallback may cause accessibility issues.

If no title is resolved, title="" renders a section with no semantic heading, which can confuse screen readers and breaks document outline.

🛡️ Proposed fix
-	title={storedSectionTitle ? storedSectionTitle(t) : ""}
+	title={storedSectionTitle?.(t) ?? sectionId}

This ensures every section has at least an identifier as a fallback heading.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Settings/SettingsGenerator.tsx` at line 343, The title prop is
being set to an empty string when storedSectionTitle resolves to falsy, which
harms accessibility; update the code that sets title (the storedSectionTitle
usage in SettingsGenerator) to provide a meaningful fallback (for example use
the section's key/id or a translated generic like t('settings.section') or
generate "Section: {sectionId}") so title is never empty, or alternatively
render/associate a visually-hidden heading and reference it via aria-labelledby
for the section.
public/locales/ca-ES.json-292-316 (1)

292-316: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Localize newly added Catalan strings before merge.

Line 293 through Line 316 and Line 1010 through Line 1016 are still in English in ca-ES. This will show untranslated UI in the new tabs/welcome/theme flow.

Also applies to: 1009-1016

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/locales/ca-ES.json` around lines 292 - 316, Translate the untranslated
English strings in the Catalan locale by replacing the keys under "welcome"
(title, subtitle, getStarted, customize, recommendedLabel), the "tabs" keys
(basic, advanced, appearance), and the "appearance" block (title, themePresets,
accentColor, resetTheme and the nested "themes" values: system, light, dark,
oled, purple, ocean) with proper Catalan text; apply the same translations to
the duplicate appearance/theme block later in the file (the other occurrence of
the same keys) so both places show Catalan UI copy.
public/locales/cs-CZ.json-292-316 (1)

292-316: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Localize newly added Czech strings before merge.

Line 293 through Line 316 and Line 1010 through Line 1016 are English placeholders in cs-CZ, so the new onboarding/tabs/section headers won’t be localized.

Also applies to: 1009-1016

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/locales/cs-CZ.json` around lines 292 - 316, Strings under the
"welcome" (welcome.title, welcome.subtitle, welcome.getStarted,
welcome.customize, welcome.recommendedLabel), "tabs" (tabs.basic, tabs.advanced,
tabs.appearance) and "appearance" (appearance.title, appearance.themePresets,
appearance.accentColor, appearance.resetTheme and
appearance.themes.system/light/dark/oled/purple/ocean) keys are still English in
the cs-CZ locale; replace those English placeholders with proper Czech
translations and ensure the same keys are translated in the other occurrence of
these blocks so both instances are localized consistently.
public/locales/es-ES.json-292-316 (1)

292-316: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Localize the newly added Spanish entries.

Line 293 through Line 316 and Line 1010 through Line 1016 are English in es-ES, so the new welcome/tabs/appearance and section labels are not translated.

Also applies to: 1009-1016

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/locales/es-ES.json` around lines 292 - 316, The es-ES locale contains
untranslated English strings for the JSON keys "welcome" (title, subtitle,
getStarted, customize, recommendedLabel), "tabs" (basic, advanced, appearance),
and "appearance" (title, themePresets, accentColor, resetTheme, and the "themes"
entries system/light/dark/oled/purple/ocean); update those values to their
Spanish translations in public/locales/es-ES.json (also ensure the duplicate
block around the keys at the other occurrence is translated) so each value is
the proper Spanish phrase for the corresponding key.
public/locales/fr-FR.json-292-316 (1)

292-316: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Translate newly added French locale strings.

Line 293 through Line 316 and Line 1010 through Line 1016 are English in fr-FR, which introduces mixed-language UI in the new settings flow.

Also applies to: 1009-1016

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/locales/fr-FR.json` around lines 292 - 316, The fr-FR locale contains
English strings for the newly added settings UI; update the entries in
public/locales/fr-FR.json for the welcome and appearance/tabs keys (specifically
"welcome.title", "welcome.subtitle", "welcome.getStarted", "welcome.customize",
"welcome.recommendedLabel", "tabs.basic", "tabs.advanced", "tabs.appearance",
"appearance.title", "appearance.themePresets", "appearance.accentColor",
"appearance.resetTheme" and the theme names under "appearance.themes" such as
"system", "light", "dark", "oled", "purple", "ocean") to their proper French
translations, and also fix the duplicate English block present elsewhere in the
file (the other block of settings strings around the second occurrence) so all
these keys are consistently translated to French.
public/locales/de-DE.json-292-316 (1)

292-316: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Translate the newly added German locale entries.

Line 293 through Line 316 and Line 1010 through Line 1016 are English in de-DE, which causes mixed-language UI in the new settings experience.

Also applies to: 1009-1016

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/locales/de-DE.json` around lines 292 - 316, Replace the English
strings under the locale keys with their German translations: update
"welcome.title" → "Willkommen bei YouTube Enhancer!", "welcome.subtitle" → "Wir
haben einige empfehlenswerte Einstellungen für neue Nutzer vorgewählt. Du kannst
sie jederzeit ändern.", "welcome.getStarted" → "Loslegen", "welcome.customize" →
"Einstellungen anpassen", "welcome.recommendedLabel" → "Empfohlene
Einstellungen"; update "tabs.basic" → "Allgemein", "tabs.advanced" →
"Erweitert", "tabs.appearance" → "Darstellung"; update "appearance.title" →
"Darstellung", "appearance.themePresets" → "Designvorgaben",
"appearance.accentColor" → "Akzentfarbe", "appearance.resetTheme" → "Standard
wiederherstellen", and translate the "appearance.themes" entries: "system" →
"System", "light" → "Hell", "dark" → "Dunkel", "oled" → "OLED Schwarz", "purple"
→ "Lila Nacht", "ocean" → "Ozeanblau"; apply the same German translations for
the duplicate keys mentioned around the other block so no English strings remain
in de-DE.
public/locales/pl-PL.json-292-316 (1)

292-316: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Provide Polish translations for the newly added settings keys.

These newly introduced values are still English (welcome, tabs, appearance, and new section titles), which creates mixed-language UI in pl-PL.

Also applies to: 1009-1016

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/locales/pl-PL.json` around lines 292 - 316, Translate the newly added
English JSON keys into Polish in public/locales/pl-PL.json: replace the
"welcome" block values
("title","subtitle","getStarted","customize","recommendedLabel") with Polish
equivalents, translate the "tabs" values ("basic","advanced","appearance") and
all entries under the "appearance" block
("title","themePresets","accentColor","resetTheme" and the "themes" names
"system","light","dark","oled","purple","ocean") so the pl-PL locale contains
full Polish strings (also apply the same translations for the repeated block at
lines referenced 1009-1016).
public/locales/fa-IR.json-292-316 (1)

292-316: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Localize the newly added settings strings for this locale.

The newly added welcome, tabs, appearance, and new section titles are still English, which will surface mixed-language UI for Persian users. Please provide fa-IR translations for these keys.

Also applies to: 1009-1016

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/locales/fa-IR.json` around lines 292 - 316, Replace the English
placeholders in this locale with Persian translations: localize the "welcome"
object keys (title, subtitle, getStarted, customize, recommendedLabel), the
"tabs" keys (basic, advanced, appearance), the "appearance" section keys (title,
themePresets, accentColor, resetTheme) and the nested "themes" keys (system,
light, dark, oled, purple, ocean) with appropriate fa-IR strings; also locate
and update the duplicate set of these keys elsewhere in the same file to ensure
no mixed-language UI remains.
public/locales/ko-KR.json-292-316 (1)

292-316: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Localize new settings strings in this Korean locale file.

The newly added onboarding/theme/tab texts and section titles are English, causing mixed-language rendering for Korean users. Please provide ko-KR translations for these keys.

Also applies to: 1009-1016

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/locales/ko-KR.json` around lines 292 - 316, The strings under the keys
welcome.*, tabs.*, appearance.* and appearance.themes.* are still in English;
update their values in the ko-KR locale JSON to Korean equivalents (translate:
welcome.title, welcome.subtitle, welcome.getStarted, welcome.customize,
welcome.recommendedLabel; tabs.basic, tabs.advanced, tabs.appearance;
appearance.title, appearance.themePresets, appearance.accentColor,
appearance.resetTheme; and appearance.themes.system, .light, .dark, .oled,
.purple, .ocean) so the onboarding, tab labels and theme presets render fully
localized for Korean users.
public/locales/ja-JP.json-292-316 (1)

292-316: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Translate the newly introduced keys for Japanese UI consistency.

The added welcome, tabs, appearance, and new section-title strings are English in ja-JP, which introduces mixed-language UI. Please localize these entries.

Also applies to: 1009-1016

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/locales/ja-JP.json` around lines 292 - 316, Several UI strings under
the keys "welcome" (title, subtitle, getStarted, customize, recommendedLabel),
"tabs" (basic, advanced, appearance), and "appearance" (title, themePresets,
accentColor, resetTheme, themes.system/light/dark/oled/purple/ocean) are still
in English in the ja-JP file; replace each of these values with appropriate
Japanese translations (and do the same for the duplicate set referenced around
keys at the other location noted) so the Japanese locale contains fully
localized strings for those keys.
public/locales/he-IL.json-292-316 (1)

292-316: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Localize the newly added settings strings for this locale.

The added welcome, tabs, appearance, and section-title entries are in English, so Hebrew users will see mixed-language UI. Please translate these keys in he-IL.

Also applies to: 1009-1016

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/locales/he-IL.json` around lines 292 - 316, Translate the newly added
English strings for the locale keys under "welcome", "tabs", and "appearance"
into Hebrew: replace "welcome.title", "welcome.subtitle", "welcome.getStarted",
"welcome.customize", "welcome.recommendedLabel", "tabs.basic", "tabs.advanced",
"tabs.appearance", "appearance.title", "appearance.themePresets",
"appearance.accentColor", "appearance.resetTheme" and the "appearance.themes"
entries ("system", "light", "dark", "oled", "purple", "ocean") with proper
Hebrew translations so the UI is fully localized for he-IL; keep the same JSON
keys and punctuation, ensure strings are valid JSON (escaped where necessary),
and apply the same translation for the duplicate entries referenced in lines
1009-1016.
public/locales/it-IT.json-292-316 (1)

292-316: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Localize newly added entries instead of shipping English fallback text.

In this Italian locale, the new welcome/tabs/appearance strings and the new section titles are still English. This causes mixed-language UX; please add it-IT translations for these keys.

Also applies to: 1009-1016

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/locales/it-IT.json` around lines 292 - 316, The Italian locale
currently contains English fallback text for the new localization keys—replace
the English values under the "welcome" object (title, subtitle, getStarted,
customize, recommendedLabel), the "tabs" keys (basic, advanced, appearance), and
the "appearance" block (title, themePresets, accentColor, resetTheme and the
"themes" entries: system, light, dark, oled, purple, ocean) with proper it-IT
translations; also locate the other duplicate occurrence of these keys and apply
the same Italian translations so there is no mixed-language UX.
public/locales/pt-BR.json-292-316 (1)

292-316: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Translate new onboarding/theme/tab strings for pt-BR.

The new welcome, tabs, appearance, and section-title values are still in English, which causes mixed-language UI in Brazilian Portuguese.

Also applies to: 1009-1016

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/locales/pt-BR.json` around lines 292 - 316, The UI strings under the
localization keys are still in English; translate every value under the
"welcome" keys (welcome.title, welcome.subtitle, welcome.getStarted,
welcome.customize, welcome.recommendedLabel), the "tabs" keys (tabs.basic,
tabs.advanced, tabs.appearance), and the "appearance" keys (appearance.title,
appearance.themePresets, appearance.accentColor, appearance.resetTheme and
appearance.themes.system/light/dark/oled/purple/ocean) into Brazilian Portuguese
(pt-BR) with correct accents/terminology, and make the same translations for the
duplicate occurrence of these keys elsewhere in the file so both blocks match.
Ensure the keys remain unchanged and only the string values are replaced.
public/locales/nl-NL.json-292-316 (1)

292-316: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Translate newly added locale keys to avoid mixed-language UX.

In nl-NL, the new welcome/tabs/appearance entries and new section titles are still English. Please localize these values into Dutch.

Also applies to: 1009-1016

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/locales/nl-NL.json` around lines 292 - 316, Translate the English
strings for the newly added locale keys into Dutch: update the "welcome" object
(title, subtitle, getStarted, customize, recommendedLabel), the "tabs" keys
(basic, advanced, appearance), the "appearance" keys (title, themePresets,
accentColor, resetTheme) and the nested "themes" entries (system, light, dark,
oled, purple, ocean) with proper Dutch text while keeping the JSON keys
unchanged and quoted; also apply the same translations to the other identical
locale block elsewhere in the file to avoid mixed-language UX.
🧹 Nitpick comments (15)
src/pages/popup/PopupView.tsx (4)

94-108: ⚖️ Poor tradeoff

Inline switch implementation instead of reusing Switch component.

This layer depends on the Switch component introduced in an earlier layer, but the code implements an inline switch here. Reusing the Switch component would ensure consistent behavior and styling with the main Settings UI.

Note: The inline implementation is functional and visually appropriate for the popup context. Consider reusing Switch only if strict UI consistency across all surfaces is a project requirement.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/popup/PopupView.tsx` around lines 94 - 108, This inline switch in
PopupView.tsx duplicates UI/behavior instead of reusing the existing Switch
component; replace the button/span block with the shared Switch component
(import Switch if missing) and wire it to the same props: pass checked={isOn},
onChange={(newVal) => toggleMutation.mutate({ featureKey: feature.key, newValue:
newVal })} and any aria/role props (aria-checked, role="switch") and className
overrides if needed so the Switch handles animation/styling consistently across
the app.

36-36: 💤 Low value

Duplicated feature key parsing logic.

The featureKey.split(".") pattern appears at lines 36 and 122. Extract this to a helper function or validate the format once to reduce duplication.

♻️ Proposed refactor
+function parseFeatureKey(key: string): [string, string] {
+	const parts = key.split(".");
+	if (parts.length !== 2) throw new Error(`Invalid feature key format: ${key}`);
+	return parts as [string, string];
+}
+
 const toggleMutation = useMutation({
 	mutationFn: async ({ featureKey, newValue }: { featureKey: string; newValue: boolean }) => {
-		const [featureName, settingName] = featureKey.split(".");
+		const [featureName, settingName] = parseFeatureKey(featureKey);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/popup/PopupView.tsx` at line 36, Extract the duplicated parsing
logic featureKey.split(".") into a reusable helper (e.g., parseFeatureKey) and
replace both occurrences in PopupView with calls to that helper; the helper
should accept featureKey, validate the format (ensure it contains exactly one
"."), return { featureName, settingName } (or throw/return null and handle
errors where used), and update any code that currently does const [featureName,
settingName] = featureKey.split(".") to use the new parseFeatureKey function.

118-118: 💤 Low value

Unnecessary Set creation.

Creating a Set and immediately spreading it to an array is redundant here. Since QUICK_FEATURES is a const array with a known structure, just map directly.

♻️ Proposed fix
-	const featureKeys = [...new Set(QUICK_FEATURES.map((f) => f.key.split(".")[0]))];
+	const featureKeys = QUICK_FEATURES.map((f) => f.key.split(".")[0]);

Note: If duplicate feature names are a concern, retain the Set.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/popup/PopupView.tsx` at line 118, Replace the unnecessary Set usage
when deriving featureKeys: instead of creating a new Set and spreading it,
directly map QUICK_FEATURES to extract the prefix (use QUICK_FEATURES and
featureKeys to locate the line) — e.g., set featureKeys to the mapped array of
f.key.split(".")[0]; if deduplication is required keep the Set approach but
otherwise remove it for simplicity.

30-31: 💤 Low value

Query options duplicate parent defaults.

The refetchInterval: 500 and staleTime: 250 specified here are identical to the QueryClient defaults in Popup.tsx (lines 11, 13). Remove these redundant options to inherit from the parent configuration.

♻️ Proposed fix
 	const { data: quickSettings } = useQuery({
 		queryFn: fetchQuickSettings,
-		queryKey: ["popup-settings"],
-		refetchInterval: 500,
-		staleTime: 250
+		queryKey: ["popup-settings"]
 	});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/popup/PopupView.tsx` around lines 30 - 31, In PopupView.tsx remove
the redundant query options refetchInterval: 500 and staleTime: 250 from the
query configuration so the component (PopupView) inherits the parent QueryClient
defaults defined in Popup.tsx; locate the query invocation or options object
used by PopupView (the place where refetchInterval and staleTime are currently
set) and delete those two properties.
src/components/Settings/sections/LanguageSettings.tsx (1)

37-40: 💤 Low value

Consider extracting the repeated className pattern.

The className string "mb-3 break-inside-avoid rounded-xl bg-[var(--card-bg)] p-2 shadow-sm" appears identically in ButtonPlacement, FeatureMenuOpenType, and LanguageSettings. Consider extracting it to a shared constant for maintainability.

♻️ Example refactor

Create a shared constant:

// In a shared constants file or at the top of a common module
export const SETTING_SECTION_CARD_CLASSNAME = "mb-3 break-inside-avoid rounded-xl bg-[var(--card-bg)] p-2 shadow-sm";

Then use it in each component:

 <SettingSection
-  className="mb-3 break-inside-avoid rounded-xl bg-[var(--card-bg)] p-2 shadow-sm"
+  className={SETTING_SECTION_CARD_CLASSNAME}
   title={t((translations) => translations.pages.options.extras.language.title)}
 >
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Settings/sections/LanguageSettings.tsx` around lines 37 - 40,
Extract the repeated className string used on SettingSection into a shared
constant (e.g., SETTING_SECTION_CARD_CLASSNAME) and replace the literal in
LanguageSettings, ButtonPlacement, and FeatureMenuOpenType with that constant;
create the constant in a common module (or at top of a shared file), export it,
and import it where each component (LanguageSettings, ButtonPlacement,
FeatureMenuOpenType) uses the SettingSection component so all three reference
the single exported constant instead of repeating the string.
src/components/Settings/sections/YouTubeDataApiKey.tsx (1)

15-18: ⚡ Quick win

Consider extracting the repeated className to a shared constant.

The same className string appears in both this file and OnScreenDisplay.tsx (and per the stack context, likely in ButtonPlacement.tsx, FeatureMenuOpenType.tsx, and LanguageSettings.tsx as well). Extracting it to a shared constant would ensure consistency and simplify future styling updates.

♻️ Proposed refactor

Create a new file src/components/Settings/sections/constants.ts:

export const SECTION_CARD_CLASSNAME = "mb-3 break-inside-avoid rounded-xl bg-[var(--card-bg)] p-2 shadow-sm";

Then import and use it in each section file:

+import { SECTION_CARD_CLASSNAME } from "./constants";
+
 export default function YouTubeDataApiKeySection() {
   // ...
   return (
     <SettingSection
-      className="mb-3 break-inside-avoid rounded-xl bg-[var(--card-bg)] p-2 shadow-sm"
+      className={SECTION_CARD_CLASSNAME}
       title={t((translations) => translations.pages.options.extras.youtubeDataApiV3Key.title)}
     >

Apply this pattern to all 5 section files mentioned in the stack context.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Settings/sections/YouTubeDataApiKey.tsx` around lines 15 - 18,
Extract the repeated className used on SettingSection into a shared constant
(e.g. SECTION_CARD_CLASSNAME) in a new module (suggested:
src/components/Settings/sections/constants.ts) and replace the inline className
prop in YouTubeDataApiKey's SettingSection with that constant; do the same
replacement in the other files mentioned (OnScreenDisplay.tsx,
ButtonPlacement.tsx, FeatureMenuOpenType.tsx, LanguageSettings.tsx) so they
import SECTION_CARD_CLASSNAME and pass it to SettingSection instead of
duplicating the string.
src/components/Settings/sections/OnScreenDisplay.tsx (1)

114-117: Confirm --card-bg is defined for theming
bg-[var(--card-bg)] is backed by src/components/Settings/Settings.css, where --card-bg is defined for :root and theme variants (light/dark + [data-theme="..."]).
Repeated className="... bg-[var(--card-bg)] ..." across multiple settings sections could be extracted for consistency if you want.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Settings/sections/OnScreenDisplay.tsx` around lines 114 - 117,
Verify that Settings.css defines --card-bg on :root and in each theme variant,
then replace repeated inline usages of bg-[var(--card-bg)] on SettingSection
components with a single shared utility/class to ensure consistency: update
Settings.css to include a semantic class (e.g., .settings-card-bg) that applies
background: var(--card-bg) and refactor SettingSection instances to use that
class in their className instead of duplicating bg-[var(--card-bg)]; reference
SettingSection, className, Settings.css and the --card-bg CSS variable when
making the change.
public/locales/zh-CN.json (1)

292-316: 🏗️ Heavy lift

Localize the newly added zh-CN strings.

These keys are English-only (welcome/tabs/appearance and new section titles), which creates inconsistent Simplified Chinese UX in the updated settings interface.

Also applies to: 1009-1016

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/locales/zh-CN.json` around lines 292 - 316, The zh-CN locale file is
missing Chinese translations for the new keys under "welcome", "tabs", and
"appearance" (including nested "themes" keys like
"system","light","dark","oled","purple","ocean") — update
public/locales/zh-CN.json by replacing the English strings for "welcome" (title,
subtitle, getStarted, customize, recommendedLabel), "tabs" (basic, advanced,
appearance), and "appearance" (title, themePresets, accentColor, resetTheme, and
each theme name) with appropriate Simplified Chinese translations so the
settings UI is fully localized.
public/locales/vi-VN.json (1)

292-316: 🏗️ Heavy lift

Translate the new entries in vi-VN to avoid mixed-language UI.

The added welcome/tabs/appearance strings and new section titles are currently English and should be localized for Vietnamese users.

Also applies to: 1009-1016

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/locales/vi-VN.json` around lines 292 - 316, The new localization
entries under the "welcome", "tabs", and "appearance" keys (including nested
keys like "getStarted", "customize", "recommendedLabel", "basic", "advanced",
"appearance", "title", "themePresets", "accentColor", "resetTheme", and the
"themes" variants "system", "light", "dark", "oled", "purple", "ocean") are
still in English; replace them with Vietnamese translations so the UI is fully
localized (translate each string to Vietnamese, keeping key names unchanged and
preserving punctuation/casing). Ensure the same change is applied to the other
occurrences referenced (lines 1009-1016) so no English remains.
public/locales/hi-IN.json (1)

292-316: 🏗️ Heavy lift

Add Hindi translations for the newly added locale keys.

These new strings are English in hi-IN (welcome modal, tabs, appearance labels, and section titles), causing a mixed-language experience.

Also applies to: 1009-1016

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/locales/hi-IN.json` around lines 292 - 316, The hi-IN locale file
contains English strings under the "welcome", "tabs", and "appearance" objects
(e.g., welcome.title, welcome.subtitle, welcome.getStarted, welcome.customize,
welcome.recommendedLabel, tabs.basic, tabs.advanced, tabs.appearance,
appearance.title, appearance.themePresets, appearance.accentColor,
appearance.resetTheme, appearance.themes.system/light/dark/oled/purple/ocean);
replace these English values with proper Hindi translations for each key so the
hi-IN file is fully localized (update the string values for those keys and
ensure consistency for the additional occurrences mentioned at lines 1009-1016).
public/locales/ru-RU.json (1)

292-316: 🏗️ Heavy lift

Localize newly added strings in the Russian locale file.

These new entries are English-only, so Russian users will see mixed-language UI in the welcome flow, tab labels, appearance settings, and new section headers. Please translate these keys in ru-RU to keep locale consistency.

Also applies to: 1009-1016

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/locales/ru-RU.json` around lines 292 - 316, Translate the newly added
English strings under the "welcome", "tabs", and "appearance" keys into Russian
in the ru-RU locale: provide Russian equivalents for welcome.title,
welcome.subtitle, welcome.getStarted, welcome.customize,
welcome.recommendedLabel, tabs.basic, tabs.advanced, tabs.appearance,
appearance.title, appearance.themePresets, appearance.accentColor,
appearance.resetTheme, and all entries under appearance.themes (system, light,
dark, oled, purple, ocean); also apply the same translations to the other
identical block of keys present elsewhere in the file to ensure consistency
across the welcome flow and appearance settings.
public/locales/tr-TR.json (1)

292-316: 🏗️ Heavy lift

Provide Turkish translations for the newly introduced locale keys.

The new welcome/tabs/appearance labels and section headers are English-only in tr-TR, causing inconsistent localized UX.

Also applies to: 1009-1016

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/locales/tr-TR.json` around lines 292 - 316, The Turkish locale file is
missing translations for the new keys; update the JSON by replacing the English
strings for welcome.title, welcome.subtitle, welcome.getStarted,
welcome.customize, welcome.recommendedLabel, tabs.basic, tabs.advanced,
tabs.appearance, appearance.title, appearance.themePresets,
appearance.accentColor, appearance.resetTheme and the appearance.themes entries
(system, light, dark, oled, purple, ocean) with appropriate Turkish translations
so the UI is fully localized (also apply the same translations where the
duplicate block appears around the other occurrence).
public/locales/zh-TW.json (1)

292-316: 🏗️ Heavy lift

Translate the new keys in zh-TW for locale consistency.

The newly introduced welcome/tabs/appearance entries and section headers are English; this will show mixed-language UI for Traditional Chinese users.

Also applies to: 1009-1016

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/locales/zh-TW.json` around lines 292 - 316, Translate the English
values for the new locale keys into Traditional Chinese: replace welcome.title,
welcome.subtitle, welcome.getStarted, welcome.customize,
welcome.recommendedLabel, tabs.basic, tabs.advanced, tabs.appearance,
appearance.title, appearance.themePresets, appearance.accentColor,
appearance.resetTheme and all appearance.themes entries (system, light, dark,
oled, purple, ocean) with appropriate zh-TW strings so the UI is fully
localized; make the same replacements for the other identical block of keys
introduced elsewhere in the file.
public/locales/uk-UA.json (1)

292-316: 🏗️ Heavy lift

Add Ukrainian translations for the new settings strings.

These newly added keys are English in uk-UA, which introduces mixed-language content in the welcome modal, tabs, appearance tab, and section grouping titles.

Also applies to: 1009-1016

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/locales/uk-UA.json` around lines 292 - 316, The uk-UA locale contains
English strings for the new UI keys; replace them with Ukrainian translations
for the welcome modal and appearance/tabs keys. Update the values for
"welcome.title", "welcome.subtitle", "welcome.getStarted", "welcome.customize",
"welcome.recommendedLabel", "tabs.basic", "tabs.advanced", "tabs.appearance",
"appearance.title", "appearance.themePresets", "appearance.accentColor",
"appearance.resetTheme" and each "appearance.themes" entry
("system","light","dark","oled","purple","ocean") to their proper Ukrainian
equivalents (also apply same translations where the same keys appear later in
the file around the other occurrence mentioned). Ensure whitespace/JSON
formatting remains valid.
public/locales/sv-SE.json (1)

292-316: 🏗️ Heavy lift

Translate the new sv-SE keys instead of shipping English text.

The added welcome/tabs/appearance strings and new settings section titles are currently English, which introduces mixed-language UI for Swedish users.

Also applies to: 1009-1016

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/locales/sv-SE.json` around lines 292 - 316, Replace the English values
with Swedish translations for the new localization keys: welcome.title,
welcome.subtitle, welcome.getStarted, welcome.customize,
welcome.recommendedLabel, tabs.basic, tabs.advanced, tabs.appearance,
appearance.title, appearance.themePresets, appearance.accentColor,
appearance.resetTheme and the appearance.themes entries (system, light, dark,
oled, purple, ocean); ensure the string values are proper Swedish phrases and
preserve JSON quoting/commas and key names; also apply the same Swedish
translations to the duplicate appearance/theme keys located in the other block
referenced (the keys around the other appearance section mentioned in the
review).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 372d7733-6451-4341-85f4-a9eeee1afec2

📥 Commits

Reviewing files that changed from the base of the PR and between cb207a8 and 563bc67.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (77)
  • README.md
  • public/locales/ca-ES.json
  • public/locales/cs-CZ.json
  • public/locales/de-DE.json
  • public/locales/en-GB.json
  • public/locales/en-US.json
  • public/locales/en-US.json.d.ts
  • public/locales/es-ES.json
  • public/locales/fa-IR.json
  • public/locales/fr-FR.json
  • public/locales/he-IL.json
  • public/locales/hi-IN.json
  • public/locales/it-IT.json
  • public/locales/ja-JP.json
  • public/locales/ko-KR.json
  • public/locales/nl-NL.json
  • public/locales/pl-PL.json
  • public/locales/pt-BR.json
  • public/locales/ru-RU.json
  • public/locales/sv-SE.json
  • public/locales/tr-TR.json
  • public/locales/uk-UA.json
  • public/locales/vi-VN.json
  • public/locales/zh-CN.json
  • public/locales/zh-TW.json
  • src/components/Inputs/Switch/Switch.tsx
  • src/components/Settings/Settings.css
  • src/components/Settings/Settings.tsx
  • src/components/Settings/SettingsGenerator.tsx
  • src/components/Settings/components/ImportExportSection.tsx
  • src/components/Settings/components/Setting.tsx
  • src/components/Settings/components/SettingSection.tsx
  • src/components/Settings/components/SettingTitle.tsx
  • src/components/Settings/components/SettingsFooter.tsx
  • src/components/Settings/components/TabBar.tsx
  • src/components/Settings/components/WelcomeModal.tsx
  • src/components/Settings/sections/ButtonPlacement.tsx
  • src/components/Settings/sections/FeatureMenuOpenType.tsx
  • src/components/Settings/sections/LanguageSettings.tsx
  • src/components/Settings/sections/OnScreenDisplay.tsx
  • src/components/Settings/sections/YouTubeDataApiKey.tsx
  • src/components/Settings/tabs/AppearanceTab.tsx
  • src/features/_registry/featureMetadataRegistry.ts
  • src/features/automaticTheaterMode/index.metadata.ts
  • src/features/automaticallyDisableAmbientMode/index.metadata.ts
  • src/features/automaticallyDisableAutoPlay/index.metadata.ts
  • src/features/automaticallyDisableClosedCaptions/index.metadata.ts
  • src/features/automaticallyEnableClosedCaptions/index.metadata.ts
  • src/features/automaticallyMaximizePlayer/index.metadata.ts
  • src/features/automaticallyShowMoreVideosOnEndScreen/index.metadata.ts
  • src/features/blockNumberKeySeeking/index.metadata.ts
  • src/features/defaultToOriginalAudioTrack/index.metadata.ts
  • src/features/hideArtificialIntelligenceSummary/index.metadata.ts
  • src/features/hideEndScreenCards/index.metadata.ts
  • src/features/hideEndScreenCardsButton/index.metadata.ts
  • src/features/hideLiveStreamChat/index.metadata.ts
  • src/features/hideMembersOnlyVideos/index.metadata.ts
  • src/features/hideOfficialArtistVideosFromHomePage/index.metadata.ts
  • src/features/hidePaidPromotionBanner/index.metadata.ts
  • src/features/hidePlayables/index.metadata.ts
  • src/features/hidePlaylistRecommendationsFromHomePage/index.metadata.ts
  • src/features/hidePosts/index.metadata.ts
  • src/features/hideScrollBar/index.metadata.ts
  • src/features/hideSidebarRecommendedVideos/index.metadata.ts
  • src/features/hideTranslateComment/index.metadata.ts
  • src/features/monoToStereo/index.metadata.ts
  • src/features/pauseBackgroundPlayers/index.metadata.ts
  • src/features/rememberVolume/index.metadata.ts
  • src/features/restoreFullscreenScrolling/index.metadata.ts
  • src/features/timestampPeek/index.metadata.ts
  • src/hooks/useSectionTitle/context.ts
  • src/hooks/useSectionTitle/provider.tsx
  • src/pages/options/index.css
  • src/pages/popup/Popup.tsx
  • src/pages/popup/PopupView.tsx
  • src/pages/popup/index.css
  • src/utils/uiTheme.ts

Comment on lines +5 to +6
import { useSettings } from "../../Settings/Settings";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Decouple Switch from SettingsContext to prevent runtime coupling.

Switch is in shared inputs but depends on useSettings(). Reusing it outside the settings provider will throw and break rendering. Keep layout direction local/CSS-based instead of context-coupled.

Proposed refactor
-import { useSettings } from "../../Settings/Settings";
@@
-	const { direction } = useSettings();
 	const id = useId();
@@
-			<div
-				className={cn("min-w-0 max-w-[400px]", {
-					"ml-2": direction === "ltr",
-					"mr-2": direction === "rtl"
-				})}
-			>
+			<div className="min-w-0 max-w-[400px]" style={{ marginInlineStart: "0.5rem" }}>

Also applies to: 18-19, 41-44

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Inputs/Switch/Switch.tsx` around lines 5 - 6, The Switch
component currently imports and calls useSettings (creating a runtime dependency
on SettingsContext) — remove that import/usage and decouple layout from context:
modify the Switch component to accept a prop (e.g., layoutDirection or
orientation) or rely solely on CSS classes for row/column layout, update its
prop types/defaultProps accordingly, and remove any conditional logic that reads
settings (references: Switch component and useSettings). Ensure callers that
relied on the context either pass the new prop or let the component use its
default styling, and remove the SettingsContext coupling in the code paths
highlighted (formerly lines 18-19, 41-44).

Comment on lines +7 to +15
export type SwitchProps = {
checked: boolean;
className?: string;
disabled?: boolean;
disabledReason?: string;
label: string;
onChange: (event: ChangeEvent<HTMLInputElement>) => void;
title: string;
};
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 | ⚡ Quick win

Require an accessible name for every switch control.

The checkbox can render without an accessible name when label is empty. Add an explicit ariaLabel prop and wire it to the <input>.

Proposed fix
 export type SwitchProps = {
+	ariaLabel?: string;
 	checked: boolean;
@@
-const Switch: React.FC<SwitchProps> = ({ checked, className, disabled = false, disabledReason, label, onChange, title }) => {
+const Switch: React.FC<SwitchProps> = ({ ariaLabel, checked, className, disabled = false, disabledReason, label, onChange, title }) => {
@@
-				<input checked={checked} className="peer sr-only" disabled={disabled} id={id} onChange={onChange} type="checkbox" />
+				<input
+					aria-label={ariaLabel ?? label}
+					checked={checked}
+					className="peer sr-only"
+					disabled={disabled}
+					id={id}
+					onChange={onChange}
+					type="checkbox"
+				/>

Also applies to: 23-24, 46-48

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Inputs/Switch/Switch.tsx` around lines 7 - 15, The Switch
component can render without an accessible name when label is empty—add an
optional ariaLabel prop to SwitchProps (e.g., ariaLabel?: string) and wire it to
the underlying <input> element inside the Switch component so the input receives
aria-label={ariaLabel ?? label}; update any places where Switch is used to allow
passing ariaLabel when no visible label is present. Ensure the prop name is
SwitchProps.ariaLabel and that the input element uses that fallback logic.

Comment on lines +35 to +36
const [conflicts, setConflicts] = useState<Conflict[]>([]);
const [pendingSettings, setPendingSettings] = useState<configuration | null>(null);
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 | ⚡ Quick win

Preserve imported state:* entries through conflict resolution.

When the import hits the conflict dialog, only pendingSettings survives. The imported feature-state payload is discarded, and onResolve writes config keys only, so any state:* entries in that file are silently lost on the conflict path.

💡 Minimal fix sketch
 const [conflicts, setConflicts] = useState<Conflict[]>([]);
 const [pendingSettings, setPendingSettings] = useState<configuration | null>(null);
+const [pendingState, setPendingState] = useState<Partial<FeatureState> | null>(null);
 ...
-const { conflicts: detectedConflicts, resolved } = detectConflicts(castSettings);
+const stateKeys = metadataRegistry.getAll().map((feature) => `state:${feature.id}` as const);
+const filteredState = Object.fromEntries(
+  Object.entries(stateEntries).filter(([key]) => stateKeys.includes(key as string))
+) as Partial<FeatureState>;
+
+const { conflicts: detectedConflicts, resolved } = detectConflicts(castSettings);
 if (detectedConflicts.length > 0) {
   setPendingSettings(resolved);
+  setPendingState(filteredState);
   setConflicts(detectedConflicts);
   return;
 }
 ...
-const stateKeys = metadataRegistry.getAll().map((feature) => `state:${feature.id}` as const);
-const filteredState = Object.fromEntries(Object.entries(stateEntries).filter(([key]) => stateKeys.includes(key as string)));
 await browser.storage.local.set({ ...filteredConfig, ...filteredState });
 ...
 onCancel={() => {
   setConflicts([]);
   setPendingSettings(null);
+  setPendingState(null);
 }}
 onResolve={(resolvedConfig) => {
   void (async () => {
     const validKeys = new Set(Object.keys(defaultConfiguration));
     const filteredConfig = Object.fromEntries(Object.entries(resolvedConfig).filter(([key]) => validKeys.has(key)));
-    await browser.storage.local.set(filteredConfig);
+    await browser.storage.local.set({ ...filteredConfig, ...(pendingState ?? {}) });
     await refreshSettings();
     setConflicts([]);
     setPendingSettings(null);
+    setPendingState(null);
   })();
 }}

Also applies to: 79-90, 188-205

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Settings/components/ImportExportSection.tsx` around lines 35 -
36, The import flow drops imported "state:*" entries when a conflict opens
because only pendingSettings is preserved; update the conflict handling to also
preserve the imported feature-state payload and apply it on resolution:
introduce or reuse a state-holding variable (e.g., pendingState or extend
pendingSettings to include a stateMap) when building conflicts, ensure the
conflict dialog keeps that payload (alongside conflicts and pendingSettings),
and change onResolve (and the conflict resolution merge logic referenced around
the existing conflicts, pendingSettings and onResolve handlers) to write both
configuration keys and any preserved "state:*" entries back into storage instead
of discarding them.

Comment on lines +75 to +90
<div
className="fixed inset-0 z-50 flex items-start justify-center overflow-y-auto bg-black/60 px-4 py-8 backdrop-blur-sm"
style={{
opacity: visible ? 1 : 0,
transition: "opacity 200ms ease"
}}
>
<div
className="w-full max-w-sm rounded-2xl bg-white shadow-2xl dark:bg-[#1e2021]"
style={{
opacity: visible ? 1 : 0,
transform: visible ? "scale(1)" : "scale(0.92)",
transition: "transform 250ms cubic-bezier(0.34,1.56,0.64,1), opacity 250ms ease"
}}
>
<div className="px-6 pb-5 pt-6">
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 | ⚡ Quick win

Add dialog semantics to the modal container.

The modal should expose role="dialog" and aria-modal="true" plus label/description bindings so screen readers treat it as a proper dialog.

Proposed fix
-		<div
+		<div
+			aria-describedby="welcome-modal-description"
+			aria-labelledby="welcome-modal-title"
+			aria-modal="true"
 			className="fixed inset-0 z-50 flex items-start justify-center overflow-y-auto bg-black/60 px-4 py-8 backdrop-blur-sm"
+			role="dialog"
@@
-						<h2 className="text-lg font-bold text-gray-900 dark:text-white">Welcome to YouTube Enhancer!</h2>
+						<h2 className="text-lg font-bold text-gray-900 dark:text-white" id="welcome-modal-title">Welcome to YouTube Enhancer!</h2>
@@
-					<p className="mb-5 text-sm text-gray-500 dark:text-gray-400">
+					<p className="mb-5 text-sm text-gray-500 dark:text-gray-400" id="welcome-modal-description">
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Settings/components/WelcomeModal.tsx` around lines 75 - 90,
The modal container in WelcomeModal.tsx must be given proper dialog semantics:
on the inner modal wrapper div (the element with className "w-full max-w-sm
rounded-2xl...") add role="dialog" and aria-modal="true", and wire
aria-labelledby and aria-describedby to the IDs of the title and description
elements inside the component (create stable id attributes on the heading and
descriptive text if they don't exist). Ensure the outer overlay remains as-is
for visual backdrop but the accessible label/description IDs point to the
visible title/paragraph so screen readers treat WelcomeModal as a proper dialog.

Comment on lines +164 to +168
const tabs = [
{ icon: <MdTune size={16} />, id: "basic" as TabId, label: "Basic" },
{ icon: <MdBuild size={16} />, id: "advanced" as TabId, label: "Advanced" },
{ icon: <MdPalette size={16} />, id: "appearance" as TabId, label: "Appearance" }
];
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 | ⚡ Quick win

Localize sidebar/tab text instead of hardcoding English.

Tab labels and sidebar copy are currently hardcoded, which causes mixed-language UI in localized builds.

Also applies to: 192-193

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Settings/Settings.tsx` around lines 164 - 168, The tab labels
are hardcoded in the tabs array (variable tabs with entries using TabId "basic",
"advanced", "appearance"); replace the literal English strings ("Basic",
"Advanced", "Appearance") with localized strings via the app's i18n helper
(e.g., t('settings.basic')) or the existing translation function used elsewhere,
and do the same for any other hardcoded sidebar text referenced around the
Settings component (notably the texts at the other mentioned locations near the
component around lines 192-193) so all labels use the translation keys instead
of plain English.

Comment on lines +54 to +55
const url = chrome.runtime.getURL("src/pages/options/index.html");
void chrome.tabs.create({ url });
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 | ⚡ Quick win

Mixing chrome and browser APIs.

The file imports browser from webextension-polyfill (line 4) but uses the chrome API directly here and at line 68. This inconsistency can cause issues in non-Chrome browsers. Use browser.runtime.getURL() and browser.tabs.create() instead.

🔧 Proposed fix
 	function openSettings() {
-		const url = chrome.runtime.getURL("src/pages/options/index.html");
-		void chrome.tabs.create({ url });
+		const url = browser.runtime.getURL("src/pages/options/index.html");
+		void browser.tabs.create({ url });
 		window.close();
 	}

And at line 68:

-					<p className="text-[10px] text-gray-400">v{chrome.runtime.getManifest().version}</p>
+					<p className="text-[10px] text-gray-400">v{browser.runtime.getManifest().version}</p>
📝 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
const url = chrome.runtime.getURL("src/pages/options/index.html");
void chrome.tabs.create({ url });
const url = browser.runtime.getURL("src/pages/options/index.html");
void browser.tabs.create({ url });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/popup/PopupView.tsx` around lines 54 - 55, In PopupView.tsx replace
direct chrome API calls with the webextension-polyfill API: change uses of
chrome.runtime.getURL and chrome.tabs.create to browser.runtime.getURL and
browser.tabs.create (also update the second occurrence around the code at line
68); ensure you import/use the existing browser symbol from
webextension-polyfill and keep the call signatures the same.

Comment thread src/utils/uiTheme.ts
Comment on lines +17 to +28
export function applyTheme(theme: UiTheme, element: HTMLElement): void {
if (theme.preset === "system") {
element.removeAttribute("data-theme");
} else {
element.setAttribute("data-theme", theme.preset);
}
element.style.setProperty("--accent", theme.accentColor);
const hsl = hexToHsl(theme.accentColor);
if (hsl) {
element.style.setProperty("--accent-dark", `hsl(${hsl.h}, ${hsl.s}%, ${Math.max(hsl.l - 10, 0)}%)`);
}
}
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 | 🏗️ Heavy lift

Theme preset accent colors are overridden by inline styles, breaking theme cohesion.

The CSS defines theme-specific accent colors (purple theme uses #a855f7, ocean theme uses #38bdf8), but applyTheme always sets --accent via inline style (line 23), which has higher specificity than the CSS [data-theme] selectors. When a user selects the purple theme preset with the default accent color from DEFAULT_THEME (#2979ff), they'll see the purple background and text colors but a blue accent instead of the intended purple accent.

This creates visual inconsistency where theme presets don't display their designed color schemes.

Recommended fixes:

  1. Map preset to default accent: Create a preset-to-accent mapping and update DEFAULT_THEME or the theme application logic to use theme-specific defaults
  2. Conditional override: Only call setProperty for --accent when accentColor differs from the theme's CSS default
  3. Remove CSS accent definitions: If custom accent always overrides, remove the unused CSS --accent declarations from theme blocks
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/utils/uiTheme.ts` around lines 17 - 28, applyTheme currently always
writes --accent inline causing preset CSS values to be overridden; update
applyTheme (and related DEFAULT_THEME/UiTheme handling) to consult a
preset->accent default map (e.g., presetAccentMap for "purple","ocean", etc.)
and only call element.style.setProperty("--accent", ...) when theme.accentColor
is present and differs from the mapped preset default for theme.preset (for
"system" preserve behavior), otherwise remove the inline --accent so the
[data-theme] CSS defaults take effect; keep using hexToHsl(theme.accentColor) to
set --accent-dark only when you actually set --accent.

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

Labels

None yet

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

2 participants