I did some more thinking about the whole trait/boundary stuff and reallized that we don't need the GUI to handle the generic-ness of a trait object since only 1 platform will ever be used in a binary. What do you think of the following:
1. Define a platform trait
2. Each platform defines a zero-sized struct implementing the trait (ex `WebPlatform`).
3. Create an ifdef'd type alias on those structs:
```
// gui/src/platform/mod.rs
#[cfg(feature = "web")]
pub type CurrentPlatform = web::WebPlatform;
#[cfg(feature = "desktop")]
pub type CurrentPlatform = desktop::DesktopPlatform;
#[cfg(feature = "mobile")]
pub type CurrentPlatform = mobile::MobilePlatform;
```
4. Add a compile time assertion that `CurrentPlatform` implements `Platform`.
Pros:
- We don't end up working around async trait objects
- We define what functions are needed for a platform
- We save a little on binary size by avoiding a fully generic solution.
Cons:
- The trait does not really do much other than being a collection of functions.
- In some ways it seems like what we're currently doing but with extra steps.
Setting this buffer to 2400 was only enough to store 50m of sound
at 48,000 samples/sec. I've also done a small refactor here to
make this buffer scale with sample rate.
# Summary
This change improves the channel selection behavior to be more similar to the official client and generally more usable. It's currently mildly broken due to the details element grabbing click events from the whole row and the row text being selectable. This change also makes it more obvious that the channel title can be clicked. I'm not sure how this works on mobile, so we might need to make more changes there in the future to work better with touchscreens.
# Changes
- Channels can only be expanded or collapsed by clicking on the adjacent arrow
- Expand/collapse arrows are only displayed on channels with children or users
- Channel can only be joined by double clicking to the right of the collapse/expand arrow
- The channel title background (and the empty space to the right) display a highlight when the user hovers over them.
- All text inside the channel view is no longer selectable.
# Testing
I tested on the desktop client. I didn't test on mobile but I'll give it a shot after I merge and maybe come back with another PR to make this behavior more intuitive over there.
Reviewed-on: #21
# Summary
Channel ordering is currently broken as described in #17. This change makes sorting work correctly and cleans up the logic a bit.
# Changes
- creates a `ChannelsState` wrapper struct to handle this behavior
- moves the logic for handling `ChannelState` processing, including data update and parent-child tree sorting, into the impl for `ChannelsState`
- moves the logic for handling `ChannelRemove` into this impl
Parent child sorting properly applies the position values, which are arbitrary integers that are supposed to be sorted in numerical order. Lexicographical sorting is use for tiebreaking, which lines up (at least in my testing) with the official client's behavior. We may handle some lexicographical edge cases differently (spaces, symbols, etc) but 1. the Desktop client compliance is best effort and 2. users should use the position fields instead of relying on text sort order. Some compatibility is still helpful for matching temporary channel positioning, especially for servers with automated channel creation workflows.
This code is a bit complicated, as the mumble protocol makes no guarantees which order the channels will be sent. It ended up being simpler to just bulk recreate the children anytime any channel update is sent. I don't expect this to ever have performance issues, though maybe someday some server with 10,000 channels will send us a bug report 😆
# Testing
I tested this change by creating a bunch of channel with various sort orders and names. I compared the behavior with the official desktop client and our client seemed to follow along.
Reviewed-on: #20
(Turns out not) Pretty simple, if the average amplitude is under a certain value clear
out the buffer! The value I chose (.001) was an arbitrary value I got
from printf debugging. I was able to show that this worked pretty well
on the desktop session. Hopefully we'll add this to the settings page at
some point.
Once the app has been under that threshold for more than 200ms, we stop transmitting and send a terminator packet.
Reviewed-on: #13
Reviewed-by: restitux <restitux@ohea.xyz>
I noticed our BRB and AFK rooms were borked and made a change to fix that. I've attached a photo showing the result.
Overview:
- Add suppress field to UserState struct
- Process suppress field from UserState protobuf messages
- Update UI to show suppressed users with blacked out styling and muted icon
- Disable mute toggle button when user is suppressed
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Reviewed-on: #16
This adds android builds to the CI infrastructure. These builds generate an `apk` file that you can download and install.
- Adds a new container build job that builds a container with all the required android dependencies
- Adds a new release build that builds an android apk
- Updated the imp module to split out mobile and desktop behavior
- Adds logic to request microphone permissions
- Added a custom android manifest that declares the required permissions
Reviewed-on: #9
I vibe coded a change so that I can use mumble-web2 when I only have it in a small area instead of a full screen.
Scaling for smol screens:

Full screen scaling:

Reviewed-on: #15
Reviewed-by: restitux <restitux@ohea.xyz>
Co-authored-by: Samuel Warfield <samuel.warfield2@gmail.com>
Co-committed-by: Samuel Warfield <samuel.warfield2@gmail.com>
Add background color to #main in loader styles to prevent white flash
while app CSS loads after WASM initialization.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Shows a themed spinner overlay while the large WASM bundle downloads,
improving perceived load time on slower connections.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The PR was merged into upstream. They haven't cut a release yet though so we still need to pull from git.
Reviewed-on: #7
Reviewed-by: Sam Sartor <cap@samsartor.com>
Remove public_url config option
Use proxy_url instead for example configs
Get status from relative endpoint, like /config
Show version on login page
Reviewed-on: #5
Co-authored-by: Sam Sartor <me@samsartor.com>
Co-committed-by: Sam Sartor <me@samsartor.com>
Reviewed-on: #3
Automatically choose supported profile
Play stream once created
Co-authored-by: Sam Sartor <me@samsartor.com>
Co-committed-by: Sam Sartor <me@samsartor.com>
Adds a windows container build and a windows client build.
Outstanding issues:
- I'm not sure if `workflow_dispatch` works. Based on [this PR](https://github.com/go-gitea/gitea/pull/28163) it seems like it should, but I don't see a button. It might only work after merging into the default branch.
- The windows build container is building dioxus from git HEAD because there is an unreleased bugfix we are depending on. We should revert this to a versioned release (maybe using `binstall`) once they cut 0.7.2
Reviewed-on: #2