Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions Legba.Engine/Legba.Engine.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<UseWPF>true</UseWPF>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Version>2.0.0.0</Version>
<Version>2.1.0.0</Version>
</PropertyGroup>

<ItemGroup>
Expand All @@ -14,12 +14,13 @@

<ItemGroup>
<PackageReference Include="LiteDB" Version="5.0.21" />
<PackageReference Include="Microsoft.CodeAnalysis" Version="4.13.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="4.13.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.4" />
<PackageReference Include="Microsoft.CodeAnalysis" Version="4.14.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="4.14.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.5" />
<PackageReference Include="ScottLilly.CSharpExtender" Version="2.2.0" />
<PackageReference Include="System.Text.Json" Version="9.0.4" />
<PackageReference Include="System.Text.Json" Version="9.0.5" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
</ItemGroup>

</Project>
16 changes: 8 additions & 8 deletions Legba.Engine/Models/OpenAi/Message.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,34 +37,34 @@ public string DisplayText
}

[JsonIgnore]
public System.Windows.HorizontalAlignment MessageAlignment
public System.Windows.Media.Brush BackgroundColor
{
get
{
if (Role == Enums.Role.System || IsInitialSourceCode)
{
return System.Windows.HorizontalAlignment.Center;
return System.Windows.Media.Brushes.Gold;
}

return IsSentByUser
? System.Windows.HorizontalAlignment.Right
: System.Windows.HorizontalAlignment.Left;
? System.Windows.Media.Brushes.LightBlue
: System.Windows.Media.Brushes.LightGray;
}
}

