Commit Graph
1198 Commits
Author SHA1 Message Date
Jannis Braun b6e8aee24b fix: cap markdown images to 400x300 2026-03-24 05:16:56 +01:00
Jannis Braun 7e9020f5a6 fix: cap all embed types to 400px max-width 2026-03-24 05:16:31 +01:00
Jannis Braun e64658bdd5 fix: cap image and video attachments to 400x300 2026-03-24 05:15:51 +01:00
Jannis Braun acb79c9de7 fix: cap GIF-only messages to 250x250 2026-03-24 05:15:10 +01:00
Jannis Braun 69a16ccdd5 fix: preserve hwOverdrive across mid-stream codec restart
stopScreenShare() resets hwOverdrive: false, which clobbers the
user's intent when switching VP9 → H.264 HW mid-stream. Save
and restore the flag around the restart.
2026-03-24 04:35:33 +01:00
Jannis Braun cc30080ba8 chore: sync repo with deployed state — clean up old specs/plans, desktop tweaks 2026-03-24 04:33:23 +01:00
Jannis Braun 46c3838193 fix: reset hwOverdrive in all voice cleanup paths
Add hwOverdrive: false to resetSession(), leaveVoice(), and
handleForceDisconnect() store resets. Add deactivateHwOverdrive()
to disconnect callback and cleanup effect in useLiveKit.
Ensures "resets when stream ends" contract holds for edge cases
like server shutdown, identity collision, and logout.
2026-03-24 04:00:37 +01:00
Jannis Braun 66a0793297 feat: add hwOverdrive to updateActiveTracks effect deps for mid-stream switching 2026-03-24 03:52:20 +01:00
Jannis Braun c6c44d1bdd feat: replace codec pills with VP9/H.264 HW, wire to hwOverdrive 2026-03-24 03:51:22 +01:00
Jannis Braun 23249be956 feat: integrate hwOverdrive into screen share start/stop/detect
- buildScreenShareOptions reads hwOverdrive from voiceStore instead of config.codec; both VP9 and H.264 paths now always get a VP8 SIMULCAST backup
- startScreenShare activates SDP override before WebRTC negotiation and deactivates on failure
- stopScreenShare and handleScreenShareUnpublished both deactivate the override and reset hwOverdrive: false
- scheduleEncoderDetection checks encoderImplementation in WebRTC stats after 4s and shows a warning toast if OpenH264 (software) is detected
2026-03-24 03:49:57 +01:00
Jannis Braun 94e955989a feat: remove codec from ScreenShareConfig, add transient hwOverdrive
VP9 is now always the default codec, so the persisted codec field is
removed. hwOverdrive is a transient boolean (excluded from partialize)
that drives hardware H.264 on demand without surviving page reload.
Persist version bumped 12→13 with migration to strip stale codec field.
2026-03-24 03:45:10 +01:00
Jannis Braun 0b49300d86 feat: add hwOverdrive SDP profile override module 2026-03-24 03:39:43 +01:00
Jannis Braun 1bdd9a387a revert: remove ineffective GPU encoding flags from Electron
Hardware H.264 encoding in Electron's WebRTC requires SDP profile
negotiation changes (NVENC rejects Constrained Baseline), not
command-line flags. VP9 via libvpx is the correct default — proven
by empirical benchmarks (CS2: VP9 60fps vs H.264 30fps).
2026-03-24 02:00:43 +01:00
Jannis Braun 6844f8d8fd fix: clean up GPU flags — hardware H.264 encoding not available on Win/Mac
AcceleratedVideoEncoder only enables VA-API encoding on Linux.
Windows/macOS hardware H.264 encoding in Chromium's WebRTC is not
reliably controllable — VP9 via libvpx remains the recommended codec.
2026-03-24 01:55:18 +01:00
Jannis Braun 07ac9cf505 fix: pass screenShareEncoding to override LiveKit's 15fps default
LiveKit uses screenShareEncoding (not videoEncoding) for screen share
tracks. Without it, both primary and backup fall back to the default
ScreenSharePresets.h1080fps15 (15fps). Primary was masked by our
overdrive mechanism, but backup had no overdrive and was stuck at 15fps.
2026-03-24 01:21:16 +01:00
Jannis Braun e53d3bcb98 fix: switch backup codec to VP8@30fps, dim H.264 pill with warning
- Backup codec changed from H.264 (OpenH264, slow) to VP8 (libvpx,
  fast) — same encoder family as VP9, lower dual-encode overhead
