mirror of
https://github.com/sourcegit-scm/sourcegit.git
synced 2024-12-27 21:27:19 -08:00
Compare commits
24 commits
a8ae887320
...
651d313496
Author | SHA1 | Date | |
---|---|---|---|
|
651d313496 | ||
|
498d2b54ae | ||
|
48725a7937 | ||
|
a363f67f31 | ||
|
6c390d2f04 | ||
|
6cc0c54ac1 | ||
|
1418591b0b | ||
|
a7cccd5c1d | ||
|
566d36ca59 | ||
|
3e6e0befaa | ||
|
216d2d02bd | ||
|
134d69d403 | ||
|
9d1840f78c | ||
|
bb47dc28ef | ||
|
f1dc46c251 | ||
|
148e2fa1e5 | ||
|
3490d62f3c | ||
|
1044915be1 | ||
|
8280287362 | ||
|
ff0354d606 | ||
|
bb29476a80 | ||
|
9caa20cef0 | ||
|
6447590491 | ||
|
bde648eae8 |
23 changed files with 562 additions and 317 deletions
21
README.md
21
README.md
|
@ -1,6 +1,10 @@
|
||||||
# SourceGit - Opensource Git GUI client.
|
# SourceGit - Opensource Git GUI client.
|
||||||
|
|
||||||
![stars](https://img.shields.io/github/stars/sourcegit-scm/sourcegit.svg) ![forks](https://img.shields.io/github/forks/sourcegit-scm/sourcegit.svg) ![license](https://img.shields.io/github/license/sourcegit-scm/sourcegit.svg) ![latest](https://img.shields.io/github/v/release/sourcegit-scm/sourcegit.svg) ![downloads](https://img.shields.io/github/downloads/sourcegit-scm/sourcegit/total)
|
[![stars](https://img.shields.io/github/stars/sourcegit-scm/sourcegit.svg)](https://github.com/sourcegit-scm/sourcegit/stargazers)
|
||||||
|
[![forks](https://img.shields.io/github/forks/sourcegit-scm/sourcegit.svg)](https://github.com/sourcegit-scm/sourcegit/forks)
|
||||||
|
[![license](https://img.shields.io/github/license/sourcegit-scm/sourcegit.svg)](LICENSE)
|
||||||
|
[![latest](https://img.shields.io/github/v/release/sourcegit-scm/sourcegit.svg)](https://github.com/sourcegit-scm/sourcegit/releases/latest)
|
||||||
|
[![downloads](https://img.shields.io/github/downloads/sourcegit-scm/sourcegit/total)](https://github.com/sourcegit-scm/sourcegit/releases)
|
||||||
|
|
||||||
## Highlights
|
## Highlights
|
||||||
|
|
||||||
|
@ -43,7 +47,7 @@
|
||||||
|
|
||||||
## Translation Status
|
## Translation Status
|
||||||
|
|
||||||
[![en_US](https://img.shields.io/badge/en__US-100%25-brightgreen)](TRANSLATION.md) [![de__DE](https://img.shields.io/badge/de__DE-98.95%25-yellow)](TRANSLATION.md) [![fr__FR](https://img.shields.io/badge/fr__FR-90.36%25-yellow)](TRANSLATION.md) [![pt__BR](https://img.shields.io/badge/pt__BR-93.52%25-yellow)](TRANSLATION.md) [![ru__RU](https://img.shields.io/badge/ru__RU-98.80%25-yellow)](TRANSLATION.md) [![zh__CN](https://img.shields.io/badge/zh__CN-99.10%25-yellow)](TRANSLATION.md) [![zh__TW](https://img.shields.io/badge/zh__TW-100.00%25-brightgreen)](TRANSLATION.md)
|
[![en_US](https://img.shields.io/badge/en__US-100%25-brightgreen)](TRANSLATION.md) [![de__DE](https://img.shields.io/badge/de__DE-98.21%25-yellow)](TRANSLATION.md) [![fr__FR](https://img.shields.io/badge/fr__FR-89.69%25-yellow)](TRANSLATION.md) [![pt__BR](https://img.shields.io/badge/pt__BR-92.83%25-yellow)](TRANSLATION.md) [![ru__RU](https://img.shields.io/badge/ru__RU-99.25%25-yellow)](TRANSLATION.md) [![zh__CN](https://img.shields.io/badge/zh__CN-100.00%25-brightgreen)](TRANSLATION.md) [![zh__TW](https://img.shields.io/badge/zh__TW-100.00%25-brightgreen)](TRANSLATION.md)
|
||||||
|
|
||||||
## How to Use
|
## How to Use
|
||||||
|
|
||||||
|
@ -80,11 +84,16 @@ For **Windows** users:
|
||||||
|
|
||||||
For **macOS** users:
|
For **macOS** users:
|
||||||
|
|
||||||
* Download `sourcegit_x.y.osx-x64.zip` or `sourcegit_x.y.osx-arm64.zip` from Releases. `x64` for Intel and `arm64` for Apple Silicon.
|
* Thanks [@ybeapps](https://github.com/ybeapps) for making `SourceGit` available on `Homebrew`. You can simply install it with following command:
|
||||||
* Move `SourceGit.app` to `Applications` folder.
|
```shell
|
||||||
* Make sure your mac trusts all software from anywhere. For more information, search `spctl --master-disable`.
|
brew tap ybeapps/homebrew-sourcegit
|
||||||
|
brew install --cask --no-quarantine sourcegit
|
||||||
|
```
|
||||||
|
* If you want to install `SourceGit.app` from Github Release manually, you need run following command to make sure it works:
|
||||||
|
```shell
|
||||||
|
sudo xattr -cr /Applications/SourceGit.app
|
||||||
|
```
|
||||||
* Make sure [git-credential-manager](https://github.com/git-ecosystem/git-credential-manager/releases) is installed on your mac.
|
* Make sure [git-credential-manager](https://github.com/git-ecosystem/git-credential-manager/releases) is installed on your mac.
|
||||||
* You may need to run `sudo xattr -cr /Applications/SourceGit.app` to make sure the software works.
|
|
||||||
* You can run `echo $PATH > ~/Library/Application\ Support/SourceGit/PATH` to generate a custom PATH env file to introduce `PATH` env to SourceGit.
|
* You can run `echo $PATH > ~/Library/Application\ Support/SourceGit/PATH` to generate a custom PATH env file to introduce `PATH` env to SourceGit.
|
||||||
|
|
||||||
For **Linux** users:
|
For **Linux** users:
|
||||||
|
|
|
@ -1,20 +1,25 @@
|
||||||
### de_DE.axaml: 98.95%
|
### de_DE.axaml: 98.21%
|
||||||
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Missing Keys</summary>
|
<summary>Missing Keys</summary>
|
||||||
|
|
||||||
|
- Text.ChangeCM.GenerateCommitMessage
|
||||||
- Text.Configure.Git.EnableSignOff
|
- Text.Configure.Git.EnableSignOff
|
||||||
- Text.Configure.IssueTracker.AddSampleGitLabIssue
|
- Text.Configure.IssueTracker.AddSampleGitLabIssue
|
||||||
- Text.Configure.IssueTracker.AddSampleGitLabMergeRequest
|
- Text.Configure.IssueTracker.AddSampleGitLabMergeRequest
|
||||||
- Text.Preference.Advanced
|
- Text.Configure.OpenAI
|
||||||
|
- Text.Configure.OpenAI.Prefered
|
||||||
|
- Text.Configure.OpenAI.Prefered.Tip
|
||||||
- Text.Preference.AI.AnalyzeDiffPrompt
|
- Text.Preference.AI.AnalyzeDiffPrompt
|
||||||
- Text.Preference.AI.GenerateSubjectPrompt
|
- Text.Preference.AI.GenerateSubjectPrompt
|
||||||
|
- Text.Preference.AI.Name
|
||||||
|
- Text.Stash.KeepIndex
|
||||||
- Text.WorkingCopy.ConfirmCommitWithoutFiles
|
- Text.WorkingCopy.ConfirmCommitWithoutFiles
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### fr_FR.axaml: 90.36%
|
### fr_FR.axaml: 89.69%
|
||||||
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
@ -23,6 +28,7 @@
|
||||||
- Text.About.Chart
|
- Text.About.Chart
|
||||||
- Text.AIAssistant
|
- Text.AIAssistant
|
||||||
- Text.AIAssistant.Tip
|
- Text.AIAssistant.Tip
|
||||||
|
- Text.ChangeCM.GenerateCommitMessage
|
||||||
- Text.CherryPick.AppendSourceToMessage
|
- Text.CherryPick.AppendSourceToMessage
|
||||||
- Text.CherryPick.Mainline
|
- Text.CherryPick.Mainline
|
||||||
- Text.CherryPick.Mainline.Tips
|
- Text.CherryPick.Mainline.Tips
|
||||||
|
@ -33,6 +39,9 @@
|
||||||
- Text.Configure.Git.EnableSignOff
|
- Text.Configure.Git.EnableSignOff
|
||||||
- Text.Configure.IssueTracker.AddSampleGitLabIssue
|
- Text.Configure.IssueTracker.AddSampleGitLabIssue
|
||||||
- Text.Configure.IssueTracker.AddSampleGitLabMergeRequest
|
- Text.Configure.IssueTracker.AddSampleGitLabMergeRequest
|
||||||
|
- Text.Configure.OpenAI
|
||||||
|
- Text.Configure.OpenAI.Prefered
|
||||||
|
- Text.Configure.OpenAI.Prefered.Tip
|
||||||
- Text.ConfigureWorkspace
|
- Text.ConfigureWorkspace
|
||||||
- Text.ConfigureWorkspace.Color
|
- Text.ConfigureWorkspace.Color
|
||||||
- Text.ConfigureWorkspace.Restore
|
- Text.ConfigureWorkspace.Restore
|
||||||
|
@ -55,12 +64,12 @@
|
||||||
- Text.Hotkeys.Repo.DiscardSelected
|
- Text.Hotkeys.Repo.DiscardSelected
|
||||||
- Text.MoveRepositoryNode
|
- Text.MoveRepositoryNode
|
||||||
- Text.MoveRepositoryNode.Target
|
- Text.MoveRepositoryNode.Target
|
||||||
- Text.Preference.Advanced
|
|
||||||
- Text.Preference.AI
|
- Text.Preference.AI
|
||||||
- Text.Preference.AI.AnalyzeDiffPrompt
|
- Text.Preference.AI.AnalyzeDiffPrompt
|
||||||
- Text.Preference.AI.ApiKey
|
- Text.Preference.AI.ApiKey
|
||||||
- Text.Preference.AI.GenerateSubjectPrompt
|
- Text.Preference.AI.GenerateSubjectPrompt
|
||||||
- Text.Preference.AI.Model
|
- Text.Preference.AI.Model
|
||||||
|
- Text.Preference.AI.Name
|
||||||
- Text.Preference.AI.Server
|
- Text.Preference.AI.Server
|
||||||
- Text.Preference.General.ShowAuthorTime
|
- Text.Preference.General.ShowAuthorTime
|
||||||
- Text.Preference.Integration
|
- Text.Preference.Integration
|
||||||
|
@ -73,6 +82,7 @@
|
||||||
- Text.ScanRepositories
|
- Text.ScanRepositories
|
||||||
- Text.ScanRepositories.RootDir
|
- Text.ScanRepositories.RootDir
|
||||||
- Text.Squash.Into
|
- Text.Squash.Into
|
||||||
|
- Text.Stash.KeepIndex
|
||||||
- Text.Stash.OnlyStagedChanges
|
- Text.Stash.OnlyStagedChanges
|
||||||
- Text.Stash.TipForSelectedFiles
|
- Text.Stash.TipForSelectedFiles
|
||||||
- Text.Statistics.Overview
|
- Text.Statistics.Overview
|
||||||
|
@ -87,7 +97,7 @@
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### pt_BR.axaml: 93.52%
|
### pt_BR.axaml: 92.83%
|
||||||
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
@ -96,6 +106,7 @@
|
||||||
- Text.About.Chart
|
- Text.About.Chart
|
||||||
- Text.AIAssistant
|
- Text.AIAssistant
|
||||||
- Text.AIAssistant.Tip
|
- Text.AIAssistant.Tip
|
||||||
|
- Text.ChangeCM.GenerateCommitMessage
|
||||||
- Text.CherryPick.AppendSourceToMessage
|
- Text.CherryPick.AppendSourceToMessage
|
||||||
- Text.CherryPick.Mainline
|
- Text.CherryPick.Mainline
|
||||||
- Text.CherryPick.Mainline.Tips
|
- Text.CherryPick.Mainline.Tips
|
||||||
|
@ -108,6 +119,9 @@
|
||||||
- Text.Configure.Git.EnableSignOff
|
- Text.Configure.Git.EnableSignOff
|
||||||
- Text.Configure.IssueTracker.AddSampleGitLabIssue
|
- Text.Configure.IssueTracker.AddSampleGitLabIssue
|
||||||
- Text.Configure.IssueTracker.AddSampleGitLabMergeRequest
|
- Text.Configure.IssueTracker.AddSampleGitLabMergeRequest
|
||||||
|
- Text.Configure.OpenAI
|
||||||
|
- Text.Configure.OpenAI.Prefered
|
||||||
|
- Text.Configure.OpenAI.Prefered.Tip
|
||||||
- Text.ConfigureWorkspace
|
- Text.ConfigureWorkspace
|
||||||
- Text.ConfigureWorkspace.Color
|
- Text.ConfigureWorkspace.Color
|
||||||
- Text.ConfigureWorkspace.Restore
|
- Text.ConfigureWorkspace.Restore
|
||||||
|
@ -125,9 +139,10 @@
|
||||||
- Text.GitLFS.Locks.OnlyMine
|
- Text.GitLFS.Locks.OnlyMine
|
||||||
- Text.MoveRepositoryNode
|
- Text.MoveRepositoryNode
|
||||||
- Text.MoveRepositoryNode.Target
|
- Text.MoveRepositoryNode.Target
|
||||||
- Text.Preference.Advanced
|
- Text.Preference.AI.Name
|
||||||
- Text.Push.CheckSubmodules
|
- Text.Push.CheckSubmodules
|
||||||
- Text.Squash.Into
|
- Text.Squash.Into
|
||||||
|
- Text.Stash.KeepIndex
|
||||||
- Text.Stash.OnlyStagedChanges
|
- Text.Stash.OnlyStagedChanges
|
||||||
- Text.Stash.TipForSelectedFiles
|
- Text.Stash.TipForSelectedFiles
|
||||||
- Text.Statistics.Overview
|
- Text.Statistics.Overview
|
||||||
|
@ -139,35 +154,27 @@
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### ru_RU.axaml: 98.80%
|
### ru_RU.axaml: 99.25%
|
||||||
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Missing Keys</summary>
|
<summary>Missing Keys</summary>
|
||||||
|
|
||||||
- Text.Configure.Git.EnableSignOff
|
- Text.ChangeCM.GenerateCommitMessage
|
||||||
- Text.Configure.IssueTracker.AddSampleGitLabIssue
|
- Text.Configure.OpenAI
|
||||||
- Text.Configure.IssueTracker.AddSampleGitLabMergeRequest
|
- Text.Configure.OpenAI.Prefered
|
||||||
- Text.Preference.Advanced
|
- Text.Configure.OpenAI.Prefered.Tip
|
||||||
- Text.Preference.AI.AnalyzeDiffPrompt
|
- Text.Stash.KeepIndex
|
||||||
- Text.Preference.AI.GenerateSubjectPrompt
|
|
||||||
- Text.Repository.EnableReflog
|
|
||||||
- Text.WorkingCopy.ConfirmCommitWithoutFiles
|
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### zh_CN.axaml: 99.10%
|
### zh_CN.axaml: 100.00%
|
||||||
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Missing Keys</summary>
|
<summary>Missing Keys</summary>
|
||||||
|
|
||||||
- Text.Preference.AI
|
|
||||||
- Text.Preference.AI.AnalyzeDiffPrompt
|
|
||||||
- Text.Preference.AI.ApiKey
|
|
||||||
- Text.Preference.AI.GenerateSubjectPrompt
|
|
||||||
- Text.Preference.AI.Model
|
|
||||||
- Text.Preference.AI.Server
|
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
8.35
|
8.36
|
|
@ -20,8 +20,9 @@ namespace SourceGit.Commands
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public GenerateCommitMessage(string repo, List<Models.Change> changes, CancellationToken cancelToken, Action<string> onProgress)
|
public GenerateCommitMessage(Models.OpenAIService service, string repo, List<Models.Change> changes, CancellationToken cancelToken, Action<string> onProgress)
|
||||||
{
|
{
|
||||||
|
_service = service;
|
||||||
_repo = repo;
|
_repo = repo;
|
||||||
_changes = changes;
|
_changes = changes;
|
||||||
_cancelToken = cancelToken;
|
_cancelToken = cancelToken;
|
||||||
|
@ -75,7 +76,7 @@ namespace SourceGit.Commands
|
||||||
var rs = new GetDiffContent(_repo, new Models.DiffOption(change, false)).ReadToEnd();
|
var rs = new GetDiffContent(_repo, new Models.DiffOption(change, false)).ReadToEnd();
|
||||||
var diff = rs.IsSuccess ? rs.StdOut : "unknown change";
|
var diff = rs.IsSuccess ? rs.StdOut : "unknown change";
|
||||||
|
|
||||||
var rsp = Models.OpenAI.Chat(Models.OpenAI.AnalyzeDiffPrompt, $"Here is the `git diff` output: {diff}", _cancelToken);
|
var rsp = _service.Chat(_service.AnalyzeDiffPrompt, $"Here is the `git diff` output: {diff}", _cancelToken);
|
||||||
if (rsp != null && rsp.Choices.Count > 0)
|
if (rsp != null && rsp.Choices.Count > 0)
|
||||||
return rsp.Choices[0].Message.Content;
|
return rsp.Choices[0].Message.Content;
|
||||||
|
|
||||||
|
@ -84,13 +85,14 @@ namespace SourceGit.Commands
|
||||||
|
|
||||||
private string GenerateSubject(string summary)
|
private string GenerateSubject(string summary)
|
||||||
{
|
{
|
||||||
var rsp = Models.OpenAI.Chat(Models.OpenAI.GenerateSubjectPrompt, $"Here are the summaries changes:\n{summary}", _cancelToken);
|
var rsp = _service.Chat(_service.GenerateSubjectPrompt, $"Here are the summaries changes:\n{summary}", _cancelToken);
|
||||||
if (rsp != null && rsp.Choices.Count > 0)
|
if (rsp != null && rsp.Choices.Count > 0)
|
||||||
return rsp.Choices[0].Message.Content;
|
return rsp.Choices[0].Message.Content;
|
||||||
|
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Models.OpenAIService _service;
|
||||||
private string _repo;
|
private string _repo;
|
||||||
private List<Models.Change> _changes;
|
private List<Models.Change> _changes;
|
||||||
private CancellationToken _cancelToken;
|
private CancellationToken _cancelToken;
|
||||||
|
|
|
@ -17,24 +17,29 @@ namespace SourceGit.Commands
|
||||||
return Exec();
|
return Exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Push(List<Models.Change> changes, string message, bool onlyStaged)
|
public bool Push(List<Models.Change> changes, string message, bool onlyStaged, bool keepIndex)
|
||||||
{
|
{
|
||||||
var pathsBuilder = new StringBuilder();
|
var builder = new StringBuilder();
|
||||||
|
builder.Append("stash push ");
|
||||||
|
if (onlyStaged)
|
||||||
|
builder.Append("--staged ");
|
||||||
|
if (keepIndex)
|
||||||
|
builder.Append("--keep-index ");
|
||||||
|
builder.Append("-m \"");
|
||||||
|
builder.Append(message);
|
||||||
|
builder.Append("\" -- ");
|
||||||
|
|
||||||
if (onlyStaged)
|
if (onlyStaged)
|
||||||
{
|
{
|
||||||
foreach (var c in changes)
|
foreach (var c in changes)
|
||||||
pathsBuilder.Append($"\"{c.Path}\" ");
|
builder.Append($"\"{c.Path}\" ");
|
||||||
|
|
||||||
var paths = pathsBuilder.ToString();
|
|
||||||
Args = $"stash push --staged -m \"{message}\" -- {paths}";
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var needAdd = new List<Models.Change>();
|
var needAdd = new List<Models.Change>();
|
||||||
foreach (var c in changes)
|
foreach (var c in changes)
|
||||||
{
|
{
|
||||||
pathsBuilder.Append($"\"{c.Path}\" ");
|
builder.Append($"\"{c.Path}\" ");
|
||||||
|
|
||||||
if (c.WorkTree == Models.ChangeState.Added || c.WorkTree == Models.ChangeState.Untracked)
|
if (c.WorkTree == Models.ChangeState.Added || c.WorkTree == Models.ChangeState.Untracked)
|
||||||
{
|
{
|
||||||
|
@ -51,11 +56,9 @@ namespace SourceGit.Commands
|
||||||
new Add(WorkingDirectory, needAdd).Exec();
|
new Add(WorkingDirectory, needAdd).Exec();
|
||||||
needAdd.Clear();
|
needAdd.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
var paths = pathsBuilder.ToString();
|
|
||||||
Args = $"stash push -m \"{message}\" -- {paths}";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Args = builder.ToString();
|
||||||
return Exec();
|
return Exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,15 +50,9 @@
|
||||||
string sub2 = new string(tmp2, 0, loc2);
|
string sub2 = new string(tmp2, 0, loc2);
|
||||||
int result;
|
int result;
|
||||||
if (isDigit1 && isDigit2)
|
if (isDigit1 && isDigit2)
|
||||||
{
|
result = loc1 == loc2 ? string.CompareOrdinal(sub1, sub2) : loc1 - loc2;
|
||||||
int num1 = int.Parse(sub1);
|
|
||||||
int num2 = int.Parse(sub2);
|
|
||||||
result = num1 - num2;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
result = string.CompareOrdinal(sub1, sub2);
|
||||||
result = string.Compare(sub1, sub2, System.StringComparison.Ordinal);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result != 0)
|
if (result != 0)
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -6,6 +6,8 @@ using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
|
||||||
namespace SourceGit.Models
|
namespace SourceGit.Models
|
||||||
{
|
{
|
||||||
public class OpenAIChatMessage
|
public class OpenAIChatMessage
|
||||||
|
@ -74,44 +76,78 @@ namespace SourceGit.Models
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class OpenAI
|
public class OpenAIService : ObservableObject
|
||||||
{
|
{
|
||||||
public static string Server
|
public string Name
|
||||||
{
|
{
|
||||||
get;
|
get => _name;
|
||||||
set;
|
set => SetProperty(ref _name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string ApiKey
|
public string Server
|
||||||
{
|
{
|
||||||
get;
|
get => _server;
|
||||||
set;
|
set => SetProperty(ref _server, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string Model
|
public string ApiKey
|
||||||
{
|
{
|
||||||
get;
|
get => _apiKey;
|
||||||
set;
|
set => SetProperty(ref _apiKey, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string AnalyzeDiffPrompt
|
public string Model
|
||||||
{
|
{
|
||||||
get;
|
get => _model;
|
||||||
set;
|
set => SetProperty(ref _model, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GenerateSubjectPrompt
|
public string AnalyzeDiffPrompt
|
||||||
{
|
{
|
||||||
get;
|
get => _analyzeDiffPrompt;
|
||||||
set;
|
set => SetProperty(ref _analyzeDiffPrompt, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsValid
|
public string GenerateSubjectPrompt
|
||||||
{
|
{
|
||||||
get => !string.IsNullOrEmpty(Server) && !string.IsNullOrEmpty(Model);
|
get => _generateSubjectPrompt;
|
||||||
|
set => SetProperty(ref _generateSubjectPrompt, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static OpenAIChatResponse Chat(string prompt, string question, CancellationToken cancellation)
|
public OpenAIService()
|
||||||
|
{
|
||||||
|
AnalyzeDiffPrompt = """
|
||||||
|
You are an expert developer specialist in creating commits.
|
||||||
|
Provide a super concise one sentence overall changes summary of the user `git diff` output following strictly the next rules:
|
||||||
|
- Do not use any code snippets, imports, file routes or bullets points.
|
||||||
|
- Do not mention the route of file that has been change.
|
||||||
|
- Write clear, concise, and descriptive messages that explain the MAIN GOAL made of the changes.
|
||||||
|
- Use the present tense and active voice in the message, for example, "Fix bug" instead of "Fixed bug.".
|
||||||
|
- Use the imperative mood, which gives the message a sense of command, e.g. "Add feature" instead of "Added feature".
|
||||||
|
- Avoid using general terms like "update" or "change", be specific about what was updated or changed.
|
||||||
|
- Avoid using terms like "The main goal of", just output directly the summary in plain text
|
||||||
|
""";
|
||||||
|
|
||||||
|
GenerateSubjectPrompt = """
|
||||||
|
You are an expert developer specialist in creating commits messages.
|
||||||
|
Your only goal is to retrieve a single commit message.
|
||||||
|
Based on the provided user changes, combine them in ONE SINGLE commit message retrieving the global idea, following strictly the next rules:
|
||||||
|
- Assign the commit {type} according to the next conditions:
|
||||||
|
feat: Only when adding a new feature.
|
||||||
|
fix: When fixing a bug.
|
||||||
|
docs: When updating documentation.
|
||||||
|
style: When changing elements styles or design and/or making changes to the code style (formatting, missing semicolons, etc.) without changing the code logic.
|
||||||
|
test: When adding or updating tests.
|
||||||
|
chore: When making changes to the build process or auxiliary tools and libraries.
|
||||||
|
revert: When undoing a previous commit.
|
||||||
|
refactor: When restructuring code without changing its external behavior, or is any of the other refactor types.
|
||||||
|
- Do not add any issues numeration, explain your output nor introduce your answer.
|
||||||
|
- Output directly only one commit message in plain text with the next format: {type}: {commit_message}.
|
||||||
|
- Be as concise as possible, keep the message under 50 characters.
|
||||||
|
""";
|
||||||
|
}
|
||||||
|
|
||||||
|
public OpenAIChatResponse Chat(string prompt, string question, CancellationToken cancellation)
|
||||||
{
|
{
|
||||||
var chat = new OpenAIChatRequest() { Model = Model };
|
var chat = new OpenAIChatRequest() { Model = Model };
|
||||||
chat.AddMessage("system", prompt);
|
chat.AddMessage("system", prompt);
|
||||||
|
@ -144,5 +180,12 @@ namespace SourceGit.Models
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string _name;
|
||||||
|
private string _server;
|
||||||
|
private string _apiKey;
|
||||||
|
private string _model;
|
||||||
|
private string _analyzeDiffPrompt;
|
||||||
|
private string _generateSubjectPrompt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,6 +112,30 @@ namespace SourceGit.Models
|
||||||
set;
|
set;
|
||||||
} = false;
|
} = false;
|
||||||
|
|
||||||
|
public bool IncludeUntrackedWhenStash
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
} = true;
|
||||||
|
|
||||||
|
public bool OnlyStagedWhenStash
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
} = false;
|
||||||
|
|
||||||
|
public bool KeepIndexWhenStash
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
} = false;
|
||||||
|
|
||||||
|
public string PreferedOpenAIService
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
} = "---";
|
||||||
|
|
||||||
public void PushCommitMessage(string message)
|
public void PushCommitMessage(string message)
|
||||||
{
|
{
|
||||||
var existIdx = CommitMessages.IndexOf(message);
|
var existIdx = CommitMessages.IndexOf(message);
|
||||||
|
|
|
@ -69,6 +69,7 @@
|
||||||
<x:String x:Key="Text.Cancel" xml:space="preserve">CANCEL</x:String>
|
<x:String x:Key="Text.Cancel" xml:space="preserve">CANCEL</x:String>
|
||||||
<x:String x:Key="Text.ChangeCM.CheckoutThisRevision" xml:space="preserve">Reset to This Revision</x:String>
|
<x:String x:Key="Text.ChangeCM.CheckoutThisRevision" xml:space="preserve">Reset to This Revision</x:String>
|
||||||
<x:String x:Key="Text.ChangeCM.CheckoutFirstParentRevision" xml:space="preserve">Reset to Parent Revision</x:String>
|
<x:String x:Key="Text.ChangeCM.CheckoutFirstParentRevision" xml:space="preserve">Reset to Parent Revision</x:String>
|
||||||
|
<x:String x:Key="Text.ChangeCM.GenerateCommitMessage" xml:space="preserve">Generate commit message</x:String>
|
||||||
<x:String x:Key="Text.ChangeDisplayMode" xml:space="preserve">CHANGE DISPLAY MODE</x:String>
|
<x:String x:Key="Text.ChangeDisplayMode" xml:space="preserve">CHANGE DISPLAY MODE</x:String>
|
||||||
<x:String x:Key="Text.ChangeDisplayMode.Grid" xml:space="preserve">Show as File and Dir List</x:String>
|
<x:String x:Key="Text.ChangeDisplayMode.Grid" xml:space="preserve">Show as File and Dir List</x:String>
|
||||||
<x:String x:Key="Text.ChangeDisplayMode.List" xml:space="preserve">Show as Path List</x:String>
|
<x:String x:Key="Text.ChangeDisplayMode.List" xml:space="preserve">Show as Path List</x:String>
|
||||||
|
@ -154,6 +155,9 @@
|
||||||
<x:String x:Key="Text.Configure.IssueTracker.RuleName" xml:space="preserve">Rule Name:</x:String>
|
<x:String x:Key="Text.Configure.IssueTracker.RuleName" xml:space="preserve">Rule Name:</x:String>
|
||||||
<x:String x:Key="Text.Configure.IssueTracker.URLTemplate" xml:space="preserve">Result URL:</x:String>
|
<x:String x:Key="Text.Configure.IssueTracker.URLTemplate" xml:space="preserve">Result URL:</x:String>
|
||||||
<x:String x:Key="Text.Configure.IssueTracker.URLTemplate.Tip" xml:space="preserve">Please use $1, $2 to access regex groups values.</x:String>
|
<x:String x:Key="Text.Configure.IssueTracker.URLTemplate.Tip" xml:space="preserve">Please use $1, $2 to access regex groups values.</x:String>
|
||||||
|
<x:String x:Key="Text.Configure.OpenAI" xml:space="preserve">OPEN AI</x:String>
|
||||||
|
<x:String x:Key="Text.Configure.OpenAI.Prefered" xml:space="preserve">Prefered Service:</x:String>
|
||||||
|
<x:String x:Key="Text.Configure.OpenAI.Prefered.Tip" xml:space="preserve">If the 'Prefered Service' is set, SourceGit will only use it in this repository. Otherwise, if there is more than one service available, a context menu to choose one of them will be shown.</x:String>
|
||||||
<x:String x:Key="Text.Configure.Proxy" xml:space="preserve">HTTP Proxy</x:String>
|
<x:String x:Key="Text.Configure.Proxy" xml:space="preserve">HTTP Proxy</x:String>
|
||||||
<x:String x:Key="Text.Configure.Proxy.Placeholder" xml:space="preserve">HTTP proxy used by this repository</x:String>
|
<x:String x:Key="Text.Configure.Proxy.Placeholder" xml:space="preserve">HTTP proxy used by this repository</x:String>
|
||||||
<x:String x:Key="Text.Configure.User" xml:space="preserve">User Name</x:String>
|
<x:String x:Key="Text.Configure.User" xml:space="preserve">User Name</x:String>
|
||||||
|
@ -401,12 +405,12 @@
|
||||||
<x:String x:Key="Text.Period.LastYear" xml:space="preserve">Last year</x:String>
|
<x:String x:Key="Text.Period.LastYear" xml:space="preserve">Last year</x:String>
|
||||||
<x:String x:Key="Text.Period.YearsAgo" xml:space="preserve">{0} years ago</x:String>
|
<x:String x:Key="Text.Period.YearsAgo" xml:space="preserve">{0} years ago</x:String>
|
||||||
<x:String x:Key="Text.Preference" xml:space="preserve">Preference</x:String>
|
<x:String x:Key="Text.Preference" xml:space="preserve">Preference</x:String>
|
||||||
<x:String x:Key="Text.Preference.Advanced" xml:space="preserve">Advanced Options</x:String>
|
|
||||||
<x:String x:Key="Text.Preference.AI" xml:space="preserve">OPEN AI</x:String>
|
<x:String x:Key="Text.Preference.AI" xml:space="preserve">OPEN AI</x:String>
|
||||||
<x:String x:Key="Text.Preference.AI.AnalyzeDiffPrompt" xml:space="preserve">Analyze Diff Prompt</x:String>
|
<x:String x:Key="Text.Preference.AI.AnalyzeDiffPrompt" xml:space="preserve">Analyze Diff Prompt</x:String>
|
||||||
<x:String x:Key="Text.Preference.AI.ApiKey" xml:space="preserve">API Key</x:String>
|
<x:String x:Key="Text.Preference.AI.ApiKey" xml:space="preserve">API Key</x:String>
|
||||||
<x:String x:Key="Text.Preference.AI.GenerateSubjectPrompt" xml:space="preserve">Generate Subject Prompt</x:String>
|
<x:String x:Key="Text.Preference.AI.GenerateSubjectPrompt" xml:space="preserve">Generate Subject Prompt</x:String>
|
||||||
<x:String x:Key="Text.Preference.AI.Model" xml:space="preserve">Model</x:String>
|
<x:String x:Key="Text.Preference.AI.Model" xml:space="preserve">Model</x:String>
|
||||||
|
<x:String x:Key="Text.Preference.AI.Name" xml:space="preserve">Name</x:String>
|
||||||
<x:String x:Key="Text.Preference.AI.Server" xml:space="preserve">Server</x:String>
|
<x:String x:Key="Text.Preference.AI.Server" xml:space="preserve">Server</x:String>
|
||||||
<x:String x:Key="Text.Preference.Appearance" xml:space="preserve">APPEARANCE</x:String>
|
<x:String x:Key="Text.Preference.Appearance" xml:space="preserve">APPEARANCE</x:String>
|
||||||
<x:String x:Key="Text.Preference.Appearance.DefaultFont" xml:space="preserve">Default Font</x:String>
|
<x:String x:Key="Text.Preference.Appearance.DefaultFont" xml:space="preserve">Default Font</x:String>
|
||||||
|
@ -572,6 +576,7 @@
|
||||||
<x:String x:Key="Text.Start" xml:space="preserve">START</x:String>
|
<x:String x:Key="Text.Start" xml:space="preserve">START</x:String>
|
||||||
<x:String x:Key="Text.Stash" xml:space="preserve">Stash</x:String>
|
<x:String x:Key="Text.Stash" xml:space="preserve">Stash</x:String>
|
||||||
<x:String x:Key="Text.Stash.IncludeUntracked" xml:space="preserve">Include untracked files</x:String>
|
<x:String x:Key="Text.Stash.IncludeUntracked" xml:space="preserve">Include untracked files</x:String>
|
||||||
|
<x:String x:Key="Text.Stash.KeepIndex" xml:space="preserve">Keep staged files</x:String>
|
||||||
<x:String x:Key="Text.Stash.Message" xml:space="preserve">Message:</x:String>
|
<x:String x:Key="Text.Stash.Message" xml:space="preserve">Message:</x:String>
|
||||||
<x:String x:Key="Text.Stash.Message.Placeholder" xml:space="preserve">Optional. Name of this stash</x:String>
|
<x:String x:Key="Text.Stash.Message.Placeholder" xml:space="preserve">Optional. Name of this stash</x:String>
|
||||||
<x:String x:Key="Text.Stash.OnlyStagedChanges" xml:space="preserve">Only staged changes</x:String>
|
<x:String x:Key="Text.Stash.OnlyStagedChanges" xml:space="preserve">Only staged changes</x:String>
|
||||||
|
|
|
@ -74,8 +74,8 @@
|
||||||
<x:String x:Key="Text.ChangeCM.CheckoutFirstParentRevision" xml:space="preserve">Сбросить родительскую ревизию</x:String>
|
<x:String x:Key="Text.ChangeCM.CheckoutFirstParentRevision" xml:space="preserve">Сбросить родительскую ревизию</x:String>
|
||||||
<x:String x:Key="Text.ChangeDisplayMode" xml:space="preserve">ИЗМЕНИТЬ РЕЖИМ ОТОБРАЖЕНИЯ</x:String>
|
<x:String x:Key="Text.ChangeDisplayMode" xml:space="preserve">ИЗМЕНИТЬ РЕЖИМ ОТОБРАЖЕНИЯ</x:String>
|
||||||
<x:String x:Key="Text.ChangeDisplayMode.Grid" xml:space="preserve">Показывать в виде списка файлов и каталогов</x:String>
|
<x:String x:Key="Text.ChangeDisplayMode.Grid" xml:space="preserve">Показывать в виде списка файлов и каталогов</x:String>
|
||||||
<x:String x:Key="Text.ChangeDisplayMode.List" xml:space="preserve">Показать в виде списка путей</x:String>
|
<x:String x:Key="Text.ChangeDisplayMode.List" xml:space="preserve">Показывать в виде списка путей</x:String>
|
||||||
<x:String x:Key="Text.ChangeDisplayMode.Tree" xml:space="preserve">Показать в виде дерева файловой системы</x:String>
|
<x:String x:Key="Text.ChangeDisplayMode.Tree" xml:space="preserve">Показывать в виде дерева файловой системы</x:String>
|
||||||
<x:String x:Key="Text.Checkout" xml:space="preserve">Проверить ветку</x:String>
|
<x:String x:Key="Text.Checkout" xml:space="preserve">Проверить ветку</x:String>
|
||||||
<x:String x:Key="Text.Checkout.Commit" xml:space="preserve">Проверить фиксацию</x:String>
|
<x:String x:Key="Text.Checkout.Commit" xml:space="preserve">Проверить фиксацию</x:String>
|
||||||
<x:String x:Key="Text.Checkout.Commit.Warning" xml:space="preserve">Предупреждение: При выполнении проверки фиксации ваша голова будет отсоединена</x:String>
|
<x:String x:Key="Text.Checkout.Commit.Warning" xml:space="preserve">Предупреждение: При выполнении проверки фиксации ваша голова будет отсоединена</x:String>
|
||||||
|
@ -146,9 +146,12 @@
|
||||||
<x:String x:Key="Text.Configure.Git.AutoFetch" xml:space="preserve">Автоматическое извлечение внешних хранилищ</x:String>
|
<x:String x:Key="Text.Configure.Git.AutoFetch" xml:space="preserve">Автоматическое извлечение внешних хранилищ</x:String>
|
||||||
<x:String x:Key="Text.Configure.Git.AutoFetchIntervalSuffix" xml:space="preserve">Минут(а/ы)</x:String>
|
<x:String x:Key="Text.Configure.Git.AutoFetchIntervalSuffix" xml:space="preserve">Минут(а/ы)</x:String>
|
||||||
<x:String x:Key="Text.Configure.Git.DefaultRemote" xml:space="preserve">Удалённое хранилище по-умолчанию</x:String>
|
<x:String x:Key="Text.Configure.Git.DefaultRemote" xml:space="preserve">Удалённое хранилище по-умолчанию</x:String>
|
||||||
|
<x:String x:Key="Text.Configure.Git.EnableSignOff" xml:space="preserve">Разрешить --signoff для фиксации</x:String>
|
||||||
<x:String x:Key="Text.Configure.IssueTracker" xml:space="preserve">ОТСЛЕЖИВАНИЕ ПРОБЛЕМ</x:String>
|
<x:String x:Key="Text.Configure.IssueTracker" xml:space="preserve">ОТСЛЕЖИВАНИЕ ПРОБЛЕМ</x:String>
|
||||||
<x:String x:Key="Text.Configure.IssueTracker.AddSampleGithub" xml:space="preserve">Добавить пример правила для Git</x:String>
|
<x:String x:Key="Text.Configure.IssueTracker.AddSampleGithub" xml:space="preserve">Добавить пример правила для Git</x:String>
|
||||||
<x:String x:Key="Text.Configure.IssueTracker.AddSampleJira" xml:space="preserve">Добавить пример правила Jira</x:String>
|
<x:String x:Key="Text.Configure.IssueTracker.AddSampleJira" xml:space="preserve">Добавить пример правила Jira</x:String>
|
||||||
|
<x:String x:Key="Text.Configure.IssueTracker.AddSampleGitLabIssue" xml:space="preserve">Добавить пример правила выдачи GitLab</x:String>
|
||||||
|
<x:String x:Key="Text.Configure.IssueTracker.AddSampleGitLabMergeRequest" xml:space="preserve">Добавить пример правила запроса на слияние в GitLab</x:String>
|
||||||
<x:String x:Key="Text.Configure.IssueTracker.NewRule" xml:space="preserve">Новое правило</x:String>
|
<x:String x:Key="Text.Configure.IssueTracker.NewRule" xml:space="preserve">Новое правило</x:String>
|
||||||
<x:String x:Key="Text.Configure.IssueTracker.Regex" xml:space="preserve">Проблема с регулярным выражением:</x:String>
|
<x:String x:Key="Text.Configure.IssueTracker.Regex" xml:space="preserve">Проблема с регулярным выражением:</x:String>
|
||||||
<x:String x:Key="Text.Configure.IssueTracker.RuleName" xml:space="preserve">Имя правила:</x:String>
|
<x:String x:Key="Text.Configure.IssueTracker.RuleName" xml:space="preserve">Имя правила:</x:String>
|
||||||
|
@ -220,7 +223,7 @@
|
||||||
<x:String x:Key="Text.Diff.Copy" xml:space="preserve">Копировать</x:String>
|
<x:String x:Key="Text.Diff.Copy" xml:space="preserve">Копировать</x:String>
|
||||||
<x:String x:Key="Text.Diff.FileModeChanged" xml:space="preserve">Режим файла изменён</x:String>
|
<x:String x:Key="Text.Diff.FileModeChanged" xml:space="preserve">Режим файла изменён</x:String>
|
||||||
<x:String x:Key="Text.Diff.IgnoreWhitespace" xml:space="preserve">Игнорировать изменение пробелов</x:String>
|
<x:String x:Key="Text.Diff.IgnoreWhitespace" xml:space="preserve">Игнорировать изменение пробелов</x:String>
|
||||||
<x:String x:Key="Text.Diff.ShowHiddenSymbols" xml:space="preserve">Показать скрытые символы</x:String>
|
<x:String x:Key="Text.Diff.ShowHiddenSymbols" xml:space="preserve">Показывать скрытые символы</x:String>
|
||||||
<x:String x:Key="Text.Diff.LFS" xml:space="preserve">ИЗМЕНЕНИЕ ОБЪЕКТА ХБФ</x:String>
|
<x:String x:Key="Text.Diff.LFS" xml:space="preserve">ИЗМЕНЕНИЕ ОБЪЕКТА ХБФ</x:String>
|
||||||
<x:String x:Key="Text.Diff.Next" xml:space="preserve">Следующее различие</x:String>
|
<x:String x:Key="Text.Diff.Next" xml:space="preserve">Следующее различие</x:String>
|
||||||
<x:String x:Key="Text.Diff.NoChange" xml:space="preserve">НИКАКИХ ИЗМЕНЕНИЙ ИЛИ МЕНЯЕТСЯ ТОЛЬКО EOL</x:String>
|
<x:String x:Key="Text.Diff.NoChange" xml:space="preserve">НИКАКИХ ИЗМЕНЕНИЙ ИЛИ МЕНЯЕТСЯ ТОЛЬКО EOL</x:String>
|
||||||
|
@ -305,7 +308,7 @@
|
||||||
<x:String x:Key="Text.GitLFS.Fetch.Title" xml:space="preserve">Извлечь объекты ХБФ</x:String>
|
<x:String x:Key="Text.GitLFS.Fetch.Title" xml:space="preserve">Извлечь объекты ХБФ</x:String>
|
||||||
<x:String x:Key="Text.GitLFS.Fetch.Tips" xml:space="preserve">Запустить `git lfs fetch", чтобы загрузить объекты ХБФ Git. При этом рабочая копия не обновляется.</x:String>
|
<x:String x:Key="Text.GitLFS.Fetch.Tips" xml:space="preserve">Запустить `git lfs fetch", чтобы загрузить объекты ХБФ Git. При этом рабочая копия не обновляется.</x:String>
|
||||||
<x:String x:Key="Text.GitLFS.Install" xml:space="preserve">Установить перехват ХБФ Git</x:String>
|
<x:String x:Key="Text.GitLFS.Install" xml:space="preserve">Установить перехват ХБФ Git</x:String>
|
||||||
<x:String x:Key="Text.GitLFS.Locks" xml:space="preserve">Показать блокировки</x:String>
|
<x:String x:Key="Text.GitLFS.Locks" xml:space="preserve">Показывать блокировки</x:String>
|
||||||
<x:String x:Key="Text.GitLFS.Locks.Empty" xml:space="preserve">Нет заблокированных файлов</x:String>
|
<x:String x:Key="Text.GitLFS.Locks.Empty" xml:space="preserve">Нет заблокированных файлов</x:String>
|
||||||
<x:String x:Key="Text.GitLFS.Locks.Lock" xml:space="preserve">Блокировка</x:String>
|
<x:String x:Key="Text.GitLFS.Locks.Lock" xml:space="preserve">Блокировка</x:String>
|
||||||
<x:String x:Key="Text.GitLFS.Locks.OnlyMine" xml:space="preserve">Показывать только мои блокировки</x:String>
|
<x:String x:Key="Text.GitLFS.Locks.OnlyMine" xml:space="preserve">Показывать только мои блокировки</x:String>
|
||||||
|
@ -403,9 +406,12 @@
|
||||||
<x:String x:Key="Text.Period.YearsAgo" xml:space="preserve">{0} лет назад</x:String>
|
<x:String x:Key="Text.Period.YearsAgo" xml:space="preserve">{0} лет назад</x:String>
|
||||||
<x:String x:Key="Text.Preference" xml:space="preserve">Параметры</x:String>
|
<x:String x:Key="Text.Preference" xml:space="preserve">Параметры</x:String>
|
||||||
<x:String x:Key="Text.Preference.AI" xml:space="preserve">ОТКРЫТЬ ИИ</x:String>
|
<x:String x:Key="Text.Preference.AI" xml:space="preserve">ОТКРЫТЬ ИИ</x:String>
|
||||||
<x:String x:Key="Text.Preference.AI.Server" xml:space="preserve">Сервер</x:String>
|
|
||||||
<x:String x:Key="Text.Preference.AI.ApiKey" xml:space="preserve">Ключ API</x:String>
|
<x:String x:Key="Text.Preference.AI.ApiKey" xml:space="preserve">Ключ API</x:String>
|
||||||
|
<x:String x:Key="Text.Preference.AI.AnalyzeDiffPrompt" xml:space="preserve">Запрос на анализ различий</x:String>
|
||||||
|
<x:String x:Key="Text.Preference.AI.GenerateSubjectPrompt" xml:space="preserve">Сгенерировать запрос на тему</x:String>
|
||||||
<x:String x:Key="Text.Preference.AI.Model" xml:space="preserve">Модель</x:String>
|
<x:String x:Key="Text.Preference.AI.Model" xml:space="preserve">Модель</x:String>
|
||||||
|
<x:String x:Key="Text.Preference.AI.Name" xml:space="preserve">Имя:</x:String>
|
||||||
|
<x:String x:Key="Text.Preference.AI.Server" xml:space="preserve">Сервер</x:String>
|
||||||
<x:String x:Key="Text.Preference.Appearance" xml:space="preserve">ВИД</x:String>
|
<x:String x:Key="Text.Preference.Appearance" xml:space="preserve">ВИД</x:String>
|
||||||
<x:String x:Key="Text.Preference.Appearance.DefaultFont" xml:space="preserve">Шрифт по-умолчанию</x:String>
|
<x:String x:Key="Text.Preference.Appearance.DefaultFont" xml:space="preserve">Шрифт по-умолчанию</x:String>
|
||||||
<x:String x:Key="Text.Preference.Appearance.DefaultFontSize" xml:space="preserve">Размер шрифта по-умолчанию</x:String>
|
<x:String x:Key="Text.Preference.Appearance.DefaultFontSize" xml:space="preserve">Размер шрифта по-умолчанию</x:String>
|
||||||
|
@ -423,7 +429,7 @@
|
||||||
<x:String x:Key="Text.Preference.General.Check4UpdatesOnStartup" xml:space="preserve">Проверить обновления при старте</x:String>
|
<x:String x:Key="Text.Preference.General.Check4UpdatesOnStartup" xml:space="preserve">Проверить обновления при старте</x:String>
|
||||||
<x:String x:Key="Text.Preference.General.Locale" xml:space="preserve">Язык</x:String>
|
<x:String x:Key="Text.Preference.General.Locale" xml:space="preserve">Язык</x:String>
|
||||||
<x:String x:Key="Text.Preference.General.MaxHistoryCommits" xml:space="preserve">История фиксаций</x:String>
|
<x:String x:Key="Text.Preference.General.MaxHistoryCommits" xml:space="preserve">История фиксаций</x:String>
|
||||||
<x:String x:Key="Text.Preference.General.ShowAuthorTime" xml:space="preserve">Показать время автора вместо времени фиксации на графике</x:String>
|
<x:String x:Key="Text.Preference.General.ShowAuthorTime" xml:space="preserve">Показывать время автора вместо времени фиксации на графике</x:String>
|
||||||
<x:String x:Key="Text.Preference.General.SubjectGuideLength" xml:space="preserve">Длина темы фиксации</x:String>
|
<x:String x:Key="Text.Preference.General.SubjectGuideLength" xml:space="preserve">Длина темы фиксации</x:String>
|
||||||
<x:String x:Key="Text.Preference.Git" xml:space="preserve">GIT</x:String>
|
<x:String x:Key="Text.Preference.Git" xml:space="preserve">GIT</x:String>
|
||||||
<x:String x:Key="Text.Preference.Git.CRLF" xml:space="preserve">Включить автозавершение CRLF</x:String>
|
<x:String x:Key="Text.Preference.Git.CRLF" xml:space="preserve">Включить автозавершение CRLF</x:String>
|
||||||
|
@ -508,6 +514,7 @@
|
||||||
<x:String x:Key="Text.Repository.ClearAllCommitsFilter" xml:space="preserve">Очистить всё</x:String>
|
<x:String x:Key="Text.Repository.ClearAllCommitsFilter" xml:space="preserve">Очистить всё</x:String>
|
||||||
<x:String x:Key="Text.Repository.Configure" xml:space="preserve">Настройка этого хранилища</x:String>
|
<x:String x:Key="Text.Repository.Configure" xml:space="preserve">Настройка этого хранилища</x:String>
|
||||||
<x:String x:Key="Text.Repository.Continue" xml:space="preserve">ПРОДОЛЖИТЬ</x:String>
|
<x:String x:Key="Text.Repository.Continue" xml:space="preserve">ПРОДОЛЖИТЬ</x:String>
|
||||||
|
<x:String x:Key="Text.Repository.EnableReflog" xml:space="preserve">Разрешить опцию '--reflog'</x:String>
|
||||||
<x:String x:Key="Text.Repository.Explore" xml:space="preserve">Открыть в файловом менеджере</x:String>
|
<x:String x:Key="Text.Repository.Explore" xml:space="preserve">Открыть в файловом менеджере</x:String>
|
||||||
<x:String x:Key="Text.Repository.Filter" xml:space="preserve">Поиск веток, меток и подмодулей</x:String>
|
<x:String x:Key="Text.Repository.Filter" xml:space="preserve">Поиск веток, меток и подмодулей</x:String>
|
||||||
<x:String x:Key="Text.Repository.FilterCommitPrefix" xml:space="preserve">ОТФИЛЬТРОВАНО ОТ:</x:String>
|
<x:String x:Key="Text.Repository.FilterCommitPrefix" xml:space="preserve">ОТФИЛЬТРОВАНО ОТ:</x:String>
|
||||||
|
@ -527,7 +534,7 @@
|
||||||
<x:String x:Key="Text.Repository.Search.BySHA" xml:space="preserve">SHA</x:String>
|
<x:String x:Key="Text.Repository.Search.BySHA" xml:space="preserve">SHA</x:String>
|
||||||
<x:String x:Key="Text.Repository.Search.ByUser" xml:space="preserve">Автор и исполнитель</x:String>
|
<x:String x:Key="Text.Repository.Search.ByUser" xml:space="preserve">Автор и исполнитель</x:String>
|
||||||
<x:String x:Key="Text.Repository.Search.InCurrentBranch" xml:space="preserve">Текущая ветка</x:String>
|
<x:String x:Key="Text.Repository.Search.InCurrentBranch" xml:space="preserve">Текущая ветка</x:String>
|
||||||
<x:String x:Key="Text.Repository.ShowTagsAsTree" xml:space="preserve">Показать метки как дерево</x:String>
|
<x:String x:Key="Text.Repository.ShowTagsAsTree" xml:space="preserve">Показывать метки как дерево</x:String>
|
||||||
<x:String x:Key="Text.Repository.Statistics" xml:space="preserve">Статистики </x:String>
|
<x:String x:Key="Text.Repository.Statistics" xml:space="preserve">Статистики </x:String>
|
||||||
<x:String x:Key="Text.Repository.Submodules" xml:space="preserve">ПОДМОДУЛИ</x:String>
|
<x:String x:Key="Text.Repository.Submodules" xml:space="preserve">ПОДМОДУЛИ</x:String>
|
||||||
<x:String x:Key="Text.Repository.Submodules.Add" xml:space="preserve">ДОБАВИТЬ ПОДМОДУЛЬ</x:String>
|
<x:String x:Key="Text.Repository.Submodules.Add" xml:space="preserve">ДОБАВИТЬ ПОДМОДУЛЬ</x:String>
|
||||||
|
@ -638,6 +645,7 @@
|
||||||
<x:String x:Key="Text.WorkingCopy.CommitMessageHelper" xml:space="preserve">Шаблон/Истории</x:String>
|
<x:String x:Key="Text.WorkingCopy.CommitMessageHelper" xml:space="preserve">Шаблон/Истории</x:String>
|
||||||
<x:String x:Key="Text.WorkingCopy.CommitTip" xml:space="preserve">Запустить событие щелчка</x:String>
|
<x:String x:Key="Text.WorkingCopy.CommitTip" xml:space="preserve">Запустить событие щелчка</x:String>
|
||||||
<x:String x:Key="Text.WorkingCopy.CommitWithAutoStage" xml:space="preserve">Подготовить все изменения и зафиксировать</x:String>
|
<x:String x:Key="Text.WorkingCopy.CommitWithAutoStage" xml:space="preserve">Подготовить все изменения и зафиксировать</x:String>
|
||||||
|
<x:String x:Key="Text.WorkingCopy.ConfirmCommitWithoutFiles" xml:space="preserve">Обнаружена пустая фиксация! Вы хотите продолжить (--allow-empty)?</x:String>
|
||||||
<x:String x:Key="Text.WorkingCopy.Conflicts" xml:space="preserve">ОБНАРУЖЕНЫ КОНФЛИКТЫ</x:String>
|
<x:String x:Key="Text.WorkingCopy.Conflicts" xml:space="preserve">ОБНАРУЖЕНЫ КОНФЛИКТЫ</x:String>
|
||||||
<x:String x:Key="Text.WorkingCopy.Conflicts.Resolved" xml:space="preserve">КОНФЛИКТЫ ФАЙЛОВ РАЗРЕШЕНЫ</x:String>
|
<x:String x:Key="Text.WorkingCopy.Conflicts.Resolved" xml:space="preserve">КОНФЛИКТЫ ФАЙЛОВ РАЗРЕШЕНЫ</x:String>
|
||||||
<x:String x:Key="Text.WorkingCopy.IncludeUntracked" xml:space="preserve">ВКЛЮЧИТЬ НЕОТСЛЕЖИВАЕМЫЕ ФАЙЛЫ</x:String>
|
<x:String x:Key="Text.WorkingCopy.IncludeUntracked" xml:space="preserve">ВКЛЮЧИТЬ НЕОТСЛЕЖИВАЕМЫЕ ФАЙЛЫ</x:String>
|
||||||
|
|
|
@ -72,6 +72,7 @@
|
||||||
<x:String x:Key="Text.Cancel" xml:space="preserve">取 消</x:String>
|
<x:String x:Key="Text.Cancel" xml:space="preserve">取 消</x:String>
|
||||||
<x:String x:Key="Text.ChangeCM.CheckoutThisRevision" xml:space="preserve">重置文件到该版本</x:String>
|
<x:String x:Key="Text.ChangeCM.CheckoutThisRevision" xml:space="preserve">重置文件到该版本</x:String>
|
||||||
<x:String x:Key="Text.ChangeCM.CheckoutFirstParentRevision" xml:space="preserve">重置文件到上一版本</x:String>
|
<x:String x:Key="Text.ChangeCM.CheckoutFirstParentRevision" xml:space="preserve">重置文件到上一版本</x:String>
|
||||||
|
<x:String x:Key="Text.ChangeCM.GenerateCommitMessage" xml:space="preserve">生成提交信息</x:String>
|
||||||
<x:String x:Key="Text.ChangeDisplayMode" xml:space="preserve">切换变更显示模式</x:String>
|
<x:String x:Key="Text.ChangeDisplayMode" xml:space="preserve">切换变更显示模式</x:String>
|
||||||
<x:String x:Key="Text.ChangeDisplayMode.Grid" xml:space="preserve">文件名+路径列表模式</x:String>
|
<x:String x:Key="Text.ChangeDisplayMode.Grid" xml:space="preserve">文件名+路径列表模式</x:String>
|
||||||
<x:String x:Key="Text.ChangeDisplayMode.List" xml:space="preserve">全路径列表模式</x:String>
|
<x:String x:Key="Text.ChangeDisplayMode.List" xml:space="preserve">全路径列表模式</x:String>
|
||||||
|
@ -157,6 +158,9 @@
|
||||||
<x:String x:Key="Text.Configure.IssueTracker.RuleName" xml:space="preserve">规则名 :</x:String>
|
<x:String x:Key="Text.Configure.IssueTracker.RuleName" xml:space="preserve">规则名 :</x:String>
|
||||||
<x:String x:Key="Text.Configure.IssueTracker.URLTemplate" xml:space="preserve">为ISSUE生成的URL链接 :</x:String>
|
<x:String x:Key="Text.Configure.IssueTracker.URLTemplate" xml:space="preserve">为ISSUE生成的URL链接 :</x:String>
|
||||||
<x:String x:Key="Text.Configure.IssueTracker.URLTemplate.Tip" xml:space="preserve">可在URL中使用$1,$2等变量填入正则表达式匹配的内容</x:String>
|
<x:String x:Key="Text.Configure.IssueTracker.URLTemplate.Tip" xml:space="preserve">可在URL中使用$1,$2等变量填入正则表达式匹配的内容</x:String>
|
||||||
|
<x:String x:Key="Text.Configure.OpenAI" xml:space="preserve">OPEN AI</x:String>
|
||||||
|
<x:String x:Key="Text.Configure.OpenAI.Prefered" xml:space="preserve">启用特定服务 :</x:String>
|
||||||
|
<x:String x:Key="Text.Configure.OpenAI.Prefered.Tip" xml:space="preserve">当【启用特定服务】被设置时,SourceGit将在本仓库中仅使用该服务。否则将弹出可用的OpenAI服务列表供用户选择。</x:String>
|
||||||
<x:String x:Key="Text.Configure.Proxy" xml:space="preserve">HTTP代理</x:String>
|
<x:String x:Key="Text.Configure.Proxy" xml:space="preserve">HTTP代理</x:String>
|
||||||
<x:String x:Key="Text.Configure.Proxy.Placeholder" xml:space="preserve">HTTP网络代理</x:String>
|
<x:String x:Key="Text.Configure.Proxy.Placeholder" xml:space="preserve">HTTP网络代理</x:String>
|
||||||
<x:String x:Key="Text.Configure.User" xml:space="preserve">用户名</x:String>
|
<x:String x:Key="Text.Configure.User" xml:space="preserve">用户名</x:String>
|
||||||
|
@ -404,7 +408,13 @@
|
||||||
<x:String x:Key="Text.Period.LastYear" xml:space="preserve">一年前</x:String>
|
<x:String x:Key="Text.Period.LastYear" xml:space="preserve">一年前</x:String>
|
||||||
<x:String x:Key="Text.Period.YearsAgo" xml:space="preserve">{0}年前</x:String>
|
<x:String x:Key="Text.Period.YearsAgo" xml:space="preserve">{0}年前</x:String>
|
||||||
<x:String x:Key="Text.Preference" xml:space="preserve">偏好设置</x:String>
|
<x:String x:Key="Text.Preference" xml:space="preserve">偏好设置</x:String>
|
||||||
<x:String x:Key="Text.Preference.Advanced" xml:space="preserve">高级设置</x:String>
|
<x:String x:Key="Text.Preference.AI" xml:space="preserve">OPEN AI</x:String>
|
||||||
|
<x:String x:Key="Text.Preference.AI.AnalyzeDiffPrompt" xml:space="preserve">Analyze Diff Prompt</x:String>
|
||||||
|
<x:String x:Key="Text.Preference.AI.ApiKey" xml:space="preserve">API密钥</x:String>
|
||||||
|
<x:String x:Key="Text.Preference.AI.GenerateSubjectPrompt" xml:space="preserve">Generate Subject Prompt</x:String>
|
||||||
|
<x:String x:Key="Text.Preference.AI.Model" xml:space="preserve">模型</x:String>
|
||||||
|
<x:String x:Key="Text.Preference.AI.Name" xml:space="preserve">配置名称</x:String>
|
||||||
|
<x:String x:Key="Text.Preference.AI.Server" xml:space="preserve">服务地址</x:String>
|
||||||
<x:String x:Key="Text.Preference.Appearance" xml:space="preserve">外观配置</x:String>
|
<x:String x:Key="Text.Preference.Appearance" xml:space="preserve">外观配置</x:String>
|
||||||
<x:String x:Key="Text.Preference.Appearance.DefaultFont" xml:space="preserve">缺省字体</x:String>
|
<x:String x:Key="Text.Preference.Appearance.DefaultFont" xml:space="preserve">缺省字体</x:String>
|
||||||
<x:String x:Key="Text.Preference.Appearance.DefaultFontSize" xml:space="preserve">默认字体大小</x:String>
|
<x:String x:Key="Text.Preference.Appearance.DefaultFontSize" xml:space="preserve">默认字体大小</x:String>
|
||||||
|
@ -569,6 +579,7 @@
|
||||||
<x:String x:Key="Text.Start" xml:space="preserve">开 始</x:String>
|
<x:String x:Key="Text.Start" xml:space="preserve">开 始</x:String>
|
||||||
<x:String x:Key="Text.Stash" xml:space="preserve">贮藏(stash)</x:String>
|
<x:String x:Key="Text.Stash" xml:space="preserve">贮藏(stash)</x:String>
|
||||||
<x:String x:Key="Text.Stash.IncludeUntracked" xml:space="preserve">包含未跟踪的文件</x:String>
|
<x:String x:Key="Text.Stash.IncludeUntracked" xml:space="preserve">包含未跟踪的文件</x:String>
|
||||||
|
<x:String x:Key="Text.Stash.KeepIndex" xml:space="preserve">保留暂存区文件</x:String>
|
||||||
<x:String x:Key="Text.Stash.Message" xml:space="preserve">信息 :</x:String>
|
<x:String x:Key="Text.Stash.Message" xml:space="preserve">信息 :</x:String>
|
||||||
<x:String x:Key="Text.Stash.Message.Placeholder" xml:space="preserve">选填,用于命名此贮藏</x:String>
|
<x:String x:Key="Text.Stash.Message.Placeholder" xml:space="preserve">选填,用于命名此贮藏</x:String>
|
||||||
<x:String x:Key="Text.Stash.OnlyStagedChanges" xml:space="preserve">仅贮藏暂存区的变更</x:String>
|
<x:String x:Key="Text.Stash.OnlyStagedChanges" xml:space="preserve">仅贮藏暂存区的变更</x:String>
|
||||||
|
|
|
@ -72,6 +72,7 @@
|
||||||
<x:String x:Key="Text.Cancel" xml:space="preserve">取 消</x:String>
|
<x:String x:Key="Text.Cancel" xml:space="preserve">取 消</x:String>
|
||||||
<x:String x:Key="Text.ChangeCM.CheckoutThisRevision" xml:space="preserve">重設檔案為此版本</x:String>
|
<x:String x:Key="Text.ChangeCM.CheckoutThisRevision" xml:space="preserve">重設檔案為此版本</x:String>
|
||||||
<x:String x:Key="Text.ChangeCM.CheckoutFirstParentRevision" xml:space="preserve">重設檔案到上一版本</x:String>
|
<x:String x:Key="Text.ChangeCM.CheckoutFirstParentRevision" xml:space="preserve">重設檔案到上一版本</x:String>
|
||||||
|
<x:String x:Key="Text.ChangeCM.GenerateCommitMessage" xml:space="preserve">產生提交訊息</x:String>
|
||||||
<x:String x:Key="Text.ChangeDisplayMode" xml:space="preserve">切換變更顯示模式</x:String>
|
<x:String x:Key="Text.ChangeDisplayMode" xml:space="preserve">切換變更顯示模式</x:String>
|
||||||
<x:String x:Key="Text.ChangeDisplayMode.Grid" xml:space="preserve">檔案名稱 + 路徑列表模式</x:String>
|
<x:String x:Key="Text.ChangeDisplayMode.Grid" xml:space="preserve">檔案名稱 + 路徑列表模式</x:String>
|
||||||
<x:String x:Key="Text.ChangeDisplayMode.List" xml:space="preserve">全路徑列表模式</x:String>
|
<x:String x:Key="Text.ChangeDisplayMode.List" xml:space="preserve">全路徑列表模式</x:String>
|
||||||
|
@ -157,6 +158,9 @@
|
||||||
<x:String x:Key="Text.Configure.IssueTracker.RuleName" xml:space="preserve">規則名稱:</x:String>
|
<x:String x:Key="Text.Configure.IssueTracker.RuleName" xml:space="preserve">規則名稱:</x:String>
|
||||||
<x:String x:Key="Text.Configure.IssueTracker.URLTemplate" xml:space="preserve">為 Issue 產生的網址連結:</x:String>
|
<x:String x:Key="Text.Configure.IssueTracker.URLTemplate" xml:space="preserve">為 Issue 產生的網址連結:</x:String>
|
||||||
<x:String x:Key="Text.Configure.IssueTracker.URLTemplate.Tip" xml:space="preserve">可在網址中使用 $1、$2 等變數填入正則表示式相符的內容</x:String>
|
<x:String x:Key="Text.Configure.IssueTracker.URLTemplate.Tip" xml:space="preserve">可在網址中使用 $1、$2 等變數填入正則表示式相符的內容</x:String>
|
||||||
|
<x:String x:Key="Text.Configure.OpenAI" xml:space="preserve">OPEN AI</x:String>
|
||||||
|
<x:String x:Key="Text.Configure.OpenAI.Prefered" xml:space="preserve">启用特定服务 :</x:String>
|
||||||
|
<x:String x:Key="Text.Configure.OpenAI.Prefered.Tip" xml:space="preserve">当【启用特定服务】被设置时,SourceGit将在本仓库中仅使用该服务。否则将弹出可用的OpenAI服务列表供用户选择。</x:String>
|
||||||
<x:String x:Key="Text.Configure.Proxy" xml:space="preserve">HTTP 代理</x:String>
|
<x:String x:Key="Text.Configure.Proxy" xml:space="preserve">HTTP 代理</x:String>
|
||||||
<x:String x:Key="Text.Configure.Proxy.Placeholder" xml:space="preserve">HTTP 網路代理</x:String>
|
<x:String x:Key="Text.Configure.Proxy.Placeholder" xml:space="preserve">HTTP 網路代理</x:String>
|
||||||
<x:String x:Key="Text.Configure.User" xml:space="preserve">使用者名稱</x:String>
|
<x:String x:Key="Text.Configure.User" xml:space="preserve">使用者名稱</x:String>
|
||||||
|
@ -404,11 +408,11 @@
|
||||||
<x:String x:Key="Text.Period.LastYear" xml:space="preserve">一年前</x:String>
|
<x:String x:Key="Text.Period.LastYear" xml:space="preserve">一年前</x:String>
|
||||||
<x:String x:Key="Text.Period.YearsAgo" xml:space="preserve">{0} 年前</x:String>
|
<x:String x:Key="Text.Period.YearsAgo" xml:space="preserve">{0} 年前</x:String>
|
||||||
<x:String x:Key="Text.Preference" xml:space="preserve">偏好設定</x:String>
|
<x:String x:Key="Text.Preference" xml:space="preserve">偏好設定</x:String>
|
||||||
<x:String x:Key="Text.Preference.Advanced" xml:space="preserve">進階設定</x:String>
|
|
||||||
<x:String x:Key="Text.Preference.AI" xml:space="preserve">OpenAI</x:String>
|
<x:String x:Key="Text.Preference.AI" xml:space="preserve">OpenAI</x:String>
|
||||||
<x:String x:Key="Text.Preference.AI.Server" xml:space="preserve">伺服器</x:String>
|
<x:String x:Key="Text.Preference.AI.Server" xml:space="preserve">伺服器</x:String>
|
||||||
<x:String x:Key="Text.Preference.AI.ApiKey" xml:space="preserve">API 金鑰</x:String>
|
<x:String x:Key="Text.Preference.AI.ApiKey" xml:space="preserve">API 金鑰</x:String>
|
||||||
<x:String x:Key="Text.Preference.AI.Model" xml:space="preserve">模型</x:String>
|
<x:String x:Key="Text.Preference.AI.Model" xml:space="preserve">模型</x:String>
|
||||||
|
<x:String x:Key="Text.Preference.AI.Name" xml:space="preserve">名稱</x:String>
|
||||||
<x:String x:Key="Text.Preference.AI.AnalyzeDiffPrompt" xml:space="preserve">分析變更差異提示詞</x:String>
|
<x:String x:Key="Text.Preference.AI.AnalyzeDiffPrompt" xml:space="preserve">分析變更差異提示詞</x:String>
|
||||||
<x:String x:Key="Text.Preference.AI.GenerateSubjectPrompt" xml:space="preserve">產生提交訊息提示詞</x:String>
|
<x:String x:Key="Text.Preference.AI.GenerateSubjectPrompt" xml:space="preserve">產生提交訊息提示詞</x:String>
|
||||||
<x:String x:Key="Text.Preference.Appearance" xml:space="preserve">外觀設定</x:String>
|
<x:String x:Key="Text.Preference.Appearance" xml:space="preserve">外觀設定</x:String>
|
||||||
|
@ -575,6 +579,7 @@
|
||||||
<x:String x:Key="Text.Start" xml:space="preserve">開 始</x:String>
|
<x:String x:Key="Text.Start" xml:space="preserve">開 始</x:String>
|
||||||
<x:String x:Key="Text.Stash" xml:space="preserve">擱置變更 (stash)</x:String>
|
<x:String x:Key="Text.Stash" xml:space="preserve">擱置變更 (stash)</x:String>
|
||||||
<x:String x:Key="Text.Stash.IncludeUntracked" xml:space="preserve">包含未追蹤的檔案</x:String>
|
<x:String x:Key="Text.Stash.IncludeUntracked" xml:space="preserve">包含未追蹤的檔案</x:String>
|
||||||
|
<x:String x:Key="Text.Stash.KeepIndex" xml:space="preserve">保留已暫存的變更</x:String>
|
||||||
<x:String x:Key="Text.Stash.Message" xml:space="preserve">擱置變更訊息:</x:String>
|
<x:String x:Key="Text.Stash.Message" xml:space="preserve">擱置變更訊息:</x:String>
|
||||||
<x:String x:Key="Text.Stash.Message.Placeholder" xml:space="preserve">選填,用於命名此擱置變更</x:String>
|
<x:String x:Key="Text.Stash.Message.Placeholder" xml:space="preserve">選填,用於命名此擱置變更</x:String>
|
||||||
<x:String x:Key="Text.Stash.OnlyStagedChanges" xml:space="preserve">僅擱置已暫存的變更</x:String>
|
<x:String x:Key="Text.Stash.OnlyStagedChanges" xml:space="preserve">僅擱置已暫存的變更</x:String>
|
||||||
|
|
|
@ -3,7 +3,7 @@ using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
using Avalonia.Collections;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
|
||||||
namespace SourceGit.ViewModels
|
namespace SourceGit.ViewModels
|
||||||
|
@ -25,7 +25,6 @@ namespace SourceGit.ViewModels
|
||||||
_instance.PrepareGit();
|
_instance.PrepareGit();
|
||||||
_instance.PrepareShellOrTerminal();
|
_instance.PrepareShellOrTerminal();
|
||||||
_instance.PrepareWorkspaces();
|
_instance.PrepareWorkspaces();
|
||||||
_instance.PrepareOpenAIPrompt();
|
|
||||||
|
|
||||||
return _instance;
|
return _instance;
|
||||||
}
|
}
|
||||||
|
@ -277,71 +276,6 @@ namespace SourceGit.ViewModels
|
||||||
set => SetProperty(ref _externalMergeToolPath, value);
|
set => SetProperty(ref _externalMergeToolPath, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string OpenAIServer
|
|
||||||
{
|
|
||||||
get => Models.OpenAI.Server;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value != Models.OpenAI.Server)
|
|
||||||
{
|
|
||||||
Models.OpenAI.Server = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string OpenAIApiKey
|
|
||||||
{
|
|
||||||
get => Models.OpenAI.ApiKey;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value != Models.OpenAI.ApiKey)
|
|
||||||
{
|
|
||||||
Models.OpenAI.ApiKey = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string OpenAIModel
|
|
||||||
{
|
|
||||||
get => Models.OpenAI.Model;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value != Models.OpenAI.Model)
|
|
||||||
{
|
|
||||||
Models.OpenAI.Model = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string OpenAIAnalyzeDiffPrompt
|
|
||||||
{
|
|
||||||
get => Models.OpenAI.AnalyzeDiffPrompt;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value != Models.OpenAI.AnalyzeDiffPrompt)
|
|
||||||
{
|
|
||||||
Models.OpenAI.AnalyzeDiffPrompt = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string OpenAIGenerateSubjectPrompt
|
|
||||||
{
|
|
||||||
get => Models.OpenAI.GenerateSubjectPrompt;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value != Models.OpenAI.GenerateSubjectPrompt)
|
|
||||||
{
|
|
||||||
Models.OpenAI.GenerateSubjectPrompt = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public uint StatisticsSampleColor
|
public uint StatisticsSampleColor
|
||||||
{
|
{
|
||||||
get => _statisticsSampleColor;
|
get => _statisticsSampleColor;
|
||||||
|
@ -360,6 +294,12 @@ namespace SourceGit.ViewModels
|
||||||
set;
|
set;
|
||||||
} = [];
|
} = [];
|
||||||
|
|
||||||
|
public AvaloniaList<Models.OpenAIService> OpenAIServices
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
} = [];
|
||||||
|
|
||||||
public double LastCheckUpdateTime
|
public double LastCheckUpdateTime
|
||||||
{
|
{
|
||||||
get => _lastCheckUpdateTime;
|
get => _lastCheckUpdateTime;
|
||||||
|
@ -554,45 +494,6 @@ namespace SourceGit.ViewModels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PrepareOpenAIPrompt()
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(Models.OpenAI.AnalyzeDiffPrompt))
|
|
||||||
{
|
|
||||||
Models.OpenAI.AnalyzeDiffPrompt = """
|
|
||||||
You are an expert developer specialist in creating commits.
|
|
||||||
Provide a super concise one sentence overall changes summary of the user `git diff` output following strictly the next rules:
|
|
||||||
- Do not use any code snippets, imports, file routes or bullets points.
|
|
||||||
- Do not mention the route of file that has been change.
|
|
||||||
- Write clear, concise, and descriptive messages that explain the MAIN GOAL made of the changes.
|
|
||||||
- Use the present tense and active voice in the message, for example, "Fix bug" instead of "Fixed bug.".
|
|
||||||
- Use the imperative mood, which gives the message a sense of command, e.g. "Add feature" instead of "Added feature".
|
|
||||||
- Avoid using general terms like "update" or "change", be specific about what was updated or changed.
|
|
||||||
- Avoid using terms like "The main goal of", just output directly the summary in plain text
|
|
||||||
""";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(Models.OpenAI.GenerateSubjectPrompt))
|
|
||||||
{
|
|
||||||
Models.OpenAI.GenerateSubjectPrompt = """
|
|
||||||
You are an expert developer specialist in creating commits messages.
|
|
||||||
Your only goal is to retrieve a single commit message.
|
|
||||||
Based on the provided user changes, combine them in ONE SINGLE commit message retrieving the global idea, following strictly the next rules:
|
|
||||||
- Assign the commit {type} according to the next conditions:
|
|
||||||
feat: Only when adding a new feature.
|
|
||||||
fix: When fixing a bug.
|
|
||||||
docs: When updating documentation.
|
|
||||||
style: When changing elements styles or design and/or making changes to the code style (formatting, missing semicolons, etc.) without changing the code logic.
|
|
||||||
test: When adding or updating tests.
|
|
||||||
chore: When making changes to the build process or auxiliary tools and libraries.
|
|
||||||
revert: When undoing a previous commit.
|
|
||||||
refactor: When restructuring code without changing its external behavior, or is any of the other refactor types.
|
|
||||||
- Do not add any issues numeration, explain your output nor introduce your answer.
|
|
||||||
- Output directly only one commit message in plain text with the next format: {type}: {commit_message}.
|
|
||||||
- Be as concise as possible, keep the message under 50 characters.
|
|
||||||
""";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private RepositoryNode FindNodeRecursive(string id, List<RepositoryNode> collection)
|
private RepositoryNode FindNodeRecursive(string id, List<RepositoryNode> collection)
|
||||||
{
|
{
|
||||||
foreach (var node in collection)
|
foreach (var node in collection)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Avalonia.Collections;
|
using Avalonia.Collections;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
|
||||||
|
@ -108,6 +109,18 @@ namespace SourceGit.ViewModels
|
||||||
set => SetProperty(ref _selectedIssueTrackerRule, value);
|
set => SetProperty(ref _selectedIssueTrackerRule, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<string> AvailableOpenAIServices
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string PreferedOpenAIService
|
||||||
|
{
|
||||||
|
get => _repo.Settings.PreferedOpenAIService;
|
||||||
|
set => _repo.Settings.PreferedOpenAIService = value;
|
||||||
|
}
|
||||||
|
|
||||||
public RepositoryConfigure(Repository repo)
|
public RepositoryConfigure(Repository repo)
|
||||||
{
|
{
|
||||||
_repo = repo;
|
_repo = repo;
|
||||||
|
@ -116,6 +129,13 @@ namespace SourceGit.ViewModels
|
||||||
foreach (var remote in _repo.Remotes)
|
foreach (var remote in _repo.Remotes)
|
||||||
Remotes.Add(remote.Name);
|
Remotes.Add(remote.Name);
|
||||||
|
|
||||||
|
AvailableOpenAIServices = new List<string>() { "---" };
|
||||||
|
foreach (var service in Preference.Instance.OpenAIServices)
|
||||||
|
AvailableOpenAIServices.Add(service.Name);
|
||||||
|
|
||||||
|
if (AvailableOpenAIServices.IndexOf(PreferedOpenAIService) == -1)
|
||||||
|
PreferedOpenAIService = "---";
|
||||||
|
|
||||||
_cached = new Commands.Config(repo.FullPath).ListAll();
|
_cached = new Commands.Config(repo.FullPath).ListAll();
|
||||||
if (_cached.TryGetValue("user.name", out var name))
|
if (_cached.TryGetValue("user.name", out var name))
|
||||||
UserName = name;
|
UserName = name;
|
||||||
|
|
|
@ -18,24 +18,27 @@ namespace SourceGit.ViewModels
|
||||||
|
|
||||||
public bool IncludeUntracked
|
public bool IncludeUntracked
|
||||||
{
|
{
|
||||||
get;
|
get => _repo.Settings.IncludeUntrackedWhenStash;
|
||||||
set;
|
set => _repo.Settings.IncludeUntrackedWhenStash = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool OnlyStaged
|
public bool OnlyStaged
|
||||||
{
|
{
|
||||||
get;
|
get => _repo.Settings.OnlyStagedWhenStash;
|
||||||
set;
|
set => _repo.Settings.OnlyStagedWhenStash = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool KeepIndex
|
||||||
|
{
|
||||||
|
get => _repo.Settings.KeepIndexWhenStash;
|
||||||
|
set => _repo.Settings.KeepIndexWhenStash = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public StashChanges(Repository repo, List<Models.Change> changes, bool hasSelectedFiles)
|
public StashChanges(Repository repo, List<Models.Change> changes, bool hasSelectedFiles)
|
||||||
{
|
{
|
||||||
_repo = repo;
|
_repo = repo;
|
||||||
_changes = changes;
|
_changes = changes;
|
||||||
|
|
||||||
HasSelectedFiles = hasSelectedFiles;
|
HasSelectedFiles = hasSelectedFiles;
|
||||||
IncludeUntracked = true;
|
|
||||||
OnlyStaged = false;
|
|
||||||
|
|
||||||
View = new Views.StashChanges() { DataContext = this };
|
View = new Views.StashChanges() { DataContext = this };
|
||||||
}
|
}
|
||||||
|
@ -63,7 +66,7 @@ namespace SourceGit.ViewModels
|
||||||
|
|
||||||
return Task.Run(() =>
|
return Task.Run(() =>
|
||||||
{
|
{
|
||||||
var succ = new Commands.Stash(_repo.FullPath).Push(jobs, Message, !HasSelectedFiles && OnlyStaged);
|
var succ = new Commands.Stash(_repo.FullPath).Push(jobs, Message, !HasSelectedFiles && OnlyStaged, KeepIndex);
|
||||||
CallUIThread(() =>
|
CallUIThread(() =>
|
||||||
{
|
{
|
||||||
_repo.MarkWorkingCopyDirtyManually();
|
_repo.MarkWorkingCopyDirtyManually();
|
||||||
|
|
|
@ -403,25 +403,6 @@ namespace SourceGit.ViewModels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GenerateCommitMessageByAI()
|
|
||||||
{
|
|
||||||
if (!Models.OpenAI.IsValid)
|
|
||||||
{
|
|
||||||
App.RaiseException(_repo.FullPath, "Bad configuration for OpenAI");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_staged is { Count: > 0 })
|
|
||||||
{
|
|
||||||
var dialog = new Views.AIAssistant(_repo.FullPath, _staged, generated => CommitMessage = generated);
|
|
||||||
App.OpenDialog(dialog);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
App.RaiseException(_repo.FullPath, "No files added to commit!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Commit()
|
public void Commit()
|
||||||
{
|
{
|
||||||
DoCommit(false, false, false);
|
DoCommit(false, false, false);
|
||||||
|
@ -900,6 +881,44 @@ namespace SourceGit.ViewModels
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var menu = new ContextMenu();
|
var menu = new ContextMenu();
|
||||||
|
|
||||||
|
var ai = null as MenuItem;
|
||||||
|
var services = GetPreferedOpenAIServices();
|
||||||
|
if (services.Count > 0)
|
||||||
|
{
|
||||||
|
ai = new MenuItem();
|
||||||
|
ai.Icon = App.CreateMenuIcon("Icons.AIAssist");
|
||||||
|
ai.Header = App.Text("ChangeCM.GenerateCommitMessage");
|
||||||
|
|
||||||
|
if (services.Count == 1)
|
||||||
|
{
|
||||||
|
ai.Click += (_, e) =>
|
||||||
|
{
|
||||||
|
var dialog = new Views.AIAssistant(services[0], _repo.FullPath, _selectedStaged, generated => CommitMessage = generated);
|
||||||
|
App.OpenDialog(dialog);
|
||||||
|
e.Handled = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var service in services)
|
||||||
|
{
|
||||||
|
var dup = service;
|
||||||
|
|
||||||
|
var item = new MenuItem();
|
||||||
|
item.Header = service.Name;
|
||||||
|
item.Click += (_, e) =>
|
||||||
|
{
|
||||||
|
var dialog = new Views.AIAssistant(dup, _repo.FullPath, _selectedStaged, generated => CommitMessage = generated);
|
||||||
|
App.OpenDialog(dialog);
|
||||||
|
e.Handled = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
ai.Items.Add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (_selectedStaged.Count == 1)
|
if (_selectedStaged.Count == 1)
|
||||||
{
|
{
|
||||||
var change = _selectedStaged[0];
|
var change = _selectedStaged[0];
|
||||||
|
@ -980,24 +999,6 @@ namespace SourceGit.ViewModels
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
var copyPath = new MenuItem();
|
|
||||||
copyPath.Header = App.Text("CopyPath");
|
|
||||||
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
|
||||||
copyPath.Click += (_, e) =>
|
|
||||||
{
|
|
||||||
App.CopyText(change.Path);
|
|
||||||
e.Handled = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
var copyFileName = new MenuItem();
|
|
||||||
copyFileName.Header = App.Text("CopyFileName");
|
|
||||||
copyFileName.Icon = App.CreateMenuIcon("Icons.Copy");
|
|
||||||
copyFileName.Click += (_, e) =>
|
|
||||||
{
|
|
||||||
App.CopyText(Path.GetFileName(change.Path));
|
|
||||||
e.Handled = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
menu.Items.Add(explore);
|
menu.Items.Add(explore);
|
||||||
menu.Items.Add(openWith);
|
menu.Items.Add(openWith);
|
||||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||||
|
@ -1089,6 +1090,30 @@ namespace SourceGit.ViewModels
|
||||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ai != null)
|
||||||
|
{
|
||||||
|
menu.Items.Add(ai);
|
||||||
|
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||||
|
}
|
||||||
|
|
||||||
|
var copyPath = new MenuItem();
|
||||||
|
copyPath.Header = App.Text("CopyPath");
|
||||||
|
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||||
|
copyPath.Click += (_, e) =>
|
||||||
|
{
|
||||||
|
App.CopyText(change.Path);
|
||||||
|
e.Handled = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
var copyFileName = new MenuItem();
|
||||||
|
copyFileName.Header = App.Text("CopyFileName");
|
||||||
|
copyFileName.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||||
|
copyFileName.Click += (_, e) =>
|
||||||
|
{
|
||||||
|
App.CopyText(Path.GetFileName(change.Path));
|
||||||
|
e.Handled = true;
|
||||||
|
};
|
||||||
|
|
||||||
menu.Items.Add(copyPath);
|
menu.Items.Add(copyPath);
|
||||||
menu.Items.Add(copyFileName);
|
menu.Items.Add(copyFileName);
|
||||||
}
|
}
|
||||||
|
@ -1142,6 +1167,12 @@ namespace SourceGit.ViewModels
|
||||||
menu.Items.Add(unstage);
|
menu.Items.Add(unstage);
|
||||||
menu.Items.Add(stash);
|
menu.Items.Add(stash);
|
||||||
menu.Items.Add(patch);
|
menu.Items.Add(patch);
|
||||||
|
|
||||||
|
if (ai != null)
|
||||||
|
{
|
||||||
|
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||||
|
menu.Items.Add(ai);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return menu;
|
return menu;
|
||||||
|
@ -1211,6 +1242,51 @@ namespace SourceGit.ViewModels
|
||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ContextMenu CreateContextForOpenAI()
|
||||||
|
{
|
||||||
|
if (_staged == null || _staged.Count == 0)
|
||||||
|
{
|
||||||
|
App.RaiseException(_repo.FullPath, "No files added to commit!");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var services = GetPreferedOpenAIServices();
|
||||||
|
if (services.Count == 0)
|
||||||
|
{
|
||||||
|
App.RaiseException(_repo.FullPath, "Bad configuration for OpenAI");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (services.Count == 1)
|
||||||
|
{
|
||||||
|
var dialog = new Views.AIAssistant(services[0], _repo.FullPath, _staged, generated => CommitMessage = generated);
|
||||||
|
App.OpenDialog(dialog);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var menu = new ContextMenu() { Placement = PlacementMode.TopEdgeAlignedLeft };
|
||||||
|
|
||||||
|
foreach (var service in services)
|
||||||
|
{
|
||||||
|
var dup = service;
|
||||||
|
|
||||||
|
var item = new MenuItem();
|
||||||
|
item.Header = service.Name;
|
||||||
|
item.Click += (_, e) =>
|
||||||
|
{
|
||||||
|
var dialog = new Views.AIAssistant(dup, _repo.FullPath, _staged, generated => CommitMessage = generated);
|
||||||
|
App.OpenDialog(dialog);
|
||||||
|
e.Handled = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
menu.Items.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private List<Models.Change> GetStagedChanges()
|
private List<Models.Change> GetStagedChanges()
|
||||||
{
|
{
|
||||||
if (_useAmend)
|
if (_useAmend)
|
||||||
|
@ -1363,6 +1439,25 @@ namespace SourceGit.ViewModels
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IList<Models.OpenAIService> GetPreferedOpenAIServices()
|
||||||
|
{
|
||||||
|
var services = Preference.Instance.OpenAIServices;
|
||||||
|
if (services == null || services.Count == 0)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
if (services.Count == 1)
|
||||||
|
return services;
|
||||||
|
|
||||||
|
var prefered = _repo.Settings.PreferedOpenAIService;
|
||||||
|
foreach (var service in services)
|
||||||
|
{
|
||||||
|
if (service.Name.Equals(prefered, StringComparison.Ordinal))
|
||||||
|
return [service];
|
||||||
|
}
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
private Repository _repo = null;
|
private Repository _repo = null;
|
||||||
private bool _isLoadingData = false;
|
private bool _isLoadingData = false;
|
||||||
private bool _isStaging = false;
|
private bool _isStaging = false;
|
||||||
|
|
|
@ -17,12 +17,14 @@ namespace SourceGit.Views
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
public AIAssistant(string repo, List<Models.Change> changes, Action<string> onDone)
|
public AIAssistant(Models.OpenAIService service, string repo, List<Models.Change> changes, Action<string> onDone)
|
||||||
{
|
{
|
||||||
|
_service = service;
|
||||||
_repo = repo;
|
_repo = repo;
|
||||||
_changes = changes;
|
_changes = changes;
|
||||||
_onDone = onDone;
|
_onDone = onDone;
|
||||||
_cancel = new CancellationTokenSource();
|
_cancel = new CancellationTokenSource();
|
||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +37,7 @@ namespace SourceGit.Views
|
||||||
|
|
||||||
Task.Run(() =>
|
Task.Run(() =>
|
||||||
{
|
{
|
||||||
var message = new Commands.GenerateCommitMessage(_repo, _changes, _cancel.Token, SetDescription).Result();
|
var message = new Commands.GenerateCommitMessage(_service, _repo, _changes, _cancel.Token, SetDescription).Result();
|
||||||
if (_cancel.IsCancellationRequested)
|
if (_cancel.IsCancellationRequested)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -63,6 +65,7 @@ namespace SourceGit.Views
|
||||||
Dispatcher.UIThread.Invoke(() => ProgressMessage.Text = message);
|
Dispatcher.UIThread.Invoke(() => ProgressMessage.Text = message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Models.OpenAIService _service;
|
||||||
private string _repo;
|
private string _repo;
|
||||||
private List<Models.Change> _changes;
|
private List<Models.Change> _changes;
|
||||||
private Action<string> _onDone;
|
private Action<string> _onDone;
|
||||||
|
|
|
@ -412,7 +412,7 @@
|
||||||
<TextBlock Classes="bold" Margin="4,0,0,0" Text="{DynamicResource Text.Preference.DiffMerge}"/>
|
<TextBlock Classes="bold" Margin="4,0,0,0" Text="{DynamicResource Text.Preference.DiffMerge}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<Rectangle Margin="0,8" Fill="{DynamicResource Brush.Border2}" Height=".6" HorizontalAlignment="Stretch"/>
|
<Rectangle Margin="0,8" Fill="{DynamicResource Brush.Border2}" Height=".6" HorizontalAlignment="Stretch"/>
|
||||||
<Grid Margin="8,0,0,0" RowDefinitions="32,Auto">
|
<Grid Margin="8,0,0,8" RowDefinitions="32,Auto">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="Auto" SharedSizeGroup="IntegrationLabel"/>
|
<ColumnDefinition Width="Auto" SharedSizeGroup="IntegrationLabel"/>
|
||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="*"/>
|
||||||
|
@ -459,82 +459,117 @@
|
||||||
</TextBox.InnerRightContent>
|
</TextBox.InnerRightContent>
|
||||||
</TextBox>
|
</TextBox>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<StackPanel Orientation="Horizontal" Margin="0,24,0,0">
|
|
||||||
<Path Width="12" Height="12" Data="{StaticResource Icons.AIAssist}"/>
|
|
||||||
<TextBlock Classes="bold" Margin="4,0,0,0" Text="{DynamicResource Text.Preference.AI}"/>
|
|
||||||
</StackPanel>
|
|
||||||
<Rectangle Margin="0,8" Fill="{DynamicResource Brush.Border2}" Height=".6" HorizontalAlignment="Stretch"/>
|
|
||||||
<Grid Margin="8,0,0,0" RowDefinitions="32,32,32,32,Auto,Auto">
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="Auto" SharedSizeGroup="IntegrationLabel"/>
|
|
||||||
<ColumnDefinition Width="*"/>
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
|
|
||||||
<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.Model}"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
Margin="0,0,16,0"/>
|
|
||||||
<TextBox Grid.Row="1" Grid.Column="1"
|
|
||||||
Height="28"
|
|
||||||
CornerRadius="3"
|
|
||||||
Text="{Binding OpenAIModel, Mode=TwoWay}"/>
|
|
||||||
|
|
||||||
<TextBlock Grid.Row="2" Grid.Column="0"
|
|
||||||
Text="{DynamicResource Text.Preference.AI.ApiKey}"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
Margin="0,0,16,0"/>
|
|
||||||
<TextBox Grid.Row="2" Grid.Column="1"
|
|
||||||
Height="28"
|
|
||||||
CornerRadius="3"
|
|
||||||
PasswordChar="*"
|
|
||||||
Text="{Binding OpenAIApiKey, Mode=TwoWay}"/>
|
|
||||||
|
|
||||||
<ToggleButton Grid.Row="3" Grid.Column="1" Classes="group_expander" x:Name="OpenAIAdvancedOptions" HorizontalAlignment="Right">
|
|
||||||
<TextBlock Margin="0" Text="{DynamicResource Text.Preference.Advanced}"/>
|
|
||||||
</ToggleButton>
|
|
||||||
|
|
||||||
<TextBlock Grid.Row="4" Grid.Column="0"
|
|
||||||
Text="{DynamicResource Text.Preference.AI.AnalyzeDiffPrompt}"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
Margin="0,0,16,0"
|
|
||||||
IsVisible="{Binding #OpenAIAdvancedOptions.IsChecked}"/>
|
|
||||||
<TextBox Grid.Row="4" Grid.Column="1"
|
|
||||||
Height="120"
|
|
||||||
Margin="0,2"
|
|
||||||
CornerRadius="3"
|
|
||||||
VerticalContentAlignment="Top"
|
|
||||||
Text="{Binding OpenAIAnalyzeDiffPrompt, Mode=TwoWay}"
|
|
||||||
AcceptsReturn="true"
|
|
||||||
TextWrapping="Wrap"
|
|
||||||
IsVisible="{Binding #OpenAIAdvancedOptions.IsChecked}"/>
|
|
||||||
|
|
||||||
<TextBlock Grid.Row="5" Grid.Column="0"
|
|
||||||
Text="{DynamicResource Text.Preference.AI.GenerateSubjectPrompt}"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
Margin="0,0,16,0"
|
|
||||||
IsVisible="{Binding #OpenAIAdvancedOptions.IsChecked}"/>
|
|
||||||
<TextBox Grid.Row="5" Grid.Column="1"
|
|
||||||
Height="120"
|
|
||||||
Margin="0,2"
|
|
||||||
CornerRadius="3"
|
|
||||||
VerticalContentAlignment="Top"
|
|
||||||
Text="{Binding OpenAIGenerateSubjectPrompt, Mode=TwoWay}"
|
|
||||||
AcceptsReturn="true"
|
|
||||||
TextWrapping="Wrap"
|
|
||||||
IsVisible="{Binding #OpenAIAdvancedOptions.IsChecked}"/>
|
|
||||||
</Grid>
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|
||||||
|
<TabItem>
|
||||||
|
<TabItem.Header>
|
||||||
|
<TextBlock Classes="tab_header" Text="{DynamicResource Text.Preference.AI}"/>
|
||||||
|
</TabItem.Header>
|
||||||
|
|
||||||
|
<Grid ColumnDefinitions="200,*" Margin="0,8,0,16" MinHeight="400">
|
||||||
|
<Border Grid.Column="0"
|
||||||
|
BorderThickness="1" BorderBrush="{DynamicResource Brush.Border2}"
|
||||||
|
Background="{DynamicResource Brush.Contents}">
|
||||||
|
<Grid RowDefinitions="*,1,Auto">
|
||||||
|
<ListBox Grid.Row="0"
|
||||||
|
Background="Transparent"
|
||||||
|
ItemsSource="{Binding OpenAIServices}"
|
||||||
|
SelectedItem="{Binding #ThisControl.SelectedOpenAIService, Mode=TwoWay}"
|
||||||
|
SelectionMode="Single">
|
||||||
|
<ListBox.Styles>
|
||||||
|
<Style Selector="ListBoxItem">
|
||||||
|
<Setter Property="MinHeight" Value="0"/>
|
||||||
|
<Setter Property="Height" Value="26"/>
|
||||||
|
<Setter Property="Padding" Value="4,2"/>
|
||||||
|
</Style>
|
||||||
|
</ListBox.Styles>
|
||||||
|
|
||||||
|
<ListBox.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<StackPanel Orientation="Vertical"/>
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ListBox.ItemsPanel>
|
||||||
|
|
||||||
|
<ListBox.ItemTemplate>
|
||||||
|
<DataTemplate DataType="m:OpenAIService">
|
||||||
|
<Grid ColumnDefinitions="Auto,*">
|
||||||
|
<Path Grid.Column="0" Width="14" Height="14" Data="{StaticResource Icons.AIAssist}"/>
|
||||||
|
<TextBlock Grid.Column="1" Text="{Binding Name}" Margin="8,0" TextTrimming="CharacterEllipsis"/>
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</ListBox.ItemTemplate>
|
||||||
|
</ListBox>
|
||||||
|
|
||||||
|
<Rectangle Grid.Row="1" Height="1" Fill="{DynamicResource Brush.Border2}" HorizontalAlignment="Stretch" VerticalAlignment="Bottom"/>
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="2" Orientation="Horizontal" Background="{DynamicResource Brush.ToolBar}">
|
||||||
|
<Button Classes="icon_button" Click="OnAddOpenAIService">
|
||||||
|
<Path Width="14" Height="14" Data="{StaticResource Icons.Plus}"/>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Rectangle Width="1" Fill="{DynamicResource Brush.Border2}" HorizontalAlignment="Left" VerticalAlignment="Stretch"/>
|
||||||
|
|
||||||
|
<Button Classes="icon_button" Click="OnRemoveSelectedOpenAIService">
|
||||||
|
<Path Width="14" Height="14" Data="{StaticResource Icons.Window.Minimize}"/>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Rectangle Width="1" Fill="{DynamicResource Brush.Border2}" HorizontalAlignment="Left" VerticalAlignment="Stretch"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<ContentControl Grid.Column="1" Margin="16,0,0,0">
|
||||||
|
<ContentControl.Content>
|
||||||
|
<Binding Path="#ThisControl.SelectedOpenAIService">
|
||||||
|
<Binding.TargetNullValue>
|
||||||
|
<Path Width="64" Height="64"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Fill="{DynamicResource Brush.FG2}"
|
||||||
|
Data="{StaticResource Icons.Empty}"/>
|
||||||
|
</Binding.TargetNullValue>
|
||||||
|
</Binding>
|
||||||
|
</ContentControl.Content>
|
||||||
|
|
||||||
|
<ContentControl.DataTemplates>
|
||||||
|
<DataTemplate DataType="m:OpenAIService">
|
||||||
|
<StackPanel Orientation="Vertical" MaxWidth="680">
|
||||||
|
<TextBlock Text="{DynamicResource Text.Preference.AI.Name}"/>
|
||||||
|
<TextBox Margin="0,4,0,0" CornerRadius="3" Height="28" Text="{Binding Name, Mode=TwoWay}"/>
|
||||||
|
|
||||||
|
<TextBlock Margin="0,12,0,0" Text="{DynamicResource Text.Preference.AI.Server}"/>
|
||||||
|
<TextBox Margin="0,4,0,0" CornerRadius="3" Height="28" Text="{Binding Server, Mode=TwoWay}"/>
|
||||||
|
|
||||||
|
<TextBlock Margin="0,12,0,0" Text="{DynamicResource Text.Preference.AI.Model}"/>
|
||||||
|
<TextBox Margin="0,4,0,0" CornerRadius="3" Height="28" Text="{Binding Model, Mode=TwoWay}"/>
|
||||||
|
|
||||||
|
<TextBlock Margin="0,12,0,0" Text="{DynamicResource Text.Preference.AI.ApiKey}"/>
|
||||||
|
<TextBox Margin="0,4,0,0" CornerRadius="3" Height="28" Text="{Binding ApiKey, Mode=TwoWay}" PasswordChar="*"/>
|
||||||
|
|
||||||
|
<TextBlock Margin="0,12,0,0" Text="{DynamicResource Text.Preference.AI.AnalyzeDiffPrompt}"/>
|
||||||
|
<TextBox Height="120"
|
||||||
|
Margin="0,4,0,0"
|
||||||
|
CornerRadius="3"
|
||||||
|
VerticalContentAlignment="Top"
|
||||||
|
Text="{Binding AnalyzeDiffPrompt, Mode=TwoWay}"
|
||||||
|
AcceptsReturn="true"
|
||||||
|
TextWrapping="Wrap"/>
|
||||||
|
|
||||||
|
<TextBlock Margin="0,12,0,0" Text="{DynamicResource Text.Preference.AI.GenerateSubjectPrompt}"/>
|
||||||
|
<TextBox Height="120"
|
||||||
|
Margin="0,4,0,0"
|
||||||
|
CornerRadius="3"
|
||||||
|
VerticalContentAlignment="Top"
|
||||||
|
Text="{Binding GenerateSubjectPrompt, Mode=TwoWay}"
|
||||||
|
AcceptsReturn="true"
|
||||||
|
TextWrapping="Wrap"/>
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
</ContentControl.DataTemplates>
|
||||||
|
</ContentControl>
|
||||||
|
</Grid>
|
||||||
|
</TabItem>
|
||||||
</TabControl>
|
</TabControl>
|
||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
|
@ -74,6 +74,15 @@ namespace SourceGit.Views
|
||||||
set;
|
set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<Models.OpenAIService> SelectedOpenAIServiceProperty =
|
||||||
|
AvaloniaProperty.Register<Preference, Models.OpenAIService>(nameof(SelectedOpenAIService));
|
||||||
|
|
||||||
|
public Models.OpenAIService SelectedOpenAIService
|
||||||
|
{
|
||||||
|
get => GetValue(SelectedOpenAIServiceProperty);
|
||||||
|
set => SetValue(SelectedOpenAIServiceProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
public Preference()
|
public Preference()
|
||||||
{
|
{
|
||||||
var pref = ViewModels.Preference.Instance;
|
var pref = ViewModels.Preference.Instance;
|
||||||
|
@ -312,5 +321,24 @@ namespace SourceGit.Views
|
||||||
|
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnAddOpenAIService(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var service = new Models.OpenAIService() { Name = "Unnamed Service" };
|
||||||
|
ViewModels.Preference.Instance.OpenAIServices.Add(service);
|
||||||
|
SelectedOpenAIService = service;
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRemoveSelectedOpenAIService(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (SelectedOpenAIService == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ViewModels.Preference.Instance.OpenAIServices.Remove(SelectedOpenAIService);
|
||||||
|
SelectedOpenAIService = null;
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -340,6 +340,39 @@
|
||||||
</ContentControl>
|
</ContentControl>
|
||||||
</Grid>
|
</Grid>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|
||||||
|
<TabItem>
|
||||||
|
<TabItem.Header>
|
||||||
|
<TextBlock Classes="tab_header" Text="{DynamicResource Text.Configure.OpenAI}"/>
|
||||||
|
</TabItem.Header>
|
||||||
|
|
||||||
|
<Grid Margin="16,4,16,8" RowDefinitions="32,Auto" ColumnDefinitions="Auto,*">
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="0"
|
||||||
|
HorizontalAlignment="Right" VerticalAlignment="Center"
|
||||||
|
Margin="0,0,8,0"
|
||||||
|
Text="{DynamicResource Text.Configure.OpenAI.Prefered}"/>
|
||||||
|
<ComboBox Grid.Row="0" Grid.Column="1"
|
||||||
|
Height="28" Padding="8,0"
|
||||||
|
VerticalAlignment="Center" HorizontalAlignment="Stretch"
|
||||||
|
ItemsSource="{Binding AvailableOpenAIServices}"
|
||||||
|
SelectedItem="{Binding PreferedOpenAIService, Mode=TwoWay}">
|
||||||
|
<ComboBox.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<StackPanel Orientation="Horizontal" Height="20" VerticalAlignment="Center">
|
||||||
|
<Path Width="12" Height="12" Data="{StaticResource Icons.AIAssist}" Fill="{DynamicResource Brush.FG1}"/>
|
||||||
|
<TextBlock Margin="6,0,0,0" Text="{Binding}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
</ComboBox.ItemTemplate>
|
||||||
|
</ComboBox>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1" Grid.Column="1"
|
||||||
|
Margin="0,6,0,0"
|
||||||
|
Text="{DynamicResource Text.Configure.OpenAI.Prefered.Tip}"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Foreground="{DynamicResource Brush.FG2}"/>
|
||||||
|
</Grid>
|
||||||
|
</TabItem>
|
||||||
</TabControl>
|
</TabControl>
|
||||||
</Grid>
|
</Grid>
|
||||||
</v:ChromelessWindow>
|
</v:ChromelessWindow>
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
<TextBlock FontSize="18"
|
<TextBlock FontSize="18"
|
||||||
Classes="bold"
|
Classes="bold"
|
||||||
Text="{DynamicResource Text.Stash.Title}"/>
|
Text="{DynamicResource Text.Stash.Title}"/>
|
||||||
<Grid Margin="8,16,0,0" RowDefinitions="32,Auto,Auto" ColumnDefinitions="120,*">
|
<Grid Margin="8,16,0,0" RowDefinitions="32,Auto,Auto,32,Auto" ColumnDefinitions="120,*">
|
||||||
<TextBlock Grid.Row="0" Grid.Column="0"
|
<TextBlock Grid.Row="0" Grid.Column="0"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
Margin="8,0"
|
Margin="8,0"
|
||||||
|
@ -23,13 +23,6 @@
|
||||||
Watermark="{DynamicResource Text.Stash.Message.Placeholder}"
|
Watermark="{DynamicResource Text.Stash.Message.Placeholder}"
|
||||||
v:AutoFocusBehaviour.IsEnabled="True"/>
|
v:AutoFocusBehaviour.IsEnabled="True"/>
|
||||||
|
|
||||||
<TextBlock Grid.Row="1" Grid.Column="1"
|
|
||||||
Margin="0,4,0,0"
|
|
||||||
Text="{DynamicResource Text.Stash.TipForSelectedFiles}"
|
|
||||||
TextWrapping="Wrap"
|
|
||||||
Foreground="{DynamicResource Brush.FG2}"
|
|
||||||
IsVisible="{Binding HasSelectedFiles}"/>
|
|
||||||
|
|
||||||
<CheckBox Grid.Row="1" Grid.Column="1"
|
<CheckBox Grid.Row="1" Grid.Column="1"
|
||||||
Height="32"
|
Height="32"
|
||||||
Content="{DynamicResource Text.Stash.IncludeUntracked}"
|
Content="{DynamicResource Text.Stash.IncludeUntracked}"
|
||||||
|
@ -41,6 +34,18 @@
|
||||||
Content="{DynamicResource Text.Stash.OnlyStagedChanges}"
|
Content="{DynamicResource Text.Stash.OnlyStagedChanges}"
|
||||||
IsChecked="{Binding OnlyStaged, Mode=TwoWay}"
|
IsChecked="{Binding OnlyStaged, Mode=TwoWay}"
|
||||||
IsVisible="{Binding !HasSelectedFiles}"/>
|
IsVisible="{Binding !HasSelectedFiles}"/>
|
||||||
|
|
||||||
|
<CheckBox Grid.Row="3" Grid.Column="1"
|
||||||
|
Height="32"
|
||||||
|
Content="{DynamicResource Text.Stash.KeepIndex}"
|
||||||
|
IsChecked="{Binding KeepIndex, Mode=TwoWay}"/>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="4" Grid.Column="1"
|
||||||
|
Margin="0,4,0,0"
|
||||||
|
Text="{DynamicResource Text.Stash.TipForSelectedFiles}"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Foreground="{DynamicResource Brush.FG2}"
|
||||||
|
IsVisible="{Binding HasSelectedFiles}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|
|
@ -199,7 +199,7 @@
|
||||||
<Button Grid.Column="1"
|
<Button Grid.Column="1"
|
||||||
Classes="icon_button"
|
Classes="icon_button"
|
||||||
Margin="4,2,0,0"
|
Margin="4,2,0,0"
|
||||||
Command="{Binding GenerateCommitMessageByAI}"
|
Click="OnOpenOpenAIHelper"
|
||||||
ToolTip.Tip="{DynamicResource Text.AIAssistant.Tip}"
|
ToolTip.Tip="{DynamicResource Text.AIAssistant.Tip}"
|
||||||
ToolTip.Placement="Top"
|
ToolTip.Placement="Top"
|
||||||
ToolTip.VerticalOffset="0">
|
ToolTip.VerticalOffset="0">
|
||||||
|
|
|
@ -120,6 +120,17 @@ namespace SourceGit.Views
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnOpenOpenAIHelper(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (DataContext is ViewModels.WorkingCopy vm)
|
||||||
|
{
|
||||||
|
var menu = vm.CreateContextForOpenAI();
|
||||||
|
(sender as Button)?.OpenContextMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
private void OnOpenConventionalCommitHelper(object _, RoutedEventArgs e)
|
private void OnOpenConventionalCommitHelper(object _, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (DataContext is ViewModels.WorkingCopy vm)
|
if (DataContext is ViewModels.WorkingCopy vm)
|
||||||
|
|
Loading…
Reference in a new issue