TASK 24 follow-up: TRAX button moved into the game audio bar, left of the 'Desktop Audio' label in the preview overlay (the creator's follow-up pick — the music control sits with the desktop audio controls it rides); removed from the footer's left cluster next to Socials. TraxToolTip now teaches the clicks ('left-click plays/pauses, right-click chooses a track'). Docs in the same commit: TASKS.md TASK 24 item 6, ai.md audio section, ViewModels/index.md. Build 0 warnings, 197 tests passing (no test changes — UI relocation only)

This commit is contained in:
2026-08-15 15:42:24 -07:00
parent a0a8331569
commit cc71ed5066
5 changed files with 37 additions and 37 deletions
+20 -21
View File
@@ -765,6 +765,21 @@
Margin="0,0,0,12" Margin="0,0,0,12"
Visibility="{Binding IsGameAudioBarVisible, Converter={StaticResource BoolToVis}}"> Visibility="{Binding IsGameAudioBarVisible, Converter={StaticResource BoolToVis}}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<!-- TRAX sits left of the "Desktop Audio" label
(the creator's pick): it rides the desktop
channel, so the music control lives with the
desktop audio controls. -->
<Button Style="{StaticResource YtButtonSecondary}" Padding="8,2"
FontSize="11" VerticalAlignment="Center" Margin="0,0,8,0"
ToolTip="{Binding TraxToolTip}"
Click="TraxButton_Click"
PreviewMouseRightButtonUp="TraxButton_PreviewMouseRightButtonUp">
<StackPanel Orientation="Horizontal">
<Ellipse Width="8" Height="8" Fill="{Binding TraxDotBrush}"
Margin="0,0,6,0" VerticalAlignment="Center"/>
<TextBlock Text="TRAX"/>
</StackPanel>
</Button>
<TextBlock Text="Desktop Audio" Foreground="#a0a0b0" FontSize="11" <TextBlock Text="Desktop Audio" Foreground="#a0a0b0" FontSize="11"
FontWeight="SemiBold" VerticalAlignment="Center" Margin="0,0,8,0" FontWeight="SemiBold" VerticalAlignment="Center" Margin="0,0,8,0"
ToolTip="Desktop/game audio via WASAPI loopback (automatic); TRAX music rides the same channel"/> ToolTip="Desktop/game audio via WASAPI loopback (automatic); TRAX music rides the same channel"/>
@@ -863,12 +878,13 @@
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<!-- Line 1: Social controls + TRAX on the LEFT (under scenes/sources), <!-- Line 1: Social controls on the LEFT (under scenes/sources),
sound meter + volume control CENTERED beneath the preview panel. sound meter + volume control CENTERED beneath the preview panel.
Audio is KISS: desktop/game audio is automatic (WASAPI loopback); Audio is KISS: desktop/game audio is automatic (WASAPI loopback);
the mic meter is the creator's only footer audio control (the the mic meter is the creator's only footer audio control (the
game audio bar lives INSIDE the preview window, overlaid at its game audio bar lives INSIDE the preview window, overlaid at its
bottom edge while a full-screen game is up). --> bottom edge while a full-screen game is up, and carries the
TRAX button left of its "Desktop Audio" label). -->
<Grid Grid.Row="0"> <Grid Grid.Row="0">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="220"/> <ColumnDefinition Width="220"/>
@@ -876,14 +892,12 @@
<ColumnDefinition Width="300"/> <ColumnDefinition Width="300"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<!-- Social controls + TRAX: centered under the scenes/sources <!-- Social controls: centered under the scenes/sources listboxes -->
listboxes. TRAX rides the desktop/game audio channel, so
it lives in the left cluster, away from the mic meter. -->
<StackPanel Grid.Column="0" Orientation="Horizontal" <StackPanel Grid.Column="0" Orientation="Horizontal"
HorizontalAlignment="Center" VerticalAlignment="Center"> HorizontalAlignment="Center" VerticalAlignment="Center">
<Button Command="{Binding OpenSocialDialogCommand}" <Button Command="{Binding OpenSocialDialogCommand}"
Style="{StaticResource YtButtonSecondary}" Padding="12,4" Style="{StaticResource YtButtonSecondary}" Padding="12,4"
FontSize="11" VerticalAlignment="Center" Margin="0,0,8,0" FontSize="11" VerticalAlignment="Center"
ToolTip="Add or manage your social handles"> ToolTip="Add or manage your social handles">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<Ellipse Width="8" Height="8" Fill="{Binding SocialBarDotBrush}" <Ellipse Width="8" Height="8" Fill="{Binding SocialBarDotBrush}"
@@ -891,21 +905,6 @@
<TextBlock Text="Socials"/> <TextBlock Text="Socials"/>
</StackPanel> </StackPanel>
</Button> </Button>
<!-- TRAX (TASK 9): free background music. Dot: green = playing,
yellow = loaded/stopped, red = no track. Left-click toggles
play/pause (opens the picker when no track); right-click
always opens the picker. -->
<Button Style="{StaticResource YtButtonSecondary}" Padding="12,4"
FontSize="11" VerticalAlignment="Center"
ToolTip="{Binding TraxToolTip}"
Click="TraxButton_Click"
PreviewMouseRightButtonUp="TraxButton_PreviewMouseRightButtonUp">
<StackPanel Orientation="Horizontal">
<Ellipse Width="8" Height="8" Fill="{Binding TraxDotBrush}"
Margin="0,0,6,0" VerticalAlignment="Center"/>
<TextBlock Text="TRAX"/>
</StackPanel>
</Button>
</StackPanel> </StackPanel>
<!-- Mic meter + volume: centered under the preview panel --> <!-- Mic meter + volume: centered under the preview panel -->
+6 -5
View File
@@ -734,14 +734,15 @@ The creator reviewed the TASK 9 build and filed 8 issues. All fixed in one branc
3. ✅ **TRAX played once then stopped** — `MusicPlayer.OnPlaybackStopped` only looped when 3. ✅ **TRAX played once then stopped** — `MusicPlayer.OnPlaybackStopped` only looped when
`Position >= Length`, unreliable for `MediaFoundationReader`; now any clean stop `Position >= Length`, unreliable for `MediaFoundationReader`; now any clean stop
(`e.Exception == null`) rewinds + replays; only errors/explicit stops surface via `PlaybackEnded`. (`e.Exception == null`) rewinds + replays; only errors/explicit stops surface via `PlaybackEnded`.
4. ✅ **TRAX/MIC buttons swapped** — the footer's mic cluster is now MIC + meter + mute + volume; TRAX 4. ✅ **TRAX/MIC buttons swapped** — TRAX no longer sits between MIC and the (mic-specific) meter;
no longer sits between MIC and the (mic-specific) meter. the footer's mic cluster is MIC + meter + mute + volume.
5. ✅ **Mic source persists across restarts** — `LayoutStore` gained a `Settings` key/value table 5. ✅ **Mic source persists across restarts** — `LayoutStore` gained a `Settings` key/value table
(`SaveMicSourceName`/`LoadMicSourceName`); the VM saves on pick and restores before the mixer's (`SaveMicSourceName`/`LoadMicSourceName`); the VM saves on pick and restores before the mixer's
first `Start`, so a restart reconnects the same already-vetted device (green dot) or reports it first `Start`, so a restart reconnects the same already-vetted device (green dot) or reports it
missing (yellow) instead of falling back to the default endpoint. missing (yellow) instead of falling back to the default endpoint.
6. ✅ **TRAX moved right of Socials** — both centered under the scenes/sources listboxes (footer line 6. ✅ **TRAX moved into the game audio bar** — left of the "Desktop Audio" label in the preview
1, left cluster). overlay (the creator's follow-up pick), no longer in the footer; its tooltip teaches the clicks
("left-click pauses/plays, right-click chooses a track").
7. ✅ **Backdrop's icons shifted right** — the trash button's `Collapsed` released its column; a new 7. ✅ **Backdrop's icons shifted right** — the trash button's `Collapsed` released its column; a new
`HiddenBoolToVisibilityConverter` keeps the slot reserved (`Hidden`), so edit/eye stay in their `HiddenBoolToVisibilityConverter` keeps the slot reserved (`Hidden`), so edit/eye stay in their
fixed columns for every source row. fixed columns for every source row.
+2 -2
View File
@@ -523,8 +523,8 @@ public class MainViewModel : ViewModelBase
: BarOffBrush; : BarOffBrush;
public string TraxToolTip => _musicPlayer.TrackName != null public string TraxToolTip => _musicPlayer.TrackName != null
? $"TRAX: {_musicPlayer.TrackName} — click to {(IsMusicPlaying ? "pause" : "play")}" ? $"TRAX: {_musicPlayer.TrackName} — left-click {(IsMusicPlaying ? "pauses" : "plays")}, right-click chooses a track"
: "No track — right-click to choose background music"; : "No track — left-click or right-click to choose background music";
/// <summary>Left-click on TRAX: no track → pick one; otherwise toggle play/pause.</summary> /// <summary>Left-click on TRAX: no track → pick one; otherwise toggle play/pause.</summary>
public void TraxLeftClick() public void TraxLeftClick()
+1 -1
View File
File diff suppressed because one or more lines are too long
+5 -5
View File
@@ -418,11 +418,11 @@ devices, no timers).
and let tracks play once then stop). The desktop/game bar scales it locally via `LocalGain` (the VM's and let tracks play once then stop). The desktop/game bar scales it locally via `LocalGain` (the VM's
`UpdateTraxLocalGain` runs on every `GameAudioVolume`/`GameMuted` change and on TRAX load), so the `UpdateTraxLocalGain` runs on every `GameAudioVolume`/`GameMuted` change and on TRAX load), so the
creator **hears** the game volume slider and mute work on the music in the headphones — matching the creator **hears** the game volume slider and mute work on the music in the headphones — matching the
stream, where it rides the loopback channel ducked with the game. No third mixer input. Footer stream, where it rides the loopback channel ducked with the game. No third mixer input. The footer
**TRAX** button, relocated next to **Socials** in the footer's left cluster (centered under the **TRAX** button lives **inside the game audio bar** (in the preview overlay), **left of the
scenes/sources listboxes) so it no longer sits between MIC and the mic meter: status dot (**red** no "Desktop Audio" label** (the creator's pick — it rides the desktop channel, so the music control
track / **yellow** loaded stopped / **green** playing) + "TRAX"; **left-click** toggles play/pause sits with the desktop audio controls): status dot (**red** no track / **yellow** loaded stopped /
(opens the in-app picker via `OpenFileDialog` when no track is loaded); **right-click** always opens **green** playing) + "TRAX"; **left-click** toggles play/pause (opens the in-app picker via `OpenFileDialog` when no track is loaded); **right-click** always opens
the picker (`TraxButton_PreviewMouseRightButtonUp`, `e.Handled = true`, code-behind pattern); tooltip the picker (`TraxButton_PreviewMouseRightButtonUp`, `e.Handled = true`, code-behind pattern); tooltip
shows the loaded/playing track name or "No track — right-click to choose background music". The picked shows the loaded/playing track name or "No track — right-click to choose background music". The picked
track persists via **schema v9** single-row `Music` (`TrackPath`/`IsEnabled`) in `LayoutStore`. Known track persists via **schema v9** single-row `Music` (`TrackPath`/`IsEnabled`) in `LayoutStore`. Known