diff --git a/public/css/main.css b/public/css/main.css index bcade6f..e54b969 100644 --- a/public/css/main.css +++ b/public/css/main.css @@ -4,6 +4,10 @@ /* CSS Variables */ :root { + /* iOS Safari toolbar compensation (set dynamically by JS) */ + --ios-ui-bottom: 0px; + + /* Colors */ --color-bg-primary: #0a0a0f; --color-bg-secondary: #12121a; @@ -314,6 +318,15 @@ h4 { position: relative; } +/* Fix scroll sidebar on desktop (Safari / flex bug) */ +.main-content, +.page, +.home-layout, +.channel-sidebar, +.channel-list { + min-height: 0; +} + /* Floating Channel Toggle Button (Mobile) */ .channel-toggle-btn { display: none; @@ -673,6 +686,15 @@ h4 { /* Allow flex child to shrink */ } +/* Live TV player controls overlay - iOS Safari safe positioning */ +#player-controls-overlay { + position: absolute; + left: 0; + right: 0; + bottom: calc(env(safe-area-inset-bottom, 0px) + var(--ios-ui-bottom, 0px)); + box-sizing: border-box; +} + /* Favorites */ .group-header.favorites-group { background: linear-gradient(to right, rgba(239, 68, 68, 0.1), transparent); diff --git a/public/index.html b/public/index.html index 517c27c..dd74211 100644 --- a/public/index.html +++ b/public/index.html @@ -3,8 +3,7 @@
- + diff --git a/public/js/components/VideoPlayer.js b/public/js/components/VideoPlayer.js index 5965011..a089dab 100644 --- a/public/js/components/VideoPlayer.js +++ b/public/js/components/VideoPlayer.js @@ -11,6 +11,13 @@ function isMobile() { class VideoPlayer { constructor() { this.video = document.getElementById('video-player'); + + // iOS: ensure inline playback (not fullscreen by default) + if (this.video) { + this.video.setAttribute('playsinline', ''); + this.video.setAttribute('webkit-playsinline', ''); + } + this.container = document.querySelector('.video-container'); this.overlay = document.getElementById('player-overlay'); this.nowPlaying = document.getElementById('now-playing'); @@ -150,6 +157,38 @@ class VideoPlayer { this.controlsOverlay = document.getElementById('player-controls-overlay'); this.loadingSpinner = document.getElementById('player-loading'); + // iOS Safari: detect and compensate for floating bottom toolbar + const updateIosUiBottom = () => { + let uiBottom = 0; + if (window.visualViewport) { + const vv = window.visualViewport; + uiBottom = Math.max(0, window.innerHeight - (vv.height + vv.offsetTop)); + } + document.documentElement.style.setProperty('--ios-ui-bottom', uiBottom + 'px'); + }; + + updateIosUiBottom(); + + if (window.visualViewport) { + window.visualViewport.addEventListener('resize', updateIosUiBottom); + window.visualViewport.addEventListener('scroll', updateIosUiBottom); + } else { + window.addEventListener('resize', updateIosUiBottom); + } + + // iOS: use custom --vh unit to avoid 100vh issues with dynamic toolbar + const isIOS = /iP(hone|ad|od)/.test(navigator.userAgent); + if (isIOS && this.container) { + const vh = window.innerHeight * 0.01; + document.documentElement.style.setProperty('--vh', `${vh}px`); + this.container.style.height = 'calc(var(--vh) * 100)'; + } + + // Apply safe area + iOS toolbar padding to controls overlay + if (this.controlsOverlay) { + this.controlsOverlay.style.paddingBottom = 'calc(env(safe-area-inset-bottom, 0px) + var(--ios-ui-bottom, 0px) + 12px)'; + } + const btnPlay = document.getElementById('btn-play'); const btnMute = document.getElementById('btn-mute'); const btnFullscreen = document.getElementById('btn-fullscreen'); diff --git a/public/js/pages/WatchPage.js b/public/js/pages/WatchPage.js index 05cd2b5..10af703 100644 --- a/public/js/pages/WatchPage.js +++ b/public/js/pages/WatchPage.js @@ -11,6 +11,12 @@ class WatchPage { this.video = document.getElementById('watch-video'); this.overlay = document.getElementById('watch-overlay'); + // iOS: ensure inline playback (not fullscreen by default) + if (this.video) { + this.video.setAttribute('playsinline', ''); + this.video.setAttribute('webkit-playsinline', ''); + } + // Top bar this.backBtn = document.getElementById('watch-back-btn'); this.titleEl = document.getElementById('watch-title'); @@ -90,6 +96,39 @@ class WatchPage { } init() { + // iOS Safari: detect and compensate for floating bottom toolbar + const updateIosUiBottom = () => { + let uiBottom = 0; + if (window.visualViewport) { + const vv = window.visualViewport; + uiBottom = Math.max(0, window.innerHeight - (vv.height + vv.offsetTop)); + } + document.documentElement.style.setProperty('--ios-ui-bottom', uiBottom + 'px'); + }; + + updateIosUiBottom(); + + if (window.visualViewport) { + window.visualViewport.addEventListener('resize', updateIosUiBottom); + window.visualViewport.addEventListener('scroll', updateIosUiBottom); + } else { + window.addEventListener('resize', updateIosUiBottom); + } + + // iOS: use custom --vh unit to avoid 100vh issues with dynamic toolbar + const isIOS = /iP(hone|ad|od)/.test(navigator.userAgent); + const watchVideoSection = document.querySelector('.watch-video-section'); + if (isIOS && watchVideoSection) { + const vh = window.innerHeight * 0.01; + document.documentElement.style.setProperty('--vh', `${vh}px`); + watchVideoSection.style.height = 'calc(var(--vh) * 100)'; + } + + // Apply safe area + iOS toolbar padding to overlay + if (this.overlay) { + this.overlay.style.paddingBottom = 'calc(env(safe-area-inset-bottom, 0px) + var(--ios-ui-bottom, 0px) + 12px)'; + } + // Back button this.backBtn?.addEventListener('click', () => this.goBack());