Releases: kernoeb/secousse
Secousse v0.3.5
Fix
- Chat reconnection cascade. A duplicate
chat-disconnectedevent from the Rust side was triggering parallel reconnect attempts, each one aborting the previous (single-slot chat) and re-emitting another disconnect — snowballing into seconds-long chat freezes whenever Twitch reset the WebSocket. Fixed by emitting once and coalescing duplicates on the frontend.
Full changelog: v0.3.4...v0.3.5
Secousse v0.3.4
Player
- Fix 1-2s black screen at stream start — the loading spinner now stays visible until the first frame is actually decoded, not just when the HLS manifest is parsed.
- Player overlay, cursor and the open-chat button now auto-hide when the pointer is idle for cleaner viewing.
Chat
- Open-chat button repositions correctly when the navbar is hidden in fullscreen.
Secousse v0.3.3
HLS streaming
- Low-latency parity with Twitch web via progressive prefetch loader — playlist
#EXT-X-TWITCH-PREFETCHtags are rewritten into standard#EXTINFpairs and segments stream chunk-by-chunk into MSE while the encoder is still producing them. - Bandwidth/ABR fix: TTFB is now recorded at headers received instead of after the body is fully read, preventing inflated bandwidth estimates that pinned ABR to the top quality level.
Performance
- Repaints isolated per chat/tile and the video element promoted to its own GPU layer on WebKit — smoother scrolling and fewer dropped frames in multi-stream grid.
- Heavy consumers (chat, emotes, stream info) are deferred when focus shifts between grid tiles so the focus switch itself stays responsive.
Windows
- Updater no longer auto-relaunches Secousse after installing on close — the NSIS installer's
/Rflag is now suppressed via a custominstallerHooks.
Misc
- Dependencies bumped to latest compatible versions; Cargo.lock realigned with manifest constraints.
Secousse v0.3.2
UI improvements
- macOS title bar: forced to dark and matched to the navbar color (no more inconsistency with a light system theme).
- Collapsed sidebar: content re-centered and scrollbar cleanly hidden.
- Navbar / chat: removed the misleading "clickable" affordance on the navbar avatar and the user icon in chat.
Full changelog: v0.3.1...v0.3.2
Secousse v0.3.1
Fixes
-
Title bar followed the Windows theme/accent color, not the app's dark UI. On Windows 11, the native title bar inherits the OS theme and, if the user has enabled "Show accent color on title bars and window borders" in Personalization, that accent paints over even the dark theme. Tauri's
.theme(Theme::Dark)only affects the WebView'sprefers-color-scheme, not the native chrome. We now callDwmSetWindowAttributedirectly withDWMWA_USE_IMMERSIVE_DARK_MODE,DWMWA_CAPTION_COLOR, andDWMWA_TEXT_COLORon every window creation (main + pop-outs) so the title bar stays dark regardless of OS theme or accent color. -
Sidebar scrollbar overlapped channel avatars on Windows. The default Windows scrollbar rendered as an inset overlay and stole pixels from the rightmost column when the followed-channels list grew long. The scrollbar now auto-hides until hover so avatars stay fully visible.
Full changelog: v0.3.0...v0.3.1
Secousse v0.3.0
Features
-
Silent auto-update. Secousse now checks for new versions on launch and downloads them in the background, then applies the update only when you quit the app. No mid-session restart, no popup. macOS/Linux users will see the new version the next time they launch; on Windows the NSIS installer runs during the quit you already initiated.
-
Real native fullscreen with a global toggle for the multi-stream grid. Fullscreen used to be a CSS
fixed inset-0overlay that left the OS window normal — broken with multi-monitor, broken on Windows. Now it calls Tauri'ssetFullscreenAPI so the OS window itself goes fullscreen on macOS/Windows/Linux. The navbar, sidebar, stream info panel and pop-out header all hide in that state; chat stays available. A dedicated toolbar above the multi-stream grid hosts the global Fullscreen button so it isn't mistaken for a per-tile control. -
Chat scrollbar hides while auto-following live messages. The thin scrollbar in chat only appears once you've actually scrolled away from the bottom. The gutter is still reserved so content doesn't shift when the scrollbar toggles in/out.
Fixes
-
Trackpad rubber-band on the page root felt out of place (macOS). The app has its own internal scrollable panels and the page itself never needed to scroll, but the trackpad bounce was hitting the root scroller anyway. The
htmlelement is now locked withoverflow: hidden+overscroll-behavior: none, with body pinned to a fixed viewport height so flex children size correctly. -
Native Windows scrollbar in the sidebar was visually jarring. A new
.scrollbar-thinutility is applied to every scrollable container, and a parasitic horizontal scroll axis in the followed-channels column is clipped (overflow-x-hidden+min-w-0). -
Collapsed-sidebar channel tooltip was invisible. It was clipped by the scroll container's
overflow-y-autoand never rendered. Now usesposition: fixedwith hover-timegetBoundingClientRect()so it escapes the clip.
This is the seed release for the updater — users on v0.2.1 must install v0.3.0 manually, but from v0.3.0 onward updates land automatically.
Full changelog: v0.2.1...v0.3.0
Secousse v0.2.1
Fixes
- Chat listener leak that cascaded into a WebSocket storm. The disconnect listener in
useChatregistered its Tauri handler via an asyncawait listen(...)whose unlisten function was stored in a ref. If the effect's cleanup ran before that promise resolved (typical when switching focus rapidly inside the multi-stream grid), the cleanup saw a null ref and did nothing — the listener slot was then overwritten by the next effect run and the original handler leaked. Each leaked listener also receivedchat-disconnectedevents and scheduled its own 2-second-delayedconnect_to_chat, so a single Twitch-side disconnect produced N concurrent reconnect invocations on the same channel. Symptoms in the logs were repeatedChat disconnected from: <channel>entries from distinctuser-script:NNslots and floods of duplicateUSERNOTICEmessages; on screen this could degrade into the WebView going black during chat scroll under load. Track the unlisten in a local variable with acancelledflag so an in-flightlisten()is torn down as soon as it resolves.
Full changelog: v0.2.0...v0.2.1
Secousse v0.2.0
Highlights
Watch up to four streams at once in a split-screen grid, or detach any stream into its own always-on-top window. Audio routes automatically to whichever tile you click — no more juggling tabs during raids, tournaments, or multi-POV events.
Features
- Multi-stream grid (1–4 tiles). Shift+click a channel in the sidebar or the browse grid to add it to a CSS-grid layout. Click any tile to focus it: the focused tile owns the audio, the chat, and the stream info panel. Per-tile remove (×) and pop-out buttons in the hover overlay. Layout adapts automatically: 1 col for solo, 2×1 for duos, 3×1 / 2×2 beyond.
- Pop-out windows. Detach a stream into an independent Tauri window via the ↗ icon (sidebar, browse card, or grid tile). Pop-out has a minimal header with toggleable chat and an always-on-top pin. Re-opening a pop-out for the same channel focuses the existing window instead of duplicating it. Each pop-out tracks the current channel in its URL, so reloads stay on the same stream.
- Persistent grid. Your tile layout and focused tile are restored on launch. Single-stream usage stays identical — the grid is
[channel]under the hood.
Under the hood
- Per-channel emote cache (LRU, cap 5). Switching focus inside a grid no longer re-fetches 7TV/BTTV/FFZ/Twitch emote lists. Inflight requests are deduped so two tiles loading the same channel only hit the API once.
- VideoPlayer gains
compactandforceMutedmodes. Non-focused tiles render without the fullscreen button and ignore the persisted volume/mute preferences (so flipping focus between tiles doesn't silently clobber your saved volume). Mute button and slider are disabled with a clear visual treatment on muted tiles. useUserInfohook extracted fromApp.tsx. Each tile owns its own polling loop, decoupling the focused tile's lifecycle from the rest of the grid.- Rust backend: new
open_popoutcommand,popout-*capability glob,set_always_on_topandclosepermissions.
Full changelog: v0.1.5...v0.2.0
Secousse v0.1.5
Highlights
Chat scroll stability fix for users who read older messages while a busy chat keeps flowing in, plus dependency refresh across the stack.
Fixes
- Stable chat scroll while reading history. When you scroll up to read older messages, the chat now snapshots the message array and renders that fixed copy until you return to the bottom. Previously, virtua's lazy item measurements made the scroll-offset compensation drift every time the cap-300 buffer dropped a message from the front, jerking the viewport upward. Re-follow is triggered from
onScrollEnd, which correctly handles macOS trackpad momentum.
Under the hood
- Bumped Tauri to 2.11,
wry0.54 → 0.55,tokio1.50 → 1.52, plus full minor/patch refresh of all Rust transitives (tray-icon,rustls,reqwest, …). - Bumped Vite to 8.0.12,
@vitejs/plugin-react5 → 6, React 19.2.6, Tailwind 4.3, TypeScript 6.0.3. - Added a dev-only chat scroll/freeze test harness for reproducing the read-while-streaming regression locally.
Full changelog: v0.1.4...v0.1.5
Secousse v0.1.4
Highlights
This release fixes a critical WKWebView freeze that affected anyone watching busy chats with animated emotes for more than a few minutes. Memory under stress drops from 2.7 GB peak to ~600 MB.
Fixes
- Eliminate emote-induced freeze on macOS. Animated emotes now decode in Rust and render via
<canvas>, side-stepping the WKWebView per-<img>ImageFrameWorkQueueallocation that saturates the macOS dispatch pool (~512 workers/process) and freezes the app within minutes on busy chats. Stress-tested at 80 msg/s × 6 emotes/msg with 581 unique emotes (242 animated): peak RSS 600 MB vs 2.7 GB before, steady ~200 MB, no leak. - Fix Sidebar crash on banned channels in top streams. Twitch's GraphQL response occasionally includes top-streams entries with a null broadcaster (banned/suspended channels still in the cache); these now get filtered out before reaching the UI.
Under the hood
- New Rust command
decode_emote(WebP / GIF / PNG / JPEG, animated or static), downscaled to 48 px (pixel-perfect on Retina), serialized as a single binary blob over IPC. - New
<EmoteImg>React component with global cache (32 MB byte-budgeted LRU), LIFO decode queue (concurrency 6, prioritizes recently-mounted emotes), and a sharedrequestAnimationFrameorchestrator that dedupes per-URL frame indexing across all live canvases.
Full changelog: v0.1.3...v0.1.4