diff --git a/VERSION b/VERSION index 16b7561..568efca 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1300 \ No newline at end of file +1301 \ No newline at end of file diff --git a/backend/static/css/components.css b/backend/static/css/components.css index 91dfd2d..f3e34e7 100644 --- a/backend/static/css/components.css +++ b/backend/static/css/components.css @@ -8307,6 +8307,10 @@ svg.empty-state-icon { .album-title { font-size: var(--text-base); font-weight: 700; color: var(--c-text); } .album-subtitle { font-size: var(--text-xs); color: var(--c-text-muted); margin-top: 2px; } .album-close { background: none; border: none; font-size: 1.5rem; line-height: 1; color: var(--c-text-muted); cursor: pointer; padding: 0 4px; } +.album-head-actions { display: flex; align-items: center; gap: var(--space-2); flex-shrink: 0; } +.album-lang { display: inline-flex; border: 1px solid var(--c-border); border-radius: var(--radius-full); overflow: hidden; background: var(--c-surface-2); } +.album-lang-btn { background: none; border: none; cursor: pointer; padding: 4px 10px; font-size: var(--text-xs); font-weight: 700; color: var(--c-text-muted); line-height: 1.4; transition: background .15s, color .15s; } +.album-lang-btn.is-active { background: var(--c-primary); color: #fff; } .album-list { display: flex; flex-direction: column; gap: var(--space-2); } .album-song { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3); border-radius: var(--radius-md); background: var(--c-surface-2); cursor: pointer; transition: background .15s; } .album-song:active { background: var(--c-border); } diff --git a/backend/static/index.html b/backend/static/index.html index eaec6fc..dd33421 100644 --- a/backend/static/index.html +++ b/backend/static/index.html @@ -86,14 +86,14 @@ Ban Yaro - + - - - - - + + + + + @@ -624,12 +624,12 @@ - - - - - - + + + + + + @@ -639,7 +639,7 @@ - + diff --git a/backend/static/js/app.js b/backend/static/js/app.js index 1a76c84..1b8fc75 100644 --- a/backend/static/js/app.js +++ b/backend/static/js/app.js @@ -3,7 +3,7 @@ Router, State-Management, Navigation, Initialisierung. ============================================================ */ -const APP_VER = '1300'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen +const APP_VER = '1301'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen const APP_VERSION = '1.6.0'; // ← semantische Version, wird bei make release gesetzt window.APP_VER = APP_VER; // global verfügbar für andere Module (z.B. offline-indicator) window.APP_VERSION = APP_VERSION; diff --git a/backend/static/js/worlds.js b/backend/static/js/worlds.js index c3bf861..17946de 100644 --- a/backend/static/js/worlds.js +++ b/backend/static/js/worlds.js @@ -1941,7 +1941,11 @@ window.Worlds = (() => { // zentral in index.html → übersteht Re-Renders & Welt-Wechsel. const _anthem = (() => { const KEY = 'by_anthem_heard'; - const SONGS = [ + const LANG_KEY = 'by_album_lang'; + // EN-Album erst sichtbar, wenn die 7 *-en.mp3 in static/sounds/ liegen. + // Aktivieren: Dateien ablegen → EN_READY = true → make bump → deploy. + const EN_READY = true; // 2026-06-16: 7 *-en.mp3 liegen in static/sounds/ + const SONGS_DE = [ { title: 'Ban Yaro Blues', sub: 'Die Hymne', file: '/sounds/ban-yaro-blues.mp3?v=2' }, { title: 'Ban Yaro Mobil', sub: 'Erste Fahrt im Anhänger', file: '/sounds/ban-yaro-mobil.mp3' }, { title: 'Amy', sub: 'Eine Liebesromanze', file: '/sounds/amy.mp3' }, @@ -1950,6 +1954,19 @@ window.Worlds = (() => { { title: 'Platsch!', sub: 'Ab ins kühle Nass', file: '/sounds/platsch.mp3' }, { title: 'Bester Freund', sub: 'Du und ich', file: '/sounds/bester-freund.mp3' }, ]; + const SONGS_EN = [ + { title: 'Ban Yaro Blues', sub: 'The anthem', file: '/sounds/ban-yaro-blues-en.mp3' }, + { title: 'Ban Yaro Mobile', sub: 'First ride in the trailer', file: '/sounds/ban-yaro-mobil-en.mp3' }, + { title: 'Amy', sub: 'A love duet', file: '/sounds/amy-en.mp3' }, + { title: "At the Groomer's", sub: 'Half the fur, all the energy', file: '/sounds/at-the-groomers-en.mp3' }, + { title: 'Treat Paradise', sub: 'Full bowl, full heart', file: '/sounds/treat-paradise-en.mp3' }, + { title: 'Splash!', sub: 'Into the cool water', file: '/sounds/splash-en.mp3' }, + { title: 'Best Friend', sub: 'You and me', file: '/sounds/best-friend-en.mp3' }, + ]; + let _lang = (() => { + try { return (EN_READY && localStorage.getItem(LANG_KEY) === 'en') ? 'en' : 'de'; } catch (_) { return 'de'; } + })(); + const _songs = () => (_lang === 'en' && EN_READY) ? SONGS_EN : SONGS_DE; let _bound = false, _curIdx = -1; const _audio = () => document.getElementById('anthem-audio'); // Entdeckt? Server-Flag (geräteübergreifend, deploy-fest) ODER lokal (sofort/offline). @@ -1988,17 +2005,18 @@ window.Worlds = (() => { a.addEventListener('play', _sync); a.addEventListener('pause', _sync); a.addEventListener('ended', () => { // automatisch zum nächsten Song - if (_curIdx >= 0 && _curIdx < SONGS.length - 1) _play(_curIdx + 1); + if (_curIdx >= 0 && _curIdx < _songs().length - 1) _play(_curIdx + 1); else { _curIdx = -1; _sync(); } }); } function _play(i) { const a = _audio(); - if (!a || !SONGS[i]) return; + const songs = _songs(); + if (!a || !songs[i]) return; if (i === _curIdx && !a.paused) { a.pause(); return; } // aktiven Song pausieren _curIdx = i; - a.src = SONGS[i].file; + a.src = songs[i].file; a.play().catch(() => {}); _markHeard(); _sync(); @@ -2006,39 +2024,67 @@ window.Worlds = (() => { function _closeAlbum() { document.getElementById('album-modal')?.remove(); } + // Sprache wechseln: aktuelle Wiedergabe stoppen (andere Datei) und Liste neu zeichnen. + function _setLang(l) { + if (l === _lang || !EN_READY) return; + _lang = l; + try { localStorage.setItem(LANG_KEY, l); } catch (_) {} + const a = _audio(); if (a) a.pause(); + _curIdx = -1; + _fillAlbum(); + _sync(); + } + + // Inhalt des Sheets (neu) rendern + innere Controls binden — auch bei Sprachwechsel. + function _fillAlbum() { + const sheet = document.querySelector('#album-modal .album-sheet'); + if (!sheet) return; + const songs = _songs(); + const en = _lang === 'en'; + sheet.innerHTML = ` +
+
+
${en ? 'Ban Yaro — The Album' : 'Ban Yaro — das Album'}
+
${songs.length} ${en ? 'songs · homemade' : 'Songs · selbst gemacht'} 🎸
+
+
+ ${EN_READY ? ` +
+ + +
` : ''} + +
+
+
+ ${songs.map((s, i) => ` +
+ + + ${_esc(s.title)} + ${_esc(s.sub)} + +
`).join('')} +
`; + sheet.querySelector('.album-close').addEventListener('click', _closeAlbum); + sheet.querySelectorAll('.album-lang-btn').forEach(b => + b.addEventListener('click', () => _setLang(b.dataset.lang))); + sheet.querySelectorAll('.album-song').forEach(row => { + const i = parseInt(row.dataset.i, 10); + row.addEventListener('click', () => _play(i)); + row.addEventListener('keydown', e => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); _play(i); } }); + }); + } + function openAlbum() { _markHeard(); if (document.getElementById('album-modal')) return; const ov = document.createElement('div'); ov.id = 'album-modal'; - ov.innerHTML = ` -
-
-
-
Ban Yaro — das Album
-
${SONGS.length} Songs · selbst gemacht 🎸
-
- -
-
- ${SONGS.map((s, i) => ` -
- - - ${_esc(s.title)} - ${_esc(s.sub)} - -
`).join('')} -
-
`; + ov.innerHTML = `
`; document.body.appendChild(ov); ov.addEventListener('click', e => { if (e.target === ov) _closeAlbum(); }); - ov.querySelector('.album-close').addEventListener('click', _closeAlbum); - ov.querySelectorAll('.album-song').forEach(row => { - const i = parseInt(row.dataset.i, 10); - row.addEventListener('click', () => _play(i)); - row.addEventListener('keydown', e => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); _play(i); } }); - }); + _fillAlbum(); _sync(); } @@ -2061,7 +2107,7 @@ window.Worlds = (() => { updateButton(); } - return { heard, toggle: openAlbum, updateButton, initWelt, count: SONGS.length }; + return { heard, toggle: openAlbum, updateButton, initWelt, count: SONGS_DE.length }; })(); function _renderWelt() { diff --git a/backend/static/landing.html b/backend/static/landing.html index e92f60f..3a4af0c 100644 --- a/backend/static/landing.html +++ b/backend/static/landing.html @@ -4,7 +4,7 @@ - + Ban Yaro — Die Hunde-App für Deutschland, Österreich & Schweiz diff --git a/backend/static/sounds/amy-en.mp3 b/backend/static/sounds/amy-en.mp3 new file mode 100644 index 0000000..2d42126 Binary files /dev/null and b/backend/static/sounds/amy-en.mp3 differ diff --git a/backend/static/sounds/at-the-groomers-en.mp3 b/backend/static/sounds/at-the-groomers-en.mp3 new file mode 100644 index 0000000..0d1fe2f Binary files /dev/null and b/backend/static/sounds/at-the-groomers-en.mp3 differ diff --git a/backend/static/sounds/ban-yaro-blues-en.mp3 b/backend/static/sounds/ban-yaro-blues-en.mp3 new file mode 100644 index 0000000..28811ca Binary files /dev/null and b/backend/static/sounds/ban-yaro-blues-en.mp3 differ diff --git a/backend/static/sounds/ban-yaro-mobil-en.mp3 b/backend/static/sounds/ban-yaro-mobil-en.mp3 new file mode 100644 index 0000000..3f4fc3f Binary files /dev/null and b/backend/static/sounds/ban-yaro-mobil-en.mp3 differ diff --git a/backend/static/sounds/best-friend-en.mp3 b/backend/static/sounds/best-friend-en.mp3 new file mode 100644 index 0000000..314e45a Binary files /dev/null and b/backend/static/sounds/best-friend-en.mp3 differ diff --git a/backend/static/sounds/splash-en.mp3 b/backend/static/sounds/splash-en.mp3 new file mode 100644 index 0000000..e732615 Binary files /dev/null and b/backend/static/sounds/splash-en.mp3 differ diff --git a/backend/static/sounds/treat-paradise-en.mp3 b/backend/static/sounds/treat-paradise-en.mp3 new file mode 100644 index 0000000..c08deaa Binary files /dev/null and b/backend/static/sounds/treat-paradise-en.mp3 differ diff --git a/backend/static/sw.js b/backend/static/sw.js index 3eca1b4..5aa9323 100644 --- a/backend/static/sw.js +++ b/backend/static/sw.js @@ -4,7 +4,7 @@ ============================================================ */ // ← EINZIGE Stelle für die Version — STATIC_ASSETS und CACHE_VERSION leiten sich ab -const VER = '1300'; +const VER = '1301'; const CACHE_VERSION = `by-v${VER}`; const CACHE_STATIC = `${CACHE_VERSION}-static`; const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten