diff --git a/README.md b/README.md
index 495f57d..a4f13e7 100644
--- a/README.md
+++ b/README.md
@@ -80,7 +80,7 @@ mumh5 keeps the foundation and replaces the experience.
- Share a screen or a window with the mumh5 users in your channel. A dialog lets you choose what to show, with a live preview, which sound goes with it and the quality, before anything is sent
- Sound: on Linux one program or everything except mumh5 itself (through PipeWire, so viewers do not hear the voice chat twice); on Windows the whole system; in a browser what the browser offers
-- People in the channel see an indicator next to your name, on your tile and in your profile, and click to watch. The tiles above the chat fill a stage you can drag taller or shorter. A sharer's tile shows the picture; a click makes it large with everyone else in a strip below, like a meeting, and it can move to a window of its own. Viewers set the stream's volume
+- People in the channel see an indicator next to your name, on your tile and in your profile, and click to watch. The tiles above the chat fill a stage you can drag taller or shorter. A stream opens large, with everyone as a strip of tiles below, like a meeting, and can move to a window of its own; in the stacked layout it is just the picture, as wide as the chat. Sounds announce streams and viewers. Viewers set the stream's volume
- No server setup and no extra account: the setup messages travel through the Mumble server, the stream goes directly between the two clients (WebRTC), up to 8 viewers. Regular Mumble clients do not see streams
- Direct connections mean sharer and viewer see each other's IP address; mumh5 says so before the first use. Across the internet both sides need a STUN server. The browser version uses the one built into its proxy; in the desktop app you enter one in Settings, Voice (the proxy's address works there too), and none is contacted unless you do
- Tested between two desktop instances on one machine with a test picture. Sound capture, real screens, connections across the internet, Windows and the browser build are untested
diff --git a/src/ui/VoiceStage.svelte b/src/ui/VoiceStage.svelte
index 5885ae0..d96e5af 100644
--- a/src/ui/VoiceStage.svelte
+++ b/src/ui/VoiceStage.svelte
@@ -12,8 +12,8 @@
import ResizeHandle from './ResizeHandle.svelte';
// People in your voice channel as tiles. Shown on windows too narrow for the member list
- // (always), and on any window while someone in the channel shares a screen: a sharer's tile
- // carries the picture, and clicking it puts it on a large stage with the tiles as a strip.
+ // (always), and on any window while someone in the channel shares a screen. A stream is shown
+ // large above the tiles, which become a strip; the tiles themselves never carry the picture.
// videoOnly: just the picture, as wide as the chat (stacked layout, where the channel list
// above already shows who is here)
let { always = false, videoOnly = false }: { always?: boolean; videoOnly?: boolean } = $props();
@@ -34,7 +34,7 @@
return watching?.session === s ? watching.stream : null;
}
- // Tiles with a picture come first, then other people who share, so streams sit at the top left
+ // People with a stream we can show come first, then others who share
const rank = (s: number) => streamOf(s) ? 0 : session.sharing[s] ? 1 : 2;
const people = $derived([...inChannel].sort((a, b) => rank(a.session) - rank(b.session)));
@@ -52,7 +52,14 @@
lastWatch = now;
}
});
- $effect(() => { if (focus === self?.session && !mine) focus = null; });
+ // Our own stream opens large when it starts, unless another one is on the stage
+ let hadMine = false;
+ $effect(() => {
+ const has = !!mine;
+ if (has && !hadMine && focus == null && self) focus = self.session;
+ else if (!has && focus === self?.session) focus = null;
+ hadMine = has;
+ });
// Without tiles there is nothing to choose: the watched stream, else our own
$effect(() => { if (videoOnly) focus = watching ? watching.session : mine ? self?.session ?? null : null; });
@@ -264,17 +271,12 @@
{@const sharing = !!session.sharing[u.session]}
{@const isSelf = u.session === self.session}