[JsonIgnore]
public System.Windows.Media.Brush MessageBackground
public System.Windows.TextAlignment Alignment
{
get
{
if (Role == Enums.Role.System || IsInitialSourceCode)
{
return System.Windows.Media.Brushes.Gold;
return System.Windows.TextAlignment.Center;
}

return IsSentByUser
? System.Windows.Media.Brushes.LightBlue
: System.Windows.Media.Brushes.LightGray;
? System.Windows.TextAlignment.Right
: System.Windows.TextAlignment.Left;
}
}
}
8 changes: 4 additions & 4 deletions Legba.Engine/Models/OpenAi/OpenAiResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ namespace Legba.Engine.Models.OpenAi;
public class OpenAiResponse
{
[JsonPropertyName("id")]
public string Id { get; set; }
public string Id { get; set; } = string.Empty;
[JsonPropertyName("object")]
public string _object { get; set; }
public string _object { get; set; } = string.Empty;
[JsonPropertyName("created")]
public int Created { get; set; }
[JsonPropertyName("model")]
public string Model { get; set; }
public string Model { get; set; } = string.Empty;
[JsonPropertyName("usage")]
public Usage Usage { get; set; }
public Usage Usage { get; set; } = new Usage();
[JsonPropertyName("choices")]
public List<Choice> Choices { get; set; } = new List<Choice>();
}
42 changes: 26 additions & 16 deletions Legba.Engine/Services/FileCollector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,20 @@ public class FileCollector
{
#region Constants and Fields

private static readonly IReadOnlyList<string> s_fileExtensionsToInclude = [".cs", ".vb", ".xaml"];
private static readonly IReadOnlyList<string> s_fileExtensionsToInclude =
[
".cs", ".vb", ".xaml", ".xaml.cs", ".xaml.vb", ".vsixmanifest", ".vsct", ".resx",
".targets", ".props", ".ruleset", ".settings", ".cshtml", ".vbhtml", ".aspx", ".ascx"
];
private static readonly IReadOnlyList<Regex> s_excludedFilePatterns =
[
new Regex(@"(AssemblyAttributes|AssemblyInfo|\.g|\.g\.i|\.Designer|\.generated)\.(cs|vb)$",
RegexOptions.IgnoreCase | RegexOptions.Compiled)
];
[
new Regex(
@"(AssemblyInfo|AssemblyAttributes|\.g|\.g\.i|\.Designer|\.generated|\.razor\.cs|\.g\.cshtml|\.tt|\.t4|\.Reference)\.(cs|vb|xaml|cshtml)$",
RegexOptions.IgnoreCase | RegexOptions.Compiled)
];

private static readonly IReadOnlyList<string> s_excludedDirectories =
[ "bin", "obj", "node_modules", ".vs", "packages" ];

#endregion

Expand Down Expand Up @@ -91,20 +99,22 @@ public static async Task<IReadOnlyList<string>> GetFilesFromFoldersAsync(string[
{
ValidateFolderPaths(folderPaths);

var filePaths = new List<string>();
var filePaths = new ConcurrentBag<string>();

foreach (var folderPath in folderPaths)
await Parallel.ForEachAsync(folderPaths, async (folderPath, ct) =>
{
foreach (var extension in s_fileExtensionsToInclude)
await foreach (var file in Directory.EnumerateFiles(folderPath, "*.*", SearchOption.AllDirectories)
.ToAsyncEnumerable()
.Where(file =>
s_fileExtensionsToInclude.Any(ext => file.EndsWith(ext, StringComparison.OrdinalIgnoreCase)) &&
!s_excludedDirectories.Any(dir => file.Contains(Path.DirectorySeparatorChar + dir + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)) &&
!IsExcludedFile(file)))
{
filePaths.AddRange(
Directory.GetFiles(folderPath, $"*{extension}",
SearchOption.AllDirectories));
filePaths.Add(file);
}
}
});

return await Task.FromResult<IReadOnlyList<string>>(
filePaths.Where(f => !IsExcludedFile(f)).ToList().AsReadOnly());
return filePaths.ToList().AsReadOnly();
}

public static async Task<IReadOnlyList<string>> GetFilesFromFilesAsync(string[] filePaths)
Expand Down Expand Up @@ -140,7 +150,7 @@ private static void ValidateFolderPaths(string[] folderPaths)

var invalidPaths = folderPaths.Where(fp => !Directory.Exists(fp)).ToArray();

if (invalidPaths.Any())
if (invalidPaths.Length != 0)
{
throw new DirectoryNotFoundException("One or more folder paths do not exist: " + string.Join(", ", invalidPaths));
}
Expand All @@ -155,7 +165,7 @@ private static void ValidateFilePaths(string[] filePaths)

var invalidPaths = filePaths.Where(fp => !File.Exists(fp)).ToArray();

if (invalidPaths.Any())
if (invalidPaths.Length != 0)
{
throw new FileNotFoundException("One or more files do not exist: " + string.Join(", ", invalidPaths));
}
Expand Down
4 changes: 2 additions & 2 deletions Legba.Engine/ViewModels/AboutViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@

public class AboutViewModel
{
private int initialCopyrightYear = 2023;
private const int INITIAL_COPYRIGHT_YEAR = 2023;

private static readonly Version s_version =
Assembly.GetExecutingAssembly().GetName().Version;

Check warning on line 12 in Legba.Engine/ViewModels/AboutViewModel.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference assignment.

public string VersionText =>
$"{s_version.Major}.{s_version.Minor}.{s_version.Revision}";
public string Copyright =>
$"© {(DateTime.Now.Year == initialCopyrightYear ? $"{initialCopyrightYear}" : $"{initialCopyrightYear} - {DateTime.Now.Year}")}, Lilly Software Consulting";
$"© {(DateTime.Now.Year == INITIAL_COPYRIGHT_YEAR ? $"{INITIAL_COPYRIGHT_YEAR}" : $"{INITIAL_COPYRIGHT_YEAR} - {DateTime.Now.Year}")}, Lilly Software Consulting";
public string License =>
"Licensed under the MIT License";
public string ContactInformation =>
Expand Down
34 changes: 34 additions & 0 deletions Legba.Engine/ViewModels/ChatSessionViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
using Microsoft.Extensions.DependencyInjection;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;

namespace Legba.Engine.ViewModels;

Expand Down Expand Up @@ -44,6 +47,7 @@
OnPropertyChanged(nameof(ChatSession));
OnPropertyChanged(nameof(HasChatSession));
OnPropertyChanged(nameof(HasChatMessages));
OnPropertyChanged(nameof(MessagesDocument));

if (_chatSession != null)
{
Expand All @@ -52,6 +56,35 @@
}
}

public FlowDocument MessagesDocument
{
get
{
var document = new FlowDocument();

if (ChatSession == null)
{
return document;
}

foreach (var message in ChatSession.Messages)
{
var paragraph = new Paragraph(new Run(message.DisplayText))
{
Margin = new Thickness(5),
TextAlignment = message.Alignment,
Background = message.BackgroundColor,
Padding = new Thickness(8),
FontSize = 14,
FontFamily = new FontFamily("Consolas")
};

document.Blocks.Add(paragraph);
}

return document;
}
}
public bool HasChatSession => ChatSession != null;
public bool HasChatMessages => ChatSession?.Messages.Count > 0;

Expand All @@ -73,13 +106,14 @@
}

SelectModelCommand = new TypedRelayCommand<Settings.Model>(SelectModel);
AskCommand = new RelayCommand(async () => await ChatSession.AskAsync());

Check warning on line 109 in Legba.Engine/ViewModels/ChatSessionViewModel.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.
RemoveSourceCodeFileCommand = new TypedRelayCommand<object>(RemoveSourceCodeFile);
}

