From 695377f35428576a0d5fcfb074abc782023a25b6 Mon Sep 17 00:00:00 2001 From: Scott Lilly <2271154+ScottLilly@users.noreply.github.com> Date: Sat, 3 May 2025 15:56:16 -0400 Subject: [PATCH 01/13] WIP --- Legba.Engine/ViewModels/ChatViewModel.cs | 34 ++++-- ...PromptPrefixSelectionContainerViewModel.cs | 67 ++++++++++++ .../PromptPrefixSelectionViewModel.cs | 2 + Legba.WPF/App.xaml.cs | 9 +- .../CustomControls/PromptPrefixSection.xaml | 48 +++++++++ .../PromptPrefixSection.xaml.cs | 40 +++++++ Legba.WPF/MainWindow.xaml | 4 +- Legba.WPF/MainWindow.xaml.cs | 61 ++++++++--- .../GenericPromptPrefixSelectionViewModel.cs | 7 +- .../Windows/PromptPrefixSelectionView.xaml | 100 ++++++++++++++++++ .../Windows/PromptPrefixSelectionView.xaml.cs | 59 +++++++++++ 11 files changed, 399 insertions(+), 32 deletions(-) create mode 100644 Legba.Engine/ViewModels/PromptPrefixSelectionContainerViewModel.cs create mode 100644 Legba.WPF/CustomControls/PromptPrefixSection.xaml create mode 100644 Legba.WPF/CustomControls/PromptPrefixSection.xaml.cs create mode 100644 Legba.WPF/Windows/PromptPrefixSelectionView.xaml create mode 100644 Legba.WPF/Windows/PromptPrefixSelectionView.xaml.cs diff --git a/Legba.Engine/ViewModels/ChatViewModel.cs b/Legba.Engine/ViewModels/ChatViewModel.cs index 0d56431..177ec6b 100644 --- a/Legba.Engine/ViewModels/ChatViewModel.cs +++ b/Legba.Engine/ViewModels/ChatViewModel.cs @@ -14,27 +14,37 @@ public class ChatViewModel : ObservableObject public ObservableCollection Llms { get; } = new(); - private ChatSession _chatSession; + private ChatSession? _chatSession; - public ChatSession ChatSession + public ChatSession? ChatSession { get { return _chatSession; } set { + // No change, early exit + if (_chatSession == value) + { + return; + } + + // Unsubscribe from the old session's messages collection changed event if (_chatSession != null) { _chatSession.Messages.CollectionChanged -= Messages_CollectionChanged; } - if (_chatSession != value) - { - _chatSession = value; - OnPropertyChanged(nameof(ChatSession)); - OnPropertyChanged(nameof(HasChatSession)); - OnPropertyChanged(nameof(HasChatMessages)); + _chatSession = value; + // Subscribe to the new session's messages collection changed event + if (_chatSession != null) + { _chatSession.Messages.CollectionChanged += Messages_CollectionChanged; } + + // Raise property changed notifications + OnPropertyChanged(nameof(ChatSession)); + OnPropertyChanged(nameof(HasChatSession)); + OnPropertyChanged(nameof(HasChatMessages)); } } @@ -51,6 +61,8 @@ private void Messages_CollectionChanged(object? sender, NotifyCollectionChangedE #endregion + #region Constructor + public ChatViewModel(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; @@ -65,6 +77,10 @@ public ChatViewModel(IServiceProvider serviceProvider) AskCommand = new RelayCommand(async () => await ChatSession.Ask()); } + #endregion + + #region Private Methods + private void SelectModel(Settings.Model model) { var llm = Llms.First(l => l.Models.Contains(model)); @@ -73,4 +89,6 @@ private void SelectModel(Settings.Model model) ChatSession = new ChatSession(_serviceProvider, llm, model); } + + #endregion } \ No newline at end of file diff --git a/Legba.Engine/ViewModels/PromptPrefixSelectionContainerViewModel.cs b/Legba.Engine/ViewModels/PromptPrefixSelectionContainerViewModel.cs new file mode 100644 index 0000000..fc957d5 --- /dev/null +++ b/Legba.Engine/ViewModels/PromptPrefixSelectionContainerViewModel.cs @@ -0,0 +1,67 @@ +using Legba.Engine.Models; +using Legba.Engine.Services; +using System.Windows.Input; + +namespace Legba.Engine.ViewModels; + +public class PromptPrefixSelectionContainerViewModel : ObservableObject +{ + #region Fields and Properties + + private readonly PromptRepository _promptRepository; + + public PromptPrefixSelectionViewModel PersonaViewModel { get; } + public PromptPrefixSelectionViewModel PurposeViewModel { get; } + public PromptPrefixSelectionViewModel PersuasionViewModel { get; } + public PromptPrefixSelectionViewModel ProcessViewModel { get; } + + public ICommand LoadFromSolutionCommand { get; } + public ICommand LoadFromProjectCommand { get; } + public ICommand LoadFromFoldersCommand { get; } + public ICommand LoadFromFilesCommand { get; } + + #endregion + + #region Constructor + + public PromptPrefixSelectionContainerViewModel(PromptRepository promptRepository) + { + _promptRepository = promptRepository; + + PersonaViewModel = new PromptPrefixSelectionViewModel(_promptRepository); + PurposeViewModel = new PromptPrefixSelectionViewModel(_promptRepository); + PersuasionViewModel = new PromptPrefixSelectionViewModel(_promptRepository); + ProcessViewModel = new PromptPrefixSelectionViewModel(_promptRepository); + + LoadFromSolutionCommand = new RelayCommand(LoadFromSolution); + LoadFromProjectCommand = new RelayCommand(LoadFromProject); + LoadFromFoldersCommand = new RelayCommand(LoadFromFolders); + LoadFromFilesCommand = new RelayCommand(LoadFromFiles); + } + + #endregion + + #region Command Handlers + + private void LoadFromSolution() + { + // TODO: Implement solution-level file loading logic + } + + private void LoadFromProject() + { + // TODO: Implement project-level file loading logic + } + + private void LoadFromFolders() + { + // TODO: Implement folder-level file loading logic + } + + private void LoadFromFiles() + { + // TODO: Implement file-level loading logic + } + + #endregion +} diff --git a/Legba.Engine/ViewModels/PromptPrefixSelectionViewModel.cs b/Legba.Engine/ViewModels/PromptPrefixSelectionViewModel.cs index dbad1d4..ac8ae2a 100644 --- a/Legba.Engine/ViewModels/PromptPrefixSelectionViewModel.cs +++ b/Legba.Engine/ViewModels/PromptPrefixSelectionViewModel.cs @@ -49,6 +49,8 @@ public T? SelectedPromptPrefix public ObservableCollection PromptPrefixes { get; } = new(); + public ObservableCollection LoadedFileNames { get; } = new(); + public ICommand UseCommand { get; private set; } public ICommand EditCommand { get; private set; } public ICommand DeleteCommand { get; private set; } diff --git a/Legba.WPF/App.xaml.cs b/Legba.WPF/App.xaml.cs index ce15b2a..c3d409e 100644 --- a/Legba.WPF/App.xaml.cs +++ b/Legba.WPF/App.xaml.cs @@ -57,6 +57,8 @@ public App() services.AddTransient>(); services.AddTransient>(); services.AddTransient>(); + services.AddTransient(); + services.AddTransient(); // Register 'service' objects for injection services.AddSingleton(); @@ -76,11 +78,6 @@ protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); - // Resolve the main window and set its DataContext to the injected view model - var mainWindow = _serviceProvider.GetRequiredService(); - var viewModel = _serviceProvider.GetRequiredService(); - - mainWindow.DataContext = viewModel; - mainWindow.Show(); + _serviceProvider.GetRequiredService().Show(); } } \ No newline at end of file diff --git a/Legba.WPF/CustomControls/PromptPrefixSection.xaml b/Legba.WPF/CustomControls/PromptPrefixSection.xaml new file mode 100644 index 0000000..02a35ba --- /dev/null +++ b/Legba.WPF/CustomControls/PromptPrefixSection.xaml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Legba.WPF/CustomControls/PromptPrefixSection.xaml.cs b/Legba.WPF/CustomControls/PromptPrefixSection.xaml.cs new file mode 100644 index 0000000..683c2a6 --- /dev/null +++ b/Legba.WPF/CustomControls/PromptPrefixSection.xaml.cs @@ -0,0 +1,40 @@ +using System.Windows; +using System.Windows.Controls; + +namespace Legba.WPF.CustomControls; + +public partial class PromptPrefixSection : UserControl +{ + public PromptPrefixSection() + { + InitializeComponent(); + + DataContext = this; + } + + public static readonly DependencyProperty ViewModelProperty = + DependencyProperty.Register(nameof(ViewModel), typeof(object), typeof(PromptPrefixSection), new PropertyMetadata(null)); + + public object ViewModel + { + get => GetValue(ViewModelProperty); + set => SetValue(ViewModelProperty, value); + } + + public static readonly DependencyProperty SectionTitleProperty = + DependencyProperty.Register(nameof(SectionTitle), typeof(string), typeof(PromptPrefixSection), new PropertyMetadata(string.Empty)); + + public string SectionTitle + { + get => (string)GetValue(SectionTitleProperty); + set => SetValue(SectionTitleProperty, value); + } + + public event RoutedEventHandler ManageClicked; + public event RoutedEventHandler AddUpdateClicked; + public event RoutedEventHandler ClearClicked; + + private void Manage_Click(object sender, RoutedEventArgs e) => ManageClicked?.Invoke(this, e); + private void AddUpdate_Click(object sender, RoutedEventArgs e) => AddUpdateClicked?.Invoke(this, e); + private void Clear_Click(object sender, RoutedEventArgs e) => ClearClicked?.Invoke(this, e); +} \ No newline at end of file diff --git a/Legba.WPF/MainWindow.xaml b/Legba.WPF/MainWindow.xaml index 34eef8a..85f2863 100644 --- a/Legba.WPF/MainWindow.xaml +++ b/Legba.WPF/MainWindow.xaml @@ -124,7 +124,7 @@ + Click="ContextMenuCopyToClipboard_Click"/> @@ -166,7 +166,7 @@ IsEnabled="{Binding ElementName=prompt, Path=Text, Converter={StaticResource StringNotEmptyToBooleanConverter}}" Command="{Binding AskCommand, AsyncState=True}"/> - +