commit 9103ff1fb73f31592eae96ac5d7bea8c22fe139d Author: gramps Date: Tue Aug 4 09:09:52 2026 -0700 Initial scaffold: ytLive C# WPF project - MVVM architecture (Models, ViewModels, Views, Services, Helpers) - Models: Scene, Source, StreamConfig, StreamHealth, YouTubeChannel, ChatMessage - Services: YouTubeAuthService (OAuth2), YouTubeStreamService (broadcasts/stream health), YouTubeChatService (live chat polling) - ViewModels: MainViewModel with scene management, stream controls, chat - MainWindow with dark theme: scene/source panel, preview area, chat panel, status bar - Version roadmap: v0.1 (scenes, RTMP, OAuth2, chat, health) -> 1.0 diff --git a/App.xaml b/App.xaml new file mode 100644 index 0000000..2bf3cea --- /dev/null +++ b/App.xaml @@ -0,0 +1,9 @@ + + + + + diff --git a/App.xaml.cs b/App.xaml.cs new file mode 100644 index 0000000..1ff0ff3 --- /dev/null +++ b/App.xaml.cs @@ -0,0 +1,13 @@ +using System.Configuration; +using System.Data; +using System.Windows; + +namespace ytLive; + +/// +/// Interaction logic for App.xaml +/// +public partial class App : Application +{ +} + diff --git a/AssemblyInfo.cs b/AssemblyInfo.cs new file mode 100644 index 0000000..c73865b --- /dev/null +++ b/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Windows; + +[assembly:ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] diff --git a/Helpers/RelayCommand.cs b/Helpers/RelayCommand.cs new file mode 100644 index 0000000..9ea4c8f --- /dev/null +++ b/Helpers/RelayCommand.cs @@ -0,0 +1,24 @@ +using System.Windows.Input; + +namespace ytLive.Helpers; + +public class RelayCommand : ICommand +{ + private readonly Action _execute; + private readonly Func? _canExecute; + + public RelayCommand(Action execute, Func? canExecute = null) + { + _execute = execute; + _canExecute = canExecute; + } + + public event EventHandler? CanExecuteChanged + { + add => CommandManager.RequerySuggested += value; + remove => CommandManager.RequerySuggested -= value; + } + + public bool CanExecute(object? parameter) => _canExecute?.Invoke(parameter) ?? true; + public void Execute(object? parameter) => _execute(parameter); +} diff --git a/Helpers/ViewModelBase.cs b/Helpers/ViewModelBase.cs new file mode 100644 index 0000000..f762d2c --- /dev/null +++ b/Helpers/ViewModelBase.cs @@ -0,0 +1,23 @@ +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace ytLive.Helpers; + +public abstract class ViewModelBase : INotifyPropertyChanged +{ + public event PropertyChangedEventHandler? PropertyChanged; + + protected bool SetProperty(ref T field, T value, [CallerMemberName] string? propertyName = null) + { + if (EqualityComparer.Default.Equals(field, value)) + return false; + field = value; + OnPropertyChanged(propertyName); + return true; + } + + protected void OnPropertyChanged([CallerMemberName] string? propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } +} diff --git a/MainWindow.xaml b/MainWindow.xaml new file mode 100644 index 0000000..8d4fb81 --- /dev/null +++ b/MainWindow.xaml @@ -0,0 +1,242 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +