feat(topbar): one decision — [ Build Scenes ][ Record / Go Live ]

Unit A of the top-bar redesign (creator ruling 2026-11-12). The standalone EDIT toggle is gone;
Build Scenes IS edit mode, so "which world am I in?" and "can I edit?" cannot disagree any more.

BarWorld (ViewModels/MainViewModel.TopBar.cs) has two values, Build and Output. Build ships
highlighted at launch and sets EditingActive on; choosing Record / Go Live turns it off. Clicking the
already-highlighted half is deliberately not a no-op: it re-enters that world, which is the only way
back to the layers during a session now that the EDIT button is gone (unit 1 made that legal).

WorldSegmentButton (Themes/Controls.xaml) is a Button, not a ToggleButton, and each half passes its
own flag through Tag to one shared DataTrigger — a ToggleButton would fight the view model and, with a
one-way IsChecked, clear its own binding on first click.

UiShellTests updated where it guarded the old EDIT markup (it now guards the segment). TopBarWorldTests
pins launch state, both transitions, and the re-enter case.

679 pass, 0 warnings. Next: unit B (output switch), unit C (output-dependent action + End labels).
Scope justification: Assets/starting-background.jpg is outside this unit — the creator's replacement
art, still awaiting their visual check.
This commit is contained in:
2026-11-12 15:05:29 -08:00
parent d46e84562f
commit a5dc2f494c
7 changed files with 174 additions and 30 deletions
+55
View File
@@ -0,0 +1,55 @@
using System;
using System.IO;
using Xunit;
using ytLive.ViewModels;
namespace ytLive.Tests;
/// <summary>
/// The top bar's one decision: <c>[ Build Scenes ][ Record / Go Live ]</c>. Build Scenes ships
/// highlighted, and it <b>is</b> edit mode — the old EDIT toggle is gone, so "can I edit?" and "which
/// world am I in?" cannot disagree.
/// </summary>
[Collection("MainViewModelLayoutPath")]
public class TopBarWorldTests
{
[Fact]
public void Build_scenes_is_the_default_world_and_is_the_only_place_editing_lives()
{
var db = Path.Combine(Path.GetTempPath(), $"llamacasty-world-{Guid.NewGuid():N}.db");
MainViewModel.LayoutPathOverride = db;
try
{
var vm = new MainViewModel();
// Launch state: Build Scenes highlighted, editing on.
Assert.Equal(BarWorld.Build, vm.BarWorld);
Assert.True(vm.IsBuildWorld);
Assert.False(vm.IsOutputWorld);
Assert.True(vm.IsEditMode);
// Record / Go Live leaves edit mode — the output surface has nothing to drag.
vm.EnterOutputWorldCommand.Execute(null);
Assert.Equal(BarWorld.Output, vm.BarWorld);
Assert.True(vm.IsOutputWorld);
Assert.False(vm.IsBuildWorld);
Assert.False(vm.IsEditMode);
// Clicking the highlighted half is not a no-op: it re-enters that world, which is how a
// creator takes editing back during a session.
vm.EnterBuildWorldCommand.Execute(null);
Assert.True(vm.IsBuildWorld);
Assert.True(vm.IsEditMode);
// …and re-entering the world already in force still re-asserts its edit state.
vm.EditingActive = false;
vm.EnterBuildWorldCommand.Execute(null);
Assert.True(vm.IsEditMode);
}
finally
{
MainViewModel.LayoutPathOverride = null;
try { File.Delete(db); } catch (IOException) { }
}
}
}
+12 -10
View File
@@ -27,12 +27,13 @@ public class UiShellTests
Assert.Contains("Brush.Youtube", tokens);
Assert.Equal(2, Count(preview, "Background=\"{StaticResource Brush.Youtube}\""));
// 2. EDIT: resting face differs from the armed face, and the label follows it.
Assert.Contains("x:Name=\"EditBody\" Background=\"{StaticResource Brush.SurfaceContainerHigh}\"", topBar);
Assert.Contains("<Setter TargetName=\"EditBody\" Property=\"Background\" Value=\"{StaticResource Brush.Tertiary}\"/>", topBar);
// The label is white in BOTH states — only the fill changes. A muted resting colour is
// what made it illegible on the dark bar (creator, 2026-10-30).
Assert.Contains("FontSize=\"12\" Foreground=\"{StaticResource Brush.OnAccent}\"", topBar);
// 2. The world segment replaced the EDIT toggle (creator ruling 2026-11-12): both halves are
// present, each lit by its own world flag through the shared style.
Assert.Contains("Content=\"Build Scenes\"", topBar);
Assert.Contains("Content=\"Record / Go Live\"", topBar);
Assert.Contains("Tag=\"{Binding IsBuildWorld}\"", topBar);
Assert.Contains("Tag=\"{Binding IsOutputWorld}\"", topBar);
Assert.DoesNotContain("x:Name=\"EditBody\"", topBar);
// 3. Active scene = orange; live stays red.
Assert.Contains("IsStaged}\" Value=\"True\">\n <Setter Property=\"BorderBrush\" Value=\"{StaticResource Brush.Attention}\"/>", strip);
@@ -91,11 +92,12 @@ public class UiShellTests
Assert.Contains("MouseLeftButtonUp=\"SettingsTab_Click\"", gear);
Assert.DoesNotContain("ToolTip=\"Settings\"", topBar);
// EDIT stays in the left brand cluster, before the centre session cluster.
var edit = topBar.IndexOf("EditingActive", System.StringComparison.Ordinal);
// The world segment leads the centre cluster; the brand column carries no edit control.
var centre = topBar.IndexOf("<StackPanel Grid.Column=\"1\"", System.StringComparison.Ordinal);
Assert.True(edit > 0 && centre > edit,
"the EDIT toggle must appear before the centre (session) cluster");
var segment = topBar.IndexOf("Content=\"Build Scenes\"", System.StringComparison.Ordinal);
Assert.True(centre > 0 && segment > centre,
"the [ Build Scenes ][ Record / Go Live ] segment must lead the centre cluster");
Assert.DoesNotContain("x:Name=\"EditBody\"", topBar);
}
private static int Count(string haystack, string needle)