- Backup capped at 30fps with proportional bitrate to keep CPU
  overhead low during SIMULCAST dual-encoding
- H.264 pill visually dimmed with amber warning when selected
2026-03-24 01:14:47 +01:00
Jannis Braun a54dd9084a feat: show encoder implementation in Connection Info, VP9 recommended hint
- Connection Info now shows encoder impl (e.g. libvpx, OpenH264,
  ExternalEncoder) on send-side video tracks alongside quality
  limitation reason
- Amber warning "VP9 is recommended" when H.264 is selected
2026-03-24 01:06:19 +01:00
Jannis Braun 65a1fc1402 fix: VP9 default for both modes, clean up codec pill UI
VP9 empirically outperforms H.264 in gaming (Chrome may not use
NVENC for screen share H.264). Codec pills now match other pill
styles — simple labels, no description text. Mode change no longer
auto-switches codec; user's codec choice is independent.
2026-03-24 00:59:04 +01:00
Jannis Braun 2b2e1c7386 fix: remove preventDefault on touchstart that killed all mobile taps
Calling e.preventDefault() on touchstart inside [data-context-menu]
elements prevented the browser from synthesizing click events
(touchstart → touchend → click chain). This broke tapping on spaces,
channels, DMs, and every other surface with data-context-menu.

Text selection prevention is already handled by CSS user-select: none
on [data-context-menu] elements — the JS preventDefault was redundant
and destructive. Reverted touchstart listener back to passive.
2026-03-24 00:14:09 +01:00
Jannis Braun 8897a5894c feat: smart codec defaults + mid-stream codec switching
- Gaming mode defaults to H.264 (NVENC hardware encoding, zero CPU)
- Text mode defaults to VP9 (better compression, CPU is free)
- Changing mode auto-updates codec to smart default
- User can manually override codec via UI pills
- Changing codec mid-stream auto-restarts the stream (~1s interruption)
2026-03-23 23:35:33 +01:00
Jannis Braun 894ce68194 feat: add manual codec selection (VP9/H.264) to stream settings
VP9 is default — better quality per bit with H.264 SIMULCAST backup
for Safari. H.264 option available for zero-CPU hardware encoding.
Codec choice is independent of gaming/text mode, which still controls
contentHint and degradationPreference.
2026-03-23 22:44:31 +01:00
Jannis Braun e7a18bd28c fix: screenshare codec fallback, native FPS, and connection info
- Gaming mode uses H.264 primary (hardware NVENC encoding, zero CPU
  impact on games). Text mode uses VP9 primary with H.264 backup
  and SIMULCAST policy for Safari compatibility.
- Fix native mode starting at 30fps by decoupling frameRate constraint
  from resolution constraint in both screenShare.ts overdrive and
  useLiveKit.ts updateActiveTracks.
- Filter paused backup codec tracks in Connection Info stats so dead
  0kbps entries don't show alongside active codec tracks.
2026-03-23 22:10:16 +01:00
Jannis Braun 7693cc737e fix: send voice_leave WS message from mobile disconnect handlers
MobileVoiceFullScreen and MobileVoiceMiniBar were missing voice_leave
(and dm_call_end) WS messages in their disconnect handlers, leaving
the server showing stale voice state. Now matches VoiceControls pattern.
2026-03-23 21:18:34 +01:00
Jannis Braun 0b7206487e chore: delete dead useLongPress.ts hook
Zero imports — useGlobalLongPress in ContextMenuRenderer handles
all long-press detection at the document level.
2026-03-23 21:12:32 +01:00
Jannis Braun 9f8a4ffa90 fix: prevent native text selection on mobile long-press
iOS triggered both the app context menu and native text selection overlay
on long-press. Three-layer fix: CSS user-select:none on buttons/[role=button]/
[data-context-menu] elements; non-passive touchstart listener with conditional
preventDefault on context-menu targets; removeAllRanges() at timer fire to
clear any residual selection. Added data-context-menu to all mobile surfaces
that use onContextMenu handlers.
2026-03-23 21:12:06 +01:00
Jannis Braun 161e876df8 fix: IncomingCallModal calls connectFn directly for iOS gesture context 2026-03-23 21:09:37 +01:00
Jannis Braun 241451ca18 feat: gesture-aware voice connection, remove AppLayout auto-connect
Replace the useEffect-based auto-connect pattern in AppLayout with
direct connect/disconnect calls from user gesture contexts. This is
required for iOS (AudioContext.resume + getUserMedia must happen in
a gesture handler) and aligns with tightening autoplay policies on
desktop browsers.

