From dfa6a340935da18853a91cea65643f1a71c161eb Mon Sep 17 00:00:00 2001 From: Jan Svabenik Date: Thu, 28 May 2026 19:13:16 +0200 Subject: [PATCH] fix(ui): iOS 9 emoji fallback -- use ASCII symbols where glyphs are missing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Media-control emoji (U+23EE U+23F8 U+23F9 U+23ED) and ๐Ÿ”Š ๐Ÿ”‡ ๐Ÿ“‹ ๐Ÿšซ are absent from the iOS 9 system font and render as empty boxes on iPad 2. Added isLegacyIOS detection (iOS <= 9 via UA) and a SYM symbol table that picks between emoji (modern) and plain-text fallbacks for iOS 9: prev=<< pause=|| stop=โ–  next=>> volOn=vol volOff=mut playlist=PL skip=โœ• Static buttons replaced at boot; dynamic updates (play/pause, mute) use SYM throughout. Modern browsers completely unaffected. Co-Authored-By: Claude Sonnet 4.6 --- web/static/app.js | 48 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/web/static/app.js b/web/static/app.js index 7463d09..36d189d 100644 --- a/web/static/app.js +++ b/web/static/app.js @@ -29,6 +29,36 @@ function apiFetch(url, opts) { // String.prototype.padStart not available in iOS < 10. function pad2(n) { return n < 10 ? '0' + n : '' + n; } +// iOS version detection โ€” used to pick emoji vs. ASCII symbols. +// Media-control emoji (โฎ โธ โน โญ) and several other glyphs are absent on iOS 9. +var isLegacyIOS = (function() { + var m = navigator.userAgent.match(/OS (\d+)_/); + return /iPad|iPhone/.test(navigator.userAgent) && m && parseInt(m[1], 10) <= 9; +}()); + +// Symbol set: modern emoji for capable browsers, plain text for iOS โ‰ค 9. +var SYM = isLegacyIOS ? { + prev: '<<', + play: 'โ–ถ', // U+25B6 โ€” works on iOS 9 + pause: '||', + stop: 'โ– ', // U+25A0 โ€” works on iOS 9 + next: '>>', + volOn: 'vol', + volOff: 'mut', + playlist:'PL', + skip: 'โœ•' // U+2715 โ€” works on iOS 9 +} : { + prev: 'โฎ', + play: 'โ–ถ', + pause: 'โธ', + stop: 'โน', + next: 'โญ', + volOn: '๐Ÿ”Š', + volOff: '๐Ÿ”‡', + playlist:'๐Ÿ“‹', + skip: '๐Ÿšซ' +}; + // NodeList.prototype.forEach not available in iOS < 10. // Wrap querySelectorAll results in a real Array before iterating. function qsa(selector, root) { @@ -158,7 +188,7 @@ function applyStatus(st) { timeLength.textContent = '-0:00'; } - btnPlay.textContent = st.state === 'playing' ? 'โธ' : 'โ–ถ'; + btnPlay.textContent = st.state === 'playing' ? SYM.pause : SYM.play; winampPlaying = st.state === 'playing'; if (typeof st.volume === 'number') { @@ -170,7 +200,7 @@ function applyStatus(st) { // โ”€โ”€ Controls โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ btnPlay.addEventListener('click', function() { - var playing = btnPlay.textContent === 'โธ'; + var playing = btnPlay.textContent === SYM.pause; send({ cmd: playing ? 'pause' : 'play' }); }); @@ -225,7 +255,7 @@ function updateVolumeFill(muted) { volumeFill.classList.toggle('muted', muted); } function updateMuteBtn(muted) { - btnMute.textContent = muted ? '๐Ÿ”‡' : '๐Ÿ”Š'; + btnMute.textContent = muted ? SYM.volOff : SYM.volOn; btnMute.classList.toggle('muted', muted); } @@ -528,5 +558,17 @@ function scrollToCurrentTrack() { } // โ”€โ”€ Boot โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +// Replace emoji with legacy symbols on iOS 9 where the glyphs are missing. +if (isLegacyIOS) { + $('btn-prev').textContent = SYM.prev; + $('btn-play').textContent = SYM.play; + $('btn-stop').textContent = SYM.stop; + $('btn-next').textContent = SYM.next; + $('btn-mute').textContent = SYM.volOn; + $('btn-show-playlist').textContent = SYM.playlist; + $('btn-kill').textContent = SYM.skip + ' รœberspringen'; +} + connect(); renderFrame();