From 08c96d590315336af0d87cb0e2cf450bc6abfb93 Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 17 Jun 2026 16:32:39 +0000 Subject: [PATCH] tiktok feed mode --- frontend/css/style.css | 208 +++++++++++++++++++++++++++ frontend/index.html | 14 ++ frontend/js/feed.js | 312 +++++++++++++++++++++++++++++++++++++++++ frontend/js/state.js | 7 +- frontend/js/ui.js | 17 +++ frontend/js/videos.js | 12 ++ 6 files changed, 569 insertions(+), 1 deletion(-) create mode 100644 frontend/js/feed.js diff --git a/frontend/css/style.css b/frontend/css/style.css index 1f39a5d..f165e55 100644 --- a/frontend/css/style.css +++ b/frontend/css/style.css @@ -1359,3 +1359,211 @@ body.theme-light .error-toast { ::-webkit-scrollbar-thumb:hover { background: var(--text-secondary); } + +/* Mode toggle FAB (grid <-> reels) */ +.mode-toggle-btn { + position: fixed; + right: 24px; + bottom: 90px; + width: 52px; + height: 52px; + border-radius: 50%; + border: 1px solid var(--border); + background: var(--bg-secondary); + backdrop-filter: blur(12px); + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + box-shadow: 0 10px 24px var(--shadow); + z-index: 2600; + transition: transform 0.2s ease, background 0.2s ease; +} + +.mode-toggle-btn:hover { + background: var(--bg-tertiary); + transform: scale(1.06); +} + +.mode-toggle-btn:active { + transform: scale(0.94); +} + +body.feed-mode-open .mode-toggle-btn { + background: rgba(0, 0, 0, 0.55); + border-color: rgba(255, 255, 255, 0.18); +} + +body.feed-mode-open .mode-toggle-btn:hover { + background: rgba(255, 255, 255, 0.2); +} + +body.feed-mode-open .mode-toggle-btn .icon-svg { + filter: invert(100%) saturate(0%) !important; +} + +/* Reels-style scrollable feed */ +.feed-view { + display: none; + position: fixed; + inset: 0; + background: #000; + z-index: 2500; +} + +.feed-view.open { + display: block; +} + +.feed-scroll { + width: 100%; + height: 100%; + overflow-y: scroll; + scroll-snap-type: y mandatory; + scrollbar-width: none; +} + +.feed-scroll::-webkit-scrollbar { + display: none; +} + +.feed-sentinel { + height: 1px; +} + +.feed-slide { + position: relative; + width: 100%; + height: 100vh; + height: 100dvh; + scroll-snap-align: start; + scroll-snap-stop: always; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; + background: #000; +} + +.feed-poster, +.feed-video { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + object-fit: contain; +} + +.feed-poster { + transition: opacity 0.2s ease; +} + +.feed-slide.is-loaded .feed-poster { + opacity: 0; +} + +.feed-info { + position: absolute; + left: 0; + right: 0; + bottom: 0; + padding: 24px 20px 44px; + background: linear-gradient(to top, rgba(0, 0, 0, 0.75), transparent); + color: #fff; + pointer-events: none; +} + +.feed-title { + font-size: 16px; + font-weight: 600; + margin: 0 0 4px; +} + +.feed-uploader { + font-size: 13px; + opacity: 0.8; + margin: 0; +} + +.feed-timeline { + position: absolute; + left: 0; + right: 0; + bottom: 4px; + height: 28px; + display: flex; + align-items: center; + padding: 0 16px; + cursor: pointer; + touch-action: none; + z-index: 5; +} + +.feed-timeline-track { + position: relative; + width: 100%; + height: 3px; + background: rgba(255, 255, 255, 0.3); + border-radius: 999px; + overflow: visible; + transition: height 0.15s ease; +} + +.feed-timeline:hover .feed-timeline-track, +.feed-timeline.is-scrubbing .feed-timeline-track { + height: 5px; +} + +.feed-timeline-fill { + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 0%; + background: #fff; + border-radius: 999px; +} + +.feed-timeline-handle { + position: absolute; + top: 50%; + left: 0%; + width: 11px; + height: 11px; + margin-left: -5.5px; + background: #fff; + border-radius: 50%; + transform: translateY(-50%) scale(0); + transition: transform 0.15s ease; + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); +} + +.feed-timeline:hover .feed-timeline-handle, +.feed-timeline.is-scrubbing .feed-timeline-handle { + transform: translateY(-50%) scale(1); +} + +.feed-mute-btn { + position: fixed; + right: 24px; + bottom: 154px; + width: 44px; + height: 44px; + border-radius: 50%; + border: 1px solid rgba(255, 255, 255, 0.18); + background: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + z-index: 2600; + transition: background 0.2s ease; +} + +.feed-mute-btn:hover { + background: rgba(255, 255, 255, 0.2); +} + +.feed-mute-btn .icon-svg { + filter: invert(100%) saturate(0%) !important; +} diff --git a/frontend/index.html b/frontend/index.html index 5735bdf..c0574bb 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -126,6 +126,19 @@ + + + +