From a29c25260609c6639dd844b757a55880a36b8340 Mon Sep 17 00:00:00 2001 From: xRangerDE Date: Mon, 16 Feb 2026 12:06:13 +0100 Subject: [PATCH] Use real flag icons in language picker and polish locale UX (v4.0.3) Replace unreliable emoji flags in the native select with a custom language picker that renders real CSS flag icons, improve retry button naming/tooltips, and keep quick/full e2e coverage green after the UI change. --- typescript-version/package-lock.json | 4 +- typescript-version/package.json | 2 +- typescript-version/scripts/smoke-test-full.js | 14 +++-- typescript-version/src/index.html | 20 +++++-- typescript-version/src/main.ts | 2 +- typescript-version/src/renderer-locale-de.ts | 4 +- typescript-version/src/renderer-locale-en.ts | 4 +- typescript-version/src/renderer-settings.ts | 16 ++++++ typescript-version/src/renderer.ts | 1 + typescript-version/src/styles.css | 56 +++++++++++++++++++ 10 files changed, 106 insertions(+), 17 deletions(-) diff --git a/typescript-version/package-lock.json b/typescript-version/package-lock.json index 0165aa0..3c2921d 100644 --- a/typescript-version/package-lock.json +++ b/typescript-version/package-lock.json @@ -1,12 +1,12 @@ { "name": "twitch-vod-manager", - "version": "4.0.2", + "version": "4.0.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "twitch-vod-manager", - "version": "4.0.2", + "version": "4.0.3", "license": "MIT", "dependencies": { "axios": "^1.6.0", diff --git a/typescript-version/package.json b/typescript-version/package.json index 2b0b7b9..d144708 100644 --- a/typescript-version/package.json +++ b/typescript-version/package.json @@ -1,6 +1,6 @@ { "name": "twitch-vod-manager", - "version": "4.0.2", + "version": "4.0.3", "description": "Twitch VOD Manager - Download Twitch VODs easily", "main": "dist/main.js", "author": "xRangerDE", diff --git a/typescript-version/scripts/smoke-test-full.js b/typescript-version/scripts/smoke-test-full.js index fd3898f..6da9f35 100644 --- a/typescript-version/scripts/smoke-test-full.js +++ b/typescript-version/scripts/smoke-test-full.js @@ -201,7 +201,9 @@ async function run() { const deState = { nav: (document.getElementById('navSettingsText')?.textContent || '').trim(), retry: (document.getElementById('btnRetryFailed')?.textContent || '').trim(), - deFlag: (document.getElementById('languageDeText')?.textContent || '').trim() + deText: (document.getElementById('languageDeText')?.textContent || '').trim(), + deIcon: !!document.querySelector('#langOptionDe .flag-icon.flag-de'), + deActive: !!document.getElementById('langOptionDe')?.classList.contains('active') }; lang.value = 'en'; @@ -210,14 +212,18 @@ async function run() { const enState = { nav: (document.getElementById('navSettingsText')?.textContent || '').trim(), retry: (document.getElementById('btnRetryFailed')?.textContent || '').trim(), - enFlag: (document.getElementById('languageEnText')?.textContent || '').trim() + enText: (document.getElementById('languageEnText')?.textContent || '').trim(), + enIcon: !!document.querySelector('#langOptionEn .flag-icon.flag-en'), + enActive: !!document.getElementById('langOptionEn')?.classList.contains('active') }; checks.language = { deState, enState }; assert(deState.nav.includes('Einstellungen'), 'German language switch failed'); assert(enState.nav.includes('Settings'), 'English language switch failed'); - assert(deState.deFlag.includes('πŸ‡©πŸ‡ͺ'), 'German flag missing'); - assert(enState.enFlag.includes('πŸ‡ΊπŸ‡Έ'), 'English flag missing'); + assert(deState.deIcon, 'German flag icon missing'); + assert(enState.enIcon, 'English flag icon missing'); + assert(deState.deActive, 'German language button did not activate'); + assert(enState.enActive, 'English language button did not activate'); await window.api.saveConfig({ client_id: '', client_secret: '', download_path: tmpDir }); window.showTab('vods'); diff --git a/typescript-version/src/index.html b/typescript-version/src/index.html index 3a2d144..2293545 100644 --- a/typescript-version/src/index.html +++ b/typescript-version/src/index.html @@ -300,9 +300,19 @@
- + +
@@ -345,7 +355,7 @@

Updates

-

Version: v4.0.2

+

Version: v4.0.3

@@ -377,7 +387,7 @@
Nicht verbunden - v4.0.2 + v4.0.3 diff --git a/typescript-version/src/main.ts b/typescript-version/src/main.ts index 1c5bef1..ac4fcd2 100644 --- a/typescript-version/src/main.ts +++ b/typescript-version/src/main.ts @@ -8,7 +8,7 @@ import { autoUpdater } from 'electron-updater'; // ========================================== // CONFIG & CONSTANTS // ========================================== -const APP_VERSION = '4.0.2'; +const APP_VERSION = '4.0.3'; const UPDATE_CHECK_URL = 'http://24-music.de/version.json'; // Paths diff --git a/typescript-version/src/renderer-locale-de.ts b/typescript-version/src/renderer-locale-de.ts index 5bbe3d3..43f0fb4 100644 --- a/typescript-version/src/renderer-locale-de.ts +++ b/typescript-version/src/renderer-locale-de.ts @@ -27,8 +27,8 @@ const UI_TEXT_DE = { designTitle: 'Design', themeLabel: 'Theme', languageLabel: 'Sprache', - languageDe: 'πŸ‡©πŸ‡ͺ Deutsch', - languageEn: 'πŸ‡ΊπŸ‡Έ Englisch', + languageDe: 'Deutsch', + languageEn: 'Englisch', apiTitle: 'Twitch API', clientIdLabel: 'Client ID', clientSecretLabel: 'Client Secret', diff --git a/typescript-version/src/renderer-locale-en.ts b/typescript-version/src/renderer-locale-en.ts index bd40d7a..7b41738 100644 --- a/typescript-version/src/renderer-locale-en.ts +++ b/typescript-version/src/renderer-locale-en.ts @@ -27,8 +27,8 @@ const UI_TEXT_EN = { designTitle: 'Design', themeLabel: 'Theme', languageLabel: 'Language', - languageDe: 'πŸ‡©πŸ‡ͺ German', - languageEn: 'πŸ‡ΊπŸ‡Έ English', + languageDe: 'German', + languageEn: 'English', apiTitle: 'Twitch API', clientIdLabel: 'Client ID', clientSecretLabel: 'Client Secret', diff --git a/typescript-version/src/renderer-settings.ts b/typescript-version/src/renderer-settings.ts index cb559ae..90d5c6a 100644 --- a/typescript-version/src/renderer-settings.ts +++ b/typescript-version/src/renderer-settings.ts @@ -22,6 +22,7 @@ function updateStatus(text: string, connected: boolean): void { function changeLanguage(lang: string): void { const normalized = setLanguage(lang); byId('languageSelect').value = normalized; + updateLanguagePicker(normalized); config.language = normalized; void window.api.saveConfig({ language: normalized }); @@ -40,6 +41,21 @@ function changeLanguage(lang: string): void { } } +function updateLanguagePicker(lang: string): void { + const de = byId('langOptionDe'); + const en = byId('langOptionEn'); + + const isDe = lang === 'de'; + de.classList.toggle('active', isDe); + en.classList.toggle('active', !isDe); + de.setAttribute('aria-pressed', String(isDe)); + en.setAttribute('aria-pressed', String(!isDe)); +} + +function selectLanguageOption(lang: string): void { + changeLanguage(lang); +} + function renderPreflightResult(result: PreflightResult): void { const entries = [ [UI_TEXT.static.preflightInternet, result.checks.internet], diff --git a/typescript-version/src/renderer.ts b/typescript-version/src/renderer.ts index f44af9e..ced3056 100644 --- a/typescript-version/src/renderer.ts +++ b/typescript-version/src/renderer.ts @@ -15,6 +15,7 @@ async function init(): Promise { byId('downloadPath').value = config.download_path ?? ''; byId('themeSelect').value = config.theme ?? 'twitch'; byId('languageSelect').value = config.language ?? 'en'; + updateLanguagePicker(config.language ?? 'en'); byId('downloadMode').value = config.download_mode ?? 'full'; byId('partMinutes').value = String(config.part_minutes ?? 120); diff --git a/typescript-version/src/styles.css b/typescript-version/src/styles.css index e4c4aa3..373eeba 100644 --- a/typescript-version/src/styles.css +++ b/typescript-version/src/styles.css @@ -606,6 +606,62 @@ body { border-color: var(--accent); } +.language-picker { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 8px; +} + +.lang-option { + display: flex; + align-items: center; + gap: 8px; + border: 1px solid rgba(255,255,255,0.14); + border-radius: 6px; + background: var(--bg-main); + color: var(--text); + padding: 9px 10px; + cursor: pointer; + font-size: 13px; +} + +.lang-option:hover { + border-color: rgba(255,255,255,0.26); +} + +.lang-option.active { + border-color: var(--accent); + box-shadow: 0 0 0 1px rgba(145, 70, 255, 0.2); +} + +.flag-icon { + width: 16px; + height: 12px; + border-radius: 2px; + border: 1px solid rgba(0,0,0,0.35); + flex-shrink: 0; + position: relative; + overflow: hidden; +} + +.flag-de { + background: linear-gradient(to bottom, #111 0 33.33%, #dd0000 33.33% 66.66%, #ffce00 66.66% 100%); +} + +.flag-en { + background: repeating-linear-gradient(to bottom, #b22234 0 7.7%, #ffffff 7.7% 15.4%); +} + +.flag-en::before { + content: ''; + position: absolute; + left: 0; + top: 0; + width: 45%; + height: 56%; + background: #3c3b6e; +} + .form-row { display: flex; gap: 10px;