Architecture:
- voiceStore gains connectFn/disconnectFn refs, registered by AppLayout
  from the single useLiveKit() instance.
- All voice join paths (ChannelSidebar, MobileSpacesScreen, MainContent,
  voice_moved WS handler) pass connectFn to joinVoiceChannel().
- All disconnect paths (VoiceControls, voiceActions, MobileVoiceFullScreen,
  MobileVoiceMiniBar, dm_call_ended/rejected WS handlers, ready handler)
  call disconnectFn() directly.
- dm_call_accepted WS handler calls connectFn() to initiate the DM call
  LiveKit connection.
- The 55-line auto-connect useEffect and lastAttemptedRef are removed.
2026-03-23 21:06:19 +01:00
Jannis Braun 8aa66816d8 feat: add MobileVoiceJoinSheet bottom sheet for voice preview 2026-03-23 20:59:26 +01:00
Jannis Braun acf6f6fe99 refactor: align MobileVoiceFullScreen voice state resolution
Brings mute/deafen state resolution in line with VoiceChannel.tsx:
- Use LiveKit participant state (then WS fallback) for mute/deafen instead of only voiceUserStates
- Use local isMuted/isDeafened store values for the current user (self)
- Add spaceMutedUserIds, spaceDeafenedUserIds, permissionMutedUserIds checks
- Render deafened indicator (headphones-slash) when user is deafened vs muted
- Add LiveKit participant username and userId as terminal fallbacks in display name chain
2026-03-23 20:55:51 +01:00
Jannis Braun cd30774596 feat: show inline voice users in mobile sidebar 2026-03-23 20:52:12 +01:00
Jannis Braun 7c1ec5e846 refactor: use VoiceUserRow in desktop VoiceChannel 2026-03-23 20:46:47 +01:00
Jannis Braun ba11eefb0c fix: add isUnwatchedCamera prop and remove unused React import
Adds camera slash overlay for unwatched cameras, matching
VoiceChannel.tsx. Removes unnecessary React import (Vite
automatic JSX transform).
2026-03-23 20:44:44 +01:00
Jannis Braun 00c5757311 feat: add VoiceUserRow pure UI component 2026-03-23 20:40:29 +01:00
Jannis Braun f27140f1c8 feat: add space folder support to MobileSpacesScreen
Folder-aware layout resolution, folder icons in space strip,
bottom sheet folder flyout with rename/color/ungroup management.
Full folder CRUD via context menus: create, move to, remove from.
2026-03-23 19:34:24 +01:00
Jannis Braun 0d9ee44eee feat: add federation badges to mobile surfaces
Globe/amber badge on federated space icons, @domain on federated
DM users, home instance on the You screen profile card.
2026-03-23 19:27:39 +01:00
Jannis Braun 32be1bceab feat: add voice participant context menus on mobile
Long-press on voice participants shows moderation items (space mute,
deafen, disconnect, move) and local audio controls (mute, volume).
Skips self and DM calls.
2026-03-23 19:25:41 +01:00
Jannis Braun 937c4b68b2 feat: add leave group context menu on MobileDmsScreen
Long-press on group DMs shows Leave Group option.
2026-03-23 19:24:17 +01:00
Jannis Braun f84b1726bf feat: add context menus to MobileSpacesScreen
Add onContextMenu handlers for space icons (invite, folder management,
transfer, leave), channel items (settings, delete), and category headers
(settings, delete). Includes full folder management handlers.
2026-03-23 19:21:14 +01:00
Jannis Braun 8f104e1c5b fix: derive spaceId from voice channel in buildVoiceModMenuItems
Use channelToSpaceMap.get(channelId) for permission checks instead of
currentSpaceId from the navigation store. On mobile, UI navigation
decouples from the active voice session.
2026-03-23 16:32:47 +01:00
Jannis Braun fd6be8c713 fix: suppress native iOS callout on mobile long-press
Add -webkit-touch-callout: none on mobile viewports to prevent the
native iOS copy/lookup popup from racing with our context menu.
Clear any text selection when the long-press timer fires.
2026-03-23 15:26:09 +01:00
Jannis Braun ce33d8f7ea feat: global long-press context menus for all mobile touch targets
Add useGlobalLongPress to ContextMenuRenderer that detects 500ms hold
with <10px movement and dispatches a synthetic contextmenu event on the
touched element. All existing onContextMenu handlers (spaces, channels,
voice users, messages, etc.) now work on touch devices automatically.