private void Messages_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
OnPropertyChanged(nameof(HasChatMessages));
OnPropertyChanged(nameof(MessagesDocument));
}

private void SelectModel(Settings.Model model)
Expand Down
14 changes: 7 additions & 7 deletions Legba/Legba.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<UseWPF>true</UseWPF>
<UseWindowsForms>True</UseWindowsForms>
<ApplicationIcon>Images\LegbaIcon.ico</ApplicationIcon>
<Version>2.0.0.0</Version>
<Version>2.1.0.0</Version>
<UserSecretsId>f63a3566-54ef-4629-9a95-4254f119e9a0</UserSecretsId>
</PropertyGroup>

Expand All @@ -24,12 +24,12 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.5" />
</ItemGroup>

<ItemGroup>
Expand Down
41 changes: 9 additions & 32 deletions Legba/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -127,38 +127,15 @@
</StackPanel>

<!-- Response list box -->
<ListBox Grid.Row="1" Grid.Column="0"
x:Name="RequestResponseMessages"
Background="#F4F6F6"
HorizontalContentAlignment="Stretch"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding ChatSession.Messages}">
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Copy to Clipboard"
Style="{StaticResource NoIconMenuItemStyle}"
Click="MenuItemCopyToClipboard_Click"/>
</ContextMenu>
</ListBox.ContextMenu>
<ListBox.ItemTemplate>
<DataTemplate>
<Border Background="{Binding MessageBackground}"
CornerRadius="3"
Margin="3"
HorizontalAlignment="{Binding MessageAlignment}">
<Border.MaxWidth>
<MultiBinding Converter="{StaticResource RelativeWidthConverter}">
<Binding Path="ActualWidth" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type ListBox}}" />
<Binding Source="0.7" />
</MultiBinding>
</Border.MaxWidth>
<TextBlock Text="{Binding DisplayText}"
TextWrapping="Wrap"
Padding="5"/>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<FlowDocumentScrollViewer
Grid.Row="1" Grid.Column="0"
Grid.ColumnSpan="2"
Name="MessageViewer"
VerticalScrollBarVisibility="Auto"
IsToolBarVisible="False"
Padding="10"
Margin="10"
Document="{Binding MessagesDocument}" />

<!-- GridSplitter -->
<GridSplitter Grid.Row="2" Grid.Column="0"
Expand Down
15 changes: 7 additions & 8 deletions Legba/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ public partial class MainWindow : Window
{
private HelpView? _helpView;

private static readonly FileConsolidator s_fileConsolidator = new();

private readonly IServiceProvider _serviceProvider;

private readonly ChatSessionViewModel? _chatSessionViewModel;
Expand Down Expand Up @@ -121,14 +119,15 @@ private void ClearPersonality_Click(object sender, RoutedEventArgs e)

private void MenuItemCopyToClipboard_Click(object sender, RoutedEventArgs e)
{
if (RequestResponseMessages.SelectedIndex == -1)
{
return;
}
// TODO: Implement copy to clipboard functionality for messages
//if (RequestResponseMessages.SelectedIndex == -1)
//{
// return;
//}

var message = (Engine.Models.OpenAi.Message)RequestResponseMessages.SelectedItem;
//var message = (Engine.Models.OpenAi.Message)RequestResponseMessages.SelectedItem;

System.Windows.Clipboard.SetText(message.Content);
//System.Windows.Clipboard.SetText(message.Content);
}

private async void AddSolution_Click(object sender, RoutedEventArgs e)
Expand Down
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,5 @@ Please report any problems at one of these places:
- [Discussions](https://github.com/LillySoftwareConsulting/Legba/discussions)

# Future Plans
- Convert the UI to something that runs cross-platform - probably [Avalonia](https://avaloniaui.net/).
- Able to share prompts with friends, co-workers, or the public.
- Possibly host a web service to share prompt prefixes.
- Connect to other LLMs besides OpenAI (on the web and/or locally).
4 changes: 4 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# RELEASE NOTES

## Version 2.1.0 (2025-??-??)
### Features
* Changed UI to make requests and responses easier to scroll through.

## Version 2.0.0 (2025-05-15)
### Breaking Changes
* Now uses a single "Personality" prompt prefix, instead of previous "Persona", "Purpose", "Persuasion", and "Process" prefixes.
Expand Down
Loading