From d5f50edf238341da8489fda01db1cdc7c01a8782 Mon Sep 17 00:00:00 2001 From: gramps Date: Fri, 7 Aug 2026 14:22:12 -0700 Subject: [PATCH] Kill stringly-typed command tokens: Add Source menu passes SourceType enum via x:Static CommandParameter (typos break the build, not silently add an Image); webcam gets its own AddWebcamCommand; AddSource keeps Enum.TryParse fallback; docs updated (65 tests passing) --- MainWindow.xaml | 9 +++++---- ViewModels/MainViewModel.cs | 23 +++++++---------------- ai.md | 2 +- 3 files changed, 13 insertions(+), 21 deletions(-) diff --git a/MainWindow.xaml b/MainWindow.xaml index e5231f7..20d8953 100644 --- a/MainWindow.xaml +++ b/MainWindow.xaml @@ -4,6 +4,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:vm="clr-namespace:ytLive.ViewModels" + xmlns:models="clr-namespace:ytLive.Models" xmlns:Helpers="clr-namespace:ytLive.Helpers" mc:Ignorable="d" Title="{Binding WindowTitle}" Height="720" Width="1280" @@ -249,13 +250,13 @@ Click="AddSourceButton_Click"> - - - + + - + BeginEditScene(scene as Scene)); RemoveSceneCommand = new RelayCommand(scene => RemoveScene(scene as Scene)); ToggleSceneVisibilityCommand = new RelayCommand(scene => ToggleSceneVisibility(scene as Scene)); - AddSourceCommand = new RelayCommand(type => AddSource(type as string)); + AddSourceCommand = new RelayCommand(parameter => AddSource(parameter)); + AddWebcamCommand = new RelayCommand(_ => _ = AddWebcamToActiveSceneAsync()); AddImageCommand = new RelayCommand(_ => AddImage()); RemoveSourceCommand = new RelayCommand(element => RemoveElement(element as SceneElement)); ChangeWebcamCommand = new RelayCommand(_ => _ = ChangeWebcamAsync(), _ => CanChangeWebcam); @@ -1060,25 +1062,14 @@ public class MainViewModel : ViewModelBase ActiveScene = Scenes.FirstOrDefault(); } - private void AddSource(string? type) + private void AddSource(object? parameter) { var scene = ActiveScene; if (scene == null) return; - if (string.Equals(type, "webcam", StringComparison.OrdinalIgnoreCase)) - { - _ = AddWebcamToActiveSceneAsync(); - return; - } - - var sourceType = type?.ToLowerInvariant() switch - { - "screen" => SourceType.DisplayCapture, - "window" => SourceType.WindowCapture, - "background" => SourceType.Background, - "text" => SourceType.TextOverlay, - _ => SourceType.Image, - }; + var sourceType = parameter is SourceType typed + ? typed + : Enum.TryParse(parameter?.ToString(), true, out var parsed) ? parsed : SourceType.Image; var baseName = sourceType switch { SourceType.DisplayCapture => "Screen", diff --git a/ai.md b/ai.md index 615bb0a..b4009fd 100644 --- a/ai.md +++ b/ai.md @@ -95,7 +95,7 @@ C# / WPF (.NET 8) following MVVM: ### Key patterns - `ViewModelBase.SetProperty()` for property change notifications -- `RelayCommand` for all button actions; commands gate on state (e.g. Start only when Offline) +- `RelayCommand` for all button actions; commands gate on state (e.g. Start only when Offline). **Typed `CommandParameter`s — no stringly-typed command tokens:** menu items that pick a source type pass the enum value itself (`CommandParameter="{x:Static models:SourceType.DisplayCapture}"`), so a typo breaks the build instead of silently adding an Image; `AddSource` still falls back to `Enum.TryParse(..., true)` for safety. The webcam item is its own `AddWebcamCommand` (it greys out via `CanAddWebcamToActiveScene` and isn't a `SourceType` — webcams are `WebcamSceneConfig`, not `Source` rows) - ViewModels are constructed in XAML (`` as DataContext) - Services are currently instantiated in MainViewModel's constructor — no DI container yet - Layout persists to SQLite (`Microsoft.Data.Sqlite`); scenes/sources/asset bytes stored in the DB, asset identity is a SHA-256 content hash (1:M reuse, no file paths — assets are always available)