feature: simple implementation for generating commit message by OpenAI (#456)

This commit is contained in:
leo 2024-09-11 18:22:05 +08:00
parent a63450f73f
commit 16f8e2fd0b
No known key found for this signature in database
16 changed files with 496 additions and 30 deletions

View file

@ -0,0 +1,60 @@
<v:ChromelessWindow xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:v="using:SourceGit.Views"
xmlns:vm="using:SourceGit.ViewModels"
xmlns:c="using:SourceGit.Converters"
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="120"
x:Class="SourceGit.Views.AIAssistant"
x:DataType="vm:WorkingCopy"
x:Name="ThisControl"
Icon="/App.ico"
Title="{DynamicResource Text.AIAssistant}"
Width="400" Height="120"
CanResize="False"
WindowStartupLocation="CenterOwner">
<Grid RowDefinitions="Auto,Auto,Auto">
<!-- TitleBar -->
<Grid Grid.Row="0" ColumnDefinitions="Auto,*,Auto" Height="30" IsVisible="{Binding !#ThisControl.UseSystemWindowFrame}">
<Border Grid.Column="0" Grid.ColumnSpan="3"
Background="{DynamicResource Brush.TitleBar}"
BorderThickness="0,0,0,1" BorderBrush="{DynamicResource Brush.Border0}"
PointerPressed="BeginMoveWindow"/>
<Path Grid.Column="0"
Width="14" Height="14"
Margin="10,0,0,0"
Data="{StaticResource Icons.AIAssist}"
IsVisible="{OnPlatform True, macOS=False}"/>
<v:CaptionButtonsMacOS Grid.Column="0"
Margin="0,2,0,0"
IsCloseButtonOnly="True"
IsVisible="{OnPlatform False, macOS=True}"/>
<TextBlock Grid.Column="0" Grid.ColumnSpan="3"
Classes="bold"
Text="{DynamicResource Text.AIAssistant}"
HorizontalAlignment="Center" VerticalAlignment="Center"
IsHitTestVisible="False"/>
<v:CaptionButtons Grid.Column="2"
IsCloseButtonOnly="True"
IsVisible="{OnPlatform True, macOS=False}"/>
</Grid>
<!-- Animated Icon -->
<v:LoadingIcon Grid.Row="1"
Width="24" Height="24"
Margin="0,16,0,0"/>
<!-- Message -->
<TextBlock Grid.Row="2"
x:Name="ProgressMessage"
Margin="16"
FontSize="{Binding Source={x:Static vm:Preference.Instance}, Path=DefaultFontSize, Converter={x:Static c:DoubleConverters.Decrease}}"
HorizontalAlignment="Center"
TextTrimming="CharacterEllipsis"/>
</Grid>
</v:ChromelessWindow>

View file

@ -0,0 +1,61 @@
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Threading;
namespace SourceGit.Views
{
public partial class AIAssistant : ChromelessWindow
{
public AIAssistant()
{
_cancel = new CancellationTokenSource();
InitializeComponent();
ProgressMessage.Text = "Generating commit message... Please wait!";
}
public void GenerateCommitMessage()
{
if (DataContext is ViewModels.WorkingCopy vm)
{
Task.Run(() =>
{
var message = new Commands.GenerateCommitMessage(vm.RepoPath, vm.Staged, _cancel.Token, SetDescription).Result();
if (_cancel.IsCancellationRequested)
return;
Dispatcher.UIThread.Invoke(() =>
{
if (DataContext is ViewModels.WorkingCopy wc)
wc.CommitMessage = message;
Close();
});
}, _cancel.Token);
}
}
protected override void OnClosing(WindowClosingEventArgs e)
{
base.OnClosing(e);
_cancel.Cancel();
}
private void BeginMoveWindow(object _, PointerPressedEventArgs e)
{
BeginMoveDrag(e);
}
private void SetDescription(string message)
{
Dispatcher.UIThread.Invoke(() =>
{
ProgressMessage.Text = message;
});
}
private CancellationTokenSource _cancel;
}
}

View file

@ -348,6 +348,41 @@
</Grid>
</TabItem>
<TabItem>
<TabItem.Header>
<TextBlock Classes="tab_header" Text="{DynamicResource Text.Preference.AI}"/>
</TabItem.Header>
<Grid Margin="8" RowDefinitions="32,32,32" ColumnDefinitions="Auto,*">
<TextBlock Grid.Row="0" Grid.Column="0"
Text="{DynamicResource Text.Preference.AI.Server}"
HorizontalAlignment="Right"
Margin="0,0,16,0"/>
<TextBox Grid.Row="0" Grid.Column="1"
Height="28"
CornerRadius="3"
Text="{Binding OpenAIServer, Mode=TwoWay}"/>
<TextBlock Grid.Row="1" Grid.Column="0"
Text="{DynamicResource Text.Preference.AI.ApiKey}"
HorizontalAlignment="Right"
Margin="0,0,16,0"/>
<TextBox Grid.Row="1" Grid.Column="1"
Height="28"
CornerRadius="3"
Text="{Binding OpenAIApiKey, Mode=TwoWay}"/>
<TextBlock Grid.Row="2" Grid.Column="0"
Text="{DynamicResource Text.Preference.AI.Model}"
HorizontalAlignment="Right"
Margin="0,0,16,0"/>
<TextBox Grid.Row="2" Grid.Column="1"
Height="28"
CornerRadius="3"
Text="{Binding OpenAIModel, Mode=TwoWay}"/>
</Grid>
</TabItem>
<TabItem>
<TabItem.Header>
<TextBlock Classes="tab_header" Text="{DynamicResource Text.Preference.GPG}"/>

View file

@ -5,7 +5,7 @@
xmlns:vm="using:SourceGit.ViewModels"
xmlns:v="using:SourceGit.Views"
xmlns:c="using:SourceGit.Converters"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="600"
x:Class="SourceGit.Views.WorkingCopy"
x:DataType="vm:WorkingCopy">
<Grid>
@ -173,34 +173,41 @@
<v:CommitMessageTextBox Grid.Row="2" Text="{Binding CommitMessage, Mode=TwoWay}"/>
<!-- Commit Options -->
<Grid Grid.Row="3" Margin="0,6,0,0" ColumnDefinitions="Auto,Auto,Auto,*,Auto,Auto,Auto">
<Grid Grid.Row="3" Margin="0,6,0,0" ColumnDefinitions="Auto,Auto,Auto,Auto,*,Auto,Auto,Auto">
<Button Grid.Column="0"
Classes="no_border"
Classes="icon_button"
Margin="4,0,0,0" Padding="0"
Click="OnOpenCommitMessagePicker">
<Grid ColumnDefinitions="Auto,*">
<Path Grid.Column="0" Width="12" Height="12" Data="{StaticResource Icons.Menu}"/>
<TextBlock Grid.Column="1" Margin="8,0,0,0" Text="{DynamicResource Text.WorkingCopy.CommitMessageHelper}"/>
</Grid>
Click="OnOpenCommitMessagePicker"
ToolTip.Tip="{DynamicResource Text.WorkingCopy.CommitMessageHelper}">
<Path Grid.Column="0" Width="12" Height="12" Data="{StaticResource Icons.Menu}"/>
</Button>
<CheckBox Grid.Column="1"
<Button Grid.Column="1"
Classes="icon_button"
Width="32"
Margin="4,0,0,0"
Click="OnOpenAIAssist"
ToolTip.Tip="{DynamicResource Text.AIAssistant.Tip}">
<Path Width="14" Height="14" Data="{StaticResource Icons.AIAssist}"/>
</Button>
<CheckBox Grid.Column="2"
Height="24"
Margin="12,0,0,0"
Margin="8,0,0,0"
HorizontalAlignment="Left"
IsChecked="{Binding AutoStageBeforeCommit, Mode=TwoWay}"
Content="{DynamicResource Text.WorkingCopy.AutoStage}"/>
<CheckBox Grid.Column="2"
<CheckBox Grid.Column="3"
Height="24"
Margin="12,0,0,0"
Margin="8,0,0,0"
HorizontalAlignment="Left"
IsChecked="{Binding UseAmend, Mode=TwoWay}"
Content="{DynamicResource Text.WorkingCopy.Amend}"/>
<v:LoadingIcon Grid.Column="4" Width="18" Height="18" IsVisible="{Binding IsCommitting}"/>
<v:LoadingIcon Grid.Column="5" Width="18" Height="18" IsVisible="{Binding IsCommitting}"/>
<Button Grid.Column="5"
<Button Grid.Column="6"
Classes="flat primary"
Content="{DynamicResource Text.WorkingCopy.Commit}"
Height="28"
@ -210,7 +217,7 @@
HotKey="{OnPlatform Ctrl+Enter, macOS=⌘+Enter}"
ToolTip.Tip="{OnPlatform Ctrl+Enter, macOS=⌘+Enter}"/>
<Button Grid.Column="6"
<Button Grid.Column="7"
Classes="flat"
Content="{DynamicResource Text.WorkingCopy.CommitAndPush}"
Height="28"

View file

@ -88,5 +88,23 @@ namespace SourceGit.Views
e.Handled = true;
}
}
private void OnOpenAIAssist(object _, RoutedEventArgs e)
{
if (!Models.OpenAI.IsValid)
{
App.RaiseException(null, $"Bad configuration for OpenAI");
return;
}
if (DataContext is ViewModels.WorkingCopy vm && vm.Staged is { Count: > 0 })
{
var dialog = new AIAssistant() { DataContext = vm };
dialog.GenerateCommitMessage();
App.OpenDialog(dialog);
}
e.Handled = true;
}
}
}