Remove per-message useLongPress integration from Message.tsx since the
global handler covers it.
2026-03-23 15:12:58 +01:00
Jannis Braun 05a4b23d88 fix: prevent scroll snap-back during mobile momentum scrolling
ResizeObserver and media load callbacks were using isNearBottomRef
(5000px threshold) to decide whether to auto-scroll to bottom. On
mobile, inertial flick scrolling hadn't moved far enough to clear
that threshold before the observer fired, snapping the user back.

Now these callbacks check actual scroll distance at fire time with
a tight 150px threshold — only auto-scrolls if truly at the bottom.
2026-03-23 15:04:44 +01:00
Jannis Braun 2c372a5d34 fix: three mobile bugs — scroll, GeneralPanel loading, activity tab
1. Chat scroll: add flex flex-col to MobileChatScreen messages wrapper
   so MessageList's flex-1 actually works (parent must be flex container)

2. Instance General panel: add fetchInstanceSettings/fetchStreamingLimits
   to MobileInstancePanel mount (mirrors desktop InstancePanel useEffect)

3. Friends activity: add Activity tab to mobile FriendsPage showing
   friends grouped by activity status (active, online, offline)
2026-03-23 14:56:07 +01:00
Jannis Braun f90cf91760 fix: immediately clean up voice state when voice-active socket disconnects
Previously, voice cleanup only happened in finalizeDisconnect after a
5-second grace period, and only when ALL connections closed. If a user
was logged in on multiple devices and closed the one in voice, the
voice state was never cleaned up — creating a permanent ghost in the
channel sidebar.

Now removeConnection checks if the closing socket is the voice-active
one (tracked via voiceWs map) and immediately cleans up voice state,
broadcasts the leave event, and notifies remaining tabs.
2026-03-23 14:30:57 +01:00
Jannis Braun 319832ebc8 feat: clear voiceWs on all voice leave paths 2026-03-23 14:27:42 +01:00
Jannis Braun cc10876b93 feat: wire setVoiceWs into voice join and DM call handlers
Adds device-switch guardrail to handleVoiceJoin: if the user already
has a voice session on a different WebSocket, that old socket receives
a voice_disconnected/displaced event before the new session takes over.
Also binds voiceWs on the caller socket in handleDmCallStart and on
the acceptor socket in handleDmCallAccept.
2026-03-23 14:25:42 +01:00
Jannis Braun 357ba8b4d9 refactor: thread ws parameter through handleClientEvent to voice handlers 2026-03-23 14:23:48 +01:00
Jannis Braun 80c32ae1e6 feat: add long-press context menus for mobile messages
Create useLongPress hook with 500ms delay, 10px movement cancellation
(Euclidean distance), and ghost click suppression. Integrate into
Message component to trigger the existing context menu bottom sheet
on touch devices.
2026-03-23 14:22:38 +01:00
Jannis Braun 876326e7b7 feat: add voiceWs map and accessors to ConnectionManager 2026-03-23 14:22:28 +01:00
Jannis Braun 77a1a7920c feat: add standalone MobileMembersScreen replacing blank member panel
Create MobileMembersScreen that renders space members grouped by role
without depending on desktop memberListOpen flag or hidden md:block.
Add members button to voice full screen header for space channels.
2026-03-23 14:20:06 +01:00