diff --git a/frontend/css/style.css b/frontend/css/style.css index ed280e6..1a5a28f 100644 --- a/frontend/css/style.css +++ b/frontend/css/style.css @@ -1414,7 +1414,7 @@ body.theme-light .error-toast { cursor: pointer; box-shadow: 0 10px 24px var(--shadow); z-index: 2600; - transition: transform 0.2s ease, background 0.2s ease; + transition: transform 0.2s ease, background 0.2s ease, opacity 0.4s ease; } .mode-toggle-btn:hover { @@ -1516,12 +1516,26 @@ body.feed-mode-open .mode-toggle-btn .icon-svg { background: linear-gradient(to top, rgba(0, 0, 0, 0.75), transparent); color: #fff; pointer-events: none; + transition: opacity 0.4s ease; } .feed-title { font-size: 16px; font-weight: 600; margin: 0 0 4px; + white-space: nowrap; + overflow: hidden; +} + +.feed-title-text { + display: inline-block; + padding-right: 28px; + transform: translateX(0); +} + +.feed-title.is-marquee .feed-title-text { + animation: video-title-marquee var(--marquee-duration, 10s) linear infinite; + will-change: transform; } .feed-uploader { @@ -1602,7 +1616,7 @@ body.feed-mode-open .mode-toggle-btn .icon-svg { justify-content: center; cursor: pointer; z-index: 2600; - transition: background 0.2s ease; + transition: background 0.2s ease, opacity 0.4s ease; } .feed-mute-btn:hover { @@ -1612,3 +1626,48 @@ body.feed-mode-open .mode-toggle-btn .icon-svg { .feed-mute-btn .icon-svg { filter: invert(100%) saturate(0%) !important; } + +/* Per-slide favorite (heart) button on the feed HUD right rail. */ +.feed-fav-btn { + position: absolute; + top: auto; + left: auto; + right: 24px; + bottom: 210px; + width: 44px; + height: 44px; + border-radius: 50%; + border: 1px solid rgba(255, 255, 255, 0.18); + background: rgba(0, 0, 0, 0.5); + color: #fff; + display: flex; + align-items: center; + justify-content: center; + font-size: 22px; + line-height: 1; + cursor: pointer; + z-index: 6; + transition: background 0.2s ease, transform 0.2s ease, opacity 0.4s ease; +} + +.feed-fav-btn:hover { + background: rgba(255, 255, 255, 0.2); + transform: scale(1.05); +} + +.feed-fav-btn.is-favorite { + color: #ff3b30; + border-color: rgba(255, 59, 48, 0.6); + background: rgba(255, 59, 48, 0.18); +} + +/* Reels/TikTok mode: HUD auto-hides after a short idle. Elements stay + interactive (pointer-events untouched) so the buttons keep working while + invisible; any pointer/scroll activity reveals them again (see App.feed). */ +body.feed-hud-idle .feed-info, +body.feed-hud-idle .feed-timeline, +body.feed-hud-idle .feed-mute-btn, +body.feed-hud-idle .feed-fav-btn, +body.feed-hud-idle .mode-toggle-btn { + opacity: 0; +} diff --git a/frontend/js/feed.js b/frontend/js/feed.js index 91f84c2..3b171a6 100644 --- a/frontend/js/feed.js +++ b/frontend/js/feed.js @@ -32,6 +32,26 @@ App.feed = App.feed || {}; let scrollBound = false; let scrollRaf = null; + // HUD auto-hide: the reels HUD fades out after this much inactivity and + // reappears on any pointer movement / tap / scroll. Buttons keep their + // pointer-events while hidden, so they stay clickable even when invisible. + const HUD_IDLE_MS = 1000; + let hudIdleTimer = null; + let hudActivityBound = false; + + const scheduleHudHide = function() { + if (hudIdleTimer) clearTimeout(hudIdleTimer); + hudIdleTimer = setTimeout(() => { + hudIdleTimer = null; + if (state.feedOpen) document.body.classList.add('feed-hud-idle'); + }, HUD_IDLE_MS); + }; + + const wakeHud = function() { + document.body.classList.remove('feed-hud-idle'); + if (state.feedOpen) scheduleHudHide(); + }; + const getScroller = () => document.getElementById('feed-scroll'); const getSentinel = () => document.getElementById('feed-sentinel'); const getTopSpacer = () => document.getElementById('feed-top-spacer'); @@ -47,9 +67,51 @@ App.feed = App.feed || {}; return Math.min(total - 1, Math.max(0, index)); }; + // Remembers playback position per video id so scrolling away and back + // resumes where the user left off. Slides kept in the window are merely + // paused (instant resume); slides whose