mirror of
https://fastgit.cc/github.com/sourcegit-scm/sourcegit
synced 2026-04-20 21:01:06 +08:00
Merge branch 'release/v2026.08'
This commit is contained in:
@@ -7,7 +7,7 @@ The project uses the following third-party libraries or assets
|
||||
### AvaloniaUI
|
||||
|
||||
- **Source**: https://github.com/AvaloniaUI/Avalonia
|
||||
- **Version**: 11.3.12
|
||||
- **Version**: 11.3.13
|
||||
- **License**: MIT License
|
||||
- **License Link**: https://github.com/AvaloniaUI/Avalonia/blob/master/licence.md
|
||||
|
||||
@@ -22,7 +22,7 @@ The project uses the following third-party libraries or assets
|
||||
### LiveChartsCore.SkiaSharpView.Avalonia
|
||||
|
||||
- **Source**: https://github.com/beto-rodriguez/LiveCharts2
|
||||
- **Version**: 2.0.0-rc6.1
|
||||
- **Version**: 2.0.0
|
||||
- **License**: MIT License
|
||||
- **License Link**: https://github.com/beto-rodriguez/LiveCharts2/blob/master/LICENSE
|
||||
|
||||
@@ -36,7 +36,7 @@ The project uses the following third-party libraries or assets
|
||||
### OpenAI .NET SDK
|
||||
|
||||
- **Source**: https://github.com/openai/openai-dotnet
|
||||
- **Version**: 2.9.1
|
||||
- **Version**: 2.10.0
|
||||
- **License**: MIT License
|
||||
- **License Link**: https://github.com/openai/openai-dotnet/blob/main/LICENSE
|
||||
|
||||
|
||||
@@ -6,19 +6,23 @@ This document shows the translation status of each locale file in the repository
|
||||
|
||||
### 
|
||||
|
||||
### 
|
||||
### 
|
||||
|
||||
<details>
|
||||
<summary>Missing keys in de_DE.axaml</summary>
|
||||
|
||||
- Text.AIAssistant.Use
|
||||
- Text.Apply.3Way
|
||||
- Text.CheckoutBranchFromStash
|
||||
- Text.CheckoutBranchFromStash.Branch
|
||||
- Text.CheckoutBranchFromStash.Stash
|
||||
- Text.CommandPalette.Branches
|
||||
- Text.CommandPalette.BranchesAndTags
|
||||
- Text.CommandPalette.RepositoryActions
|
||||
- Text.CommandPalette.RevisionFiles
|
||||
- Text.CommitMessageTextBox.Column
|
||||
- Text.ConfirmEmptyCommit.StageSelectedThenCommit
|
||||
- Text.Discard.IncludeModified
|
||||
- Text.GotoRevisionSelector
|
||||
- Text.Hotkeys.Repo.CreateBranch
|
||||
- Text.Hotkeys.Repo.GoToChild
|
||||
@@ -27,6 +31,7 @@ This document shows the translation status of each locale file in the repository
|
||||
- Text.Preferences.AI.AdditionalPrompt
|
||||
- Text.Preferences.General.Use24Hours
|
||||
- Text.StashCM.ApplyFileChanges
|
||||
- Text.StashCM.Branch
|
||||
- Text.Worktree.Branch
|
||||
- Text.Worktree.Head
|
||||
- Text.Worktree.Path
|
||||
@@ -35,7 +40,7 @@ This document shows the translation status of each locale file in the repository
|
||||
|
||||
### 
|
||||
|
||||
### 
|
||||
### 
|
||||
|
||||
<details>
|
||||
<summary>Missing keys in fr_FR.axaml</summary>
|
||||
@@ -52,6 +57,9 @@ This document shows the translation status of each locale file in the repository
|
||||
- Text.ChangeCM.MergeExternal
|
||||
- Text.ChangeCM.ResetFileTo
|
||||
- Text.Checkout.WarnUpdatingSubmodules
|
||||
- Text.CheckoutBranchFromStash
|
||||
- Text.CheckoutBranchFromStash.Branch
|
||||
- Text.CheckoutBranchFromStash.Stash
|
||||
- Text.CommandPalette.Branches
|
||||
- Text.CommandPalette.BranchesAndTags
|
||||
- Text.CommandPalette.RepositoryActions
|
||||
@@ -61,6 +69,7 @@ This document shows the translation status of each locale file in the repository
|
||||
- Text.Compare.WithHead
|
||||
- Text.Configure.Git.AskBeforeAutoUpdatingSubmodules
|
||||
- Text.ConfirmEmptyCommit.StageSelectedThenCommit
|
||||
- Text.Discard.IncludeModified
|
||||
- Text.EditBranchDescription
|
||||
- Text.EditBranchDescription.Target
|
||||
- Text.FileCM.CustomAction
|
||||
@@ -110,6 +119,7 @@ This document shows the translation status of each locale file in the repository
|
||||
- Text.SquashOrFixup.Fixup
|
||||
- Text.SquashOrFixup.Into
|
||||
- Text.StashCM.ApplyFileChanges
|
||||
- Text.StashCM.Branch
|
||||
- Text.TagCM.CompareTwo
|
||||
- Text.TagCM.CompareWith
|
||||
- Text.TagCM.CompareWithHead
|
||||
@@ -122,7 +132,7 @@ This document shows the translation status of each locale file in the repository
|
||||
|
||||
</details>
|
||||
|
||||
### 
|
||||
### 
|
||||
|
||||
<details>
|
||||
<summary>Missing keys in id_ID.axaml</summary>
|
||||
@@ -143,6 +153,9 @@ This document shows the translation status of each locale file in the repository
|
||||
- Text.ChangeCM.MergeExternal
|
||||
- Text.ChangeCM.ResetFileTo
|
||||
- Text.Checkout.WarnUpdatingSubmodules
|
||||
- Text.CheckoutBranchFromStash
|
||||
- Text.CheckoutBranchFromStash.Branch
|
||||
- Text.CheckoutBranchFromStash.Stash
|
||||
- Text.CommandPalette.Branches
|
||||
- Text.CommandPalette.BranchesAndTags
|
||||
- Text.CommandPalette.RepositoryActions
|
||||
@@ -157,6 +170,7 @@ This document shows the translation status of each locale file in the repository
|
||||
- Text.ConfigureCustomActionControls.StringValue.Tip
|
||||
- Text.ConfirmEmptyCommit.StageSelectedThenCommit
|
||||
- Text.DealWithLocalChanges.DoNothing
|
||||
- Text.Discard.IncludeModified
|
||||
- Text.DropHead
|
||||
- Text.DropHead.Commit
|
||||
- Text.DropHead.NewHead
|
||||
@@ -218,6 +232,7 @@ This document shows the translation status of each locale file in the repository
|
||||
- Text.SquashOrFixup.Fixup
|
||||
- Text.SquashOrFixup.Into
|
||||
- Text.StashCM.ApplyFileChanges
|
||||
- Text.StashCM.Branch
|
||||
- Text.TagCM.CompareTwo
|
||||
- Text.TagCM.CompareWith
|
||||
- Text.TagCM.CompareWithHead
|
||||
@@ -230,7 +245,7 @@ This document shows the translation status of each locale file in the repository
|
||||
|
||||
</details>
|
||||
|
||||
### 
|
||||
### 
|
||||
|
||||
<details>
|
||||
<summary>Missing keys in it_IT.axaml</summary>
|
||||
@@ -238,12 +253,16 @@ This document shows the translation status of each locale file in the repository
|
||||
- Text.AIAssistant.Use
|
||||
- Text.Apply.3Way
|
||||
- Text.ChangeCM.ResetFileTo
|
||||
- Text.CheckoutBranchFromStash
|
||||
- Text.CheckoutBranchFromStash.Branch
|
||||
- Text.CheckoutBranchFromStash.Stash
|
||||
- Text.CommandPalette.Branches
|
||||
- Text.CommandPalette.BranchesAndTags
|
||||
- Text.CommandPalette.RepositoryActions
|
||||
- Text.CommandPalette.RevisionFiles
|
||||
- Text.CommitMessageTextBox.Column
|
||||
- Text.ConfirmEmptyCommit.StageSelectedThenCommit
|
||||
- Text.Discard.IncludeModified
|
||||
- Text.GotoRevisionSelector
|
||||
- Text.Histories.Header.DateTime
|
||||
- Text.Histories.ShowColumns
|
||||
@@ -257,37 +276,43 @@ This document shows the translation status of each locale file in the repository
|
||||
- Text.SelfUpdate.CurrentVersion
|
||||
- Text.SelfUpdate.ReleaseDate
|
||||
- Text.StashCM.ApplyFileChanges
|
||||
- Text.StashCM.Branch
|
||||
- Text.Worktree.Branch
|
||||
- Text.Worktree.Head
|
||||
- Text.Worktree.Path
|
||||
|
||||
</details>
|
||||
|
||||
### 
|
||||
### 
|
||||
|
||||
<details>
|
||||
<summary>Missing keys in ja_JP.axaml</summary>
|
||||
|
||||
- Text.AIAssistant.Use
|
||||
- Text.Apply.3Way
|
||||
- Text.CheckoutBranchFromStash
|
||||
- Text.CheckoutBranchFromStash.Branch
|
||||
- Text.CheckoutBranchFromStash.Stash
|
||||
- Text.CommandPalette.Branches
|
||||
- Text.CommandPalette.BranchesAndTags
|
||||
- Text.CommandPalette.RepositoryActions
|
||||
- Text.CommandPalette.RevisionFiles
|
||||
- Text.ConfirmEmptyCommit.StageSelectedThenCommit
|
||||
- Text.DealWithLocalChanges.DoNothing
|
||||
- Text.Discard.IncludeModified
|
||||
- Text.Hotkeys.Repo.CreateBranch
|
||||
- Text.Init.CommandTip
|
||||
- Text.Init.ErrorMessageTip
|
||||
- Text.Preferences.AI.AdditionalPrompt
|
||||
- Text.Preferences.General.Use24Hours
|
||||
- Text.StashCM.Branch
|
||||
- Text.Worktree.Branch
|
||||
- Text.Worktree.Head
|
||||
- Text.Worktree.Path
|
||||
|
||||
</details>
|
||||
|
||||
### 
|
||||
### 
|
||||
|
||||
<details>
|
||||
<summary>Missing keys in ko_KR.axaml</summary>
|
||||
@@ -308,6 +333,9 @@ This document shows the translation status of each locale file in the repository
|
||||
- Text.ChangeCM.MergeExternal
|
||||
- Text.ChangeCM.ResetFileTo
|
||||
- Text.Checkout.WarnUpdatingSubmodules
|
||||
- Text.CheckoutBranchFromStash
|
||||
- Text.CheckoutBranchFromStash.Branch
|
||||
- Text.CheckoutBranchFromStash.Stash
|
||||
- Text.CommandPalette.Branches
|
||||
- Text.CommandPalette.BranchesAndTags
|
||||
- Text.CommandPalette.RepositoryActions
|
||||
@@ -320,6 +348,7 @@ This document shows the translation status of each locale file in the repository
|
||||
- Text.ConfigureCustomActionControls.StringValue.Tip
|
||||
- Text.ConfirmEmptyCommit.StageSelectedThenCommit
|
||||
- Text.DealWithLocalChanges.DoNothing
|
||||
- Text.Discard.IncludeModified
|
||||
- Text.EditBranchDescription
|
||||
- Text.EditBranchDescription.Target
|
||||
- Text.FileCM.CustomAction
|
||||
@@ -379,6 +408,7 @@ This document shows the translation status of each locale file in the repository
|
||||
- Text.SquashOrFixup.Fixup
|
||||
- Text.SquashOrFixup.Into
|
||||
- Text.StashCM.ApplyFileChanges
|
||||
- Text.StashCM.Branch
|
||||
- Text.Submodule.Status.Unmerged
|
||||
- Text.TagCM.CompareTwo
|
||||
- Text.TagCM.CompareWith
|
||||
@@ -392,7 +422,7 @@ This document shows the translation status of each locale file in the repository
|
||||
|
||||
</details>
|
||||
|
||||
### 
|
||||
### 
|
||||
|
||||
<details>
|
||||
<summary>Missing keys in pt_BR.axaml</summary>
|
||||
@@ -416,6 +446,9 @@ This document shows the translation status of each locale file in the repository
|
||||
- Text.Checkout.WarnUpdatingSubmodules
|
||||
- Text.Checkout.WithFastForward
|
||||
- Text.Checkout.WithFastForward.Upstream
|
||||
- Text.CheckoutBranchFromStash
|
||||
- Text.CheckoutBranchFromStash.Branch
|
||||
- Text.CheckoutBranchFromStash.Stash
|
||||
- Text.Clone.RecurseSubmodules
|
||||
- Text.CommandPalette.Branches
|
||||
- Text.CommandPalette.BranchesAndTags
|
||||
@@ -509,6 +542,7 @@ This document shows the translation status of each locale file in the repository
|
||||
- Text.DirtyState.HasLocalChanges
|
||||
- Text.DirtyState.HasPendingPullOrPush
|
||||
- Text.DirtyState.UpToDate
|
||||
- Text.Discard.IncludeModified
|
||||
- Text.Discard.IncludeUntracked
|
||||
- Text.DropHead
|
||||
- Text.DropHead.Commit
|
||||
@@ -655,6 +689,7 @@ This document shows the translation status of each locale file in the repository
|
||||
- Text.SquashOrFixup.Into
|
||||
- Text.Stash.Mode
|
||||
- Text.StashCM.ApplyFileChanges
|
||||
- Text.StashCM.Branch
|
||||
- Text.StashCM.CopyMessage
|
||||
- Text.StashCM.SaveAsPatch
|
||||
- Text.Submodule.Branch
|
||||
@@ -711,7 +746,7 @@ This document shows the translation status of each locale file in the repository
|
||||
|
||||
### 
|
||||
|
||||
### 
|
||||
### 
|
||||
|
||||
<details>
|
||||
<summary>Missing keys in ta_IN.axaml</summary>
|
||||
@@ -764,6 +799,9 @@ This document shows the translation status of each locale file in the repository
|
||||
- Text.Checkout.WarnUpdatingSubmodules
|
||||
- Text.Checkout.WithFastForward
|
||||
- Text.Checkout.WithFastForward.Upstream
|
||||
- Text.CheckoutBranchFromStash
|
||||
- Text.CheckoutBranchFromStash.Branch
|
||||
- Text.CheckoutBranchFromStash.Stash
|
||||
- Text.CommandPalette.Branches
|
||||
- Text.CommandPalette.BranchesAndTags
|
||||
- Text.CommandPalette.RepositoryActions
|
||||
@@ -843,6 +881,7 @@ This document shows the translation status of each locale file in the repository
|
||||
- Text.DirtyState.HasLocalChanges
|
||||
- Text.DirtyState.HasPendingPullOrPush
|
||||
- Text.DirtyState.UpToDate
|
||||
- Text.Discard.IncludeModified
|
||||
- Text.Discard.IncludeUntracked
|
||||
- Text.DropHead
|
||||
- Text.DropHead.Commit
|
||||
@@ -957,6 +996,7 @@ This document shows the translation status of each locale file in the repository
|
||||
- Text.SquashOrFixup.Into
|
||||
- Text.Stash.Mode
|
||||
- Text.StashCM.ApplyFileChanges
|
||||
- Text.StashCM.Branch
|
||||
- Text.StashCM.CopyMessage
|
||||
- Text.Submodule.Branch
|
||||
- Text.Submodule.CopyBranch
|
||||
@@ -1008,7 +1048,7 @@ This document shows the translation status of each locale file in the repository
|
||||
|
||||
</details>
|
||||
|
||||
### 
|
||||
### 
|
||||
|
||||
<details>
|
||||
<summary>Missing keys in uk_UA.axaml</summary>
|
||||
@@ -1061,6 +1101,9 @@ This document shows the translation status of each locale file in the repository
|
||||
- Text.Checkout.WarnUpdatingSubmodules
|
||||
- Text.Checkout.WithFastForward
|
||||
- Text.Checkout.WithFastForward.Upstream
|
||||
- Text.CheckoutBranchFromStash
|
||||
- Text.CheckoutBranchFromStash.Branch
|
||||
- Text.CheckoutBranchFromStash.Stash
|
||||
- Text.CommandPalette.Branches
|
||||
- Text.CommandPalette.BranchesAndTags
|
||||
- Text.CommandPalette.RepositoryActions
|
||||
@@ -1136,6 +1179,7 @@ This document shows the translation status of each locale file in the repository
|
||||
- Text.DirtyState.HasLocalChanges
|
||||
- Text.DirtyState.HasPendingPullOrPush
|
||||
- Text.DirtyState.UpToDate
|
||||
- Text.Discard.IncludeModified
|
||||
- Text.Discard.IncludeUntracked
|
||||
- Text.DropHead
|
||||
- Text.DropHead.Commit
|
||||
@@ -1250,6 +1294,7 @@ This document shows the translation status of each locale file in the repository
|
||||
- Text.SquashOrFixup.Into
|
||||
- Text.Stash.Mode
|
||||
- Text.StashCM.ApplyFileChanges
|
||||
- Text.StashCM.Branch
|
||||
- Text.StashCM.CopyMessage
|
||||
- Text.Submodule.Branch
|
||||
- Text.Submodule.CopyBranch
|
||||
|
||||
Submodule depends/AvaloniaEdit updated: 87e83c5c0e...1e6f595ff0
@@ -3,8 +3,6 @@ using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Azure.AI.OpenAI;
|
||||
using OpenAI;
|
||||
using OpenAI.Chat;
|
||||
|
||||
namespace SourceGit.AI
|
||||
@@ -18,12 +16,10 @@ namespace SourceGit.AI
|
||||
|
||||
public async Task GenerateCommitMessageAsync(string repo, string changeList, Action<string> onUpdate, CancellationToken cancellation)
|
||||
{
|
||||
var endPoint = new Uri(_service.Server);
|
||||
var client = _service.Server.Contains("openai.azure.com/", StringComparison.Ordinal)
|
||||
? new AzureOpenAIClient(endPoint, _service.Credential)
|
||||
: new OpenAIClient(_service.Credential, new() { Endpoint = endPoint });
|
||||
var chatClient = _service.GetChatClient();
|
||||
if (chatClient == null)
|
||||
throw new Exception("Failed to fetch available models from this service. Please check your configuration and try again.");
|
||||
|
||||
var chatClient = client.GetChatClient(_service.Model);
|
||||
var options = new ChatCompletionOptions() { Tools = { ChatTools.GetDetailChangesInFile } };
|
||||
|
||||
var userMessageBuilder = new StringBuilder();
|
||||
@@ -31,8 +27,8 @@ namespace SourceGit.AI
|
||||
.AppendLine("Generate a commit message (follow the rule of conventional commit message) for given git repository.")
|
||||
.AppendLine("- Read all given changed files before generating. Only binary files (such as images, audios ...) can be skipped.")
|
||||
.AppendLine("- Output the conventional commit message (with detail changes in list) directly. Do not explain your output nor introduce your answer.")
|
||||
.AppendLine(string.IsNullOrEmpty(_service.AdditionalPrompt) ? string.Empty : _service.AdditionalPrompt)
|
||||
.Append("Reposiory path: ").AppendLine(repo.Quoted())
|
||||
.AppendLine(_service.AdditionalPrompt)
|
||||
.Append("Repository path: ").AppendLine(repo.Quoted())
|
||||
.AppendLine("Changed files ('A' means added, 'M' means modified, 'D' means deleted, 'T' means type changed, 'R' means renamed, 'C' means copied): ")
|
||||
.Append(changeList);
|
||||
|
||||
@@ -65,7 +61,7 @@ namespace SourceGit.AI
|
||||
|
||||
foreach (var call in completion.ToolCalls)
|
||||
{
|
||||
var result = await ChatTools.Process(call, onUpdate);
|
||||
var result = await ChatTools.ProcessAsync(call, onUpdate);
|
||||
messages.Add(result);
|
||||
}
|
||||
|
||||
@@ -73,7 +69,7 @@ namespace SourceGit.AI
|
||||
break;
|
||||
}
|
||||
case ChatFinishReason.ContentFilter:
|
||||
throw new Exception("Ommitted content due to a content filter flag");
|
||||
throw new Exception("Omitted content due to a content filter flag");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace SourceGit.AI
|
||||
}
|
||||
""")), false);
|
||||
|
||||
public static async Task<ToolChatMessage> Process(ChatToolCall call, Action<string> output)
|
||||
public static async Task<ToolChatMessage> ProcessAsync(ChatToolCall call, Action<string> output)
|
||||
{
|
||||
using var doc = JsonDocument.Parse(call.FunctionArguments);
|
||||
|
||||
|
||||
@@ -1,16 +1,93 @@
|
||||
using System;
|
||||
using System.ClientModel;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
using Azure.AI.OpenAI;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using OpenAI;
|
||||
using OpenAI.Chat;
|
||||
|
||||
namespace SourceGit.AI
|
||||
{
|
||||
public class Service
|
||||
public class Service : ObservableObject
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Server { get; set; }
|
||||
public string Model { get; set; }
|
||||
public string ApiKey { get; set; }
|
||||
public bool ReadApiKeyFromEnv { get; set; }
|
||||
public string AdditionalPrompt { get; set; }
|
||||
public ApiKeyCredential Credential => new ApiKeyCredential(ReadApiKeyFromEnv ? Environment.GetEnvironmentVariable(ApiKey) : ApiKey);
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set => SetProperty(ref _name, value);
|
||||
}
|
||||
|
||||
public string Server
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} = string.Empty;
|
||||
|
||||
public string ApiKey
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} = string.Empty;
|
||||
|
||||
public bool ReadApiKeyFromEnv
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} = false;
|
||||
|
||||
public string AdditionalPrompt
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} = string.Empty;
|
||||
|
||||
[JsonIgnore]
|
||||
public List<string> AvailableModels
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
} = [];
|
||||
|
||||
public string Model
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} = string.Empty;
|
||||
|
||||
public async Task<List<string>> FetchAvailableModelsAsync()
|
||||
{
|
||||
var allModels = GetOpenAIClient().GetOpenAIModelClient().GetModels();
|
||||
AvailableModels = new List<string>();
|
||||
foreach (var model in allModels.Value)
|
||||
AvailableModels.Add(model.Id);
|
||||
|
||||
if (AvailableModels.Count > 0)
|
||||
{
|
||||
if (string.IsNullOrEmpty(Model) || !AvailableModels.Contains(Model))
|
||||
Model = AvailableModels[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
Model = null;
|
||||
}
|
||||
|
||||
return AvailableModels;
|
||||
}
|
||||
|
||||
public ChatClient GetChatClient()
|
||||
{
|
||||
return !string.IsNullOrEmpty(Model) ? GetOpenAIClient().GetChatClient(Model) : null;
|
||||
}
|
||||
|
||||
private OpenAIClient GetOpenAIClient()
|
||||
{
|
||||
var credential = new ApiKeyCredential(ReadApiKeyFromEnv ? Environment.GetEnvironmentVariable(ApiKey) : ApiKey);
|
||||
return Server.Contains("openai.azure.com/", StringComparison.Ordinal)
|
||||
? new AzureOpenAIClient(new Uri(Server), credential, new AzureOpenAIClientOptions() { UserAgentApplicationId = string.Empty })
|
||||
: new OpenAIClient(credential, new() { Endpoint = new Uri(Server), UserAgentApplicationId = string.Empty });
|
||||
}
|
||||
|
||||
private string _name = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using System;
|
||||
using System.Windows.Input;
|
||||
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
|
||||
namespace SourceGit
|
||||
@@ -45,16 +43,6 @@ namespace SourceGit
|
||||
public static readonly Command OpenAboutCommand = new Command(async _ => await ShowDialog(new Views.About()));
|
||||
public static readonly Command CheckForUpdateCommand = new Command(_ => (Current as App)?.Check4Update(true));
|
||||
public static readonly Command QuitCommand = new Command(_ => Quit(0));
|
||||
public static readonly Command CopyTextBlockCommand = new Command(async p =>
|
||||
{
|
||||
if (p is not TextBlock textBlock)
|
||||
return;
|
||||
|
||||
if (textBlock.Inlines is { Count: > 0 } inlines)
|
||||
await CopyTextAsync(inlines.Text);
|
||||
else if (!string.IsNullOrEmpty(textBlock.Text))
|
||||
await CopyTextAsync(textBlock.Text);
|
||||
});
|
||||
|
||||
public static readonly Command HideAppCommand = new Command(_ =>
|
||||
{
|
||||
|
||||
178
src/App.axaml.cs
178
src/App.axaml.cs
@@ -1,19 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Data.Core.Plugins;
|
||||
using Avalonia.Input.Platform;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Media.Fonts;
|
||||
@@ -32,7 +28,7 @@ namespace SourceGit
|
||||
|
||||
AppDomain.CurrentDomain.UnhandledException += (_, e) =>
|
||||
{
|
||||
LogException(e.ExceptionObject as Exception);
|
||||
Native.OS.LogException(e.ExceptionObject as Exception);
|
||||
};
|
||||
|
||||
TaskScheduler.UnobservedTaskException += (_, e) =>
|
||||
@@ -51,7 +47,7 @@ namespace SourceGit
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogException(ex);
|
||||
Native.OS.LogException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,52 +72,9 @@ namespace SourceGit
|
||||
Native.OS.SetupApp(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static void LogException(Exception ex)
|
||||
{
|
||||
if (ex == null)
|
||||
return;
|
||||
|
||||
var crashDir = Path.Combine(Native.OS.DataDir, "crashes");
|
||||
if (!Directory.Exists(crashDir))
|
||||
Directory.CreateDirectory(crashDir);
|
||||
|
||||
var time = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
|
||||
var file = Path.Combine(crashDir, $"{time}.log");
|
||||
using var writer = new StreamWriter(file);
|
||||
writer.WriteLine($"Crash::: {ex.GetType().FullName}: {ex.Message}");
|
||||
writer.WriteLine();
|
||||
writer.WriteLine("----------------------------");
|
||||
writer.WriteLine($"Version: {Assembly.GetExecutingAssembly().GetName().Version}");
|
||||
writer.WriteLine($"OS: {Environment.OSVersion}");
|
||||
writer.WriteLine($"Framework: {AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName}");
|
||||
writer.WriteLine($"Source: {ex.Source}");
|
||||
writer.WriteLine($"Thread Name: {Thread.CurrentThread.Name ?? "Unnamed"}");
|
||||
writer.WriteLine($"App Start Time: {Process.GetCurrentProcess().StartTime}");
|
||||
writer.WriteLine($"Exception Time: {DateTime.Now}");
|
||||
writer.WriteLine($"Memory Usage: {Process.GetCurrentProcess().PrivateMemorySize64 / 1024 / 1024} MB");
|
||||
writer.WriteLine("----------------------------");
|
||||
writer.WriteLine();
|
||||
writer.WriteLine(ex);
|
||||
writer.Flush();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Utility Functions
|
||||
public static Control CreateViewForViewModel(object data)
|
||||
{
|
||||
var dataTypeName = data.GetType().FullName;
|
||||
if (string.IsNullOrEmpty(dataTypeName) || !dataTypeName.Contains(".ViewModels.", StringComparison.Ordinal))
|
||||
return null;
|
||||
|
||||
var viewTypeName = dataTypeName.Replace(".ViewModels.", ".Views.");
|
||||
var viewType = Type.GetType(viewTypeName);
|
||||
if (viewType != null)
|
||||
return Activator.CreateInstance(viewType) as Control;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Task ShowDialog(object data, Window owner = null)
|
||||
{
|
||||
if (owner == null)
|
||||
@@ -135,7 +88,7 @@ namespace SourceGit
|
||||
if (data is Views.ChromelessWindow window)
|
||||
return window.ShowDialog(owner);
|
||||
|
||||
window = CreateViewForViewModel(data) as Views.ChromelessWindow;
|
||||
window = Views.ControlExtensions.CreateFromViewModels(data) as Views.ChromelessWindow;
|
||||
if (window != null)
|
||||
{
|
||||
window.DataContext = data;
|
||||
@@ -149,7 +102,7 @@ namespace SourceGit
|
||||
{
|
||||
if (data is not Views.ChromelessWindow window)
|
||||
{
|
||||
window = CreateViewForViewModel(data) as Views.ChromelessWindow;
|
||||
window = Views.ControlExtensions.CreateFromViewModels(data) as Views.ChromelessWindow;
|
||||
if (window == null)
|
||||
return;
|
||||
|
||||
@@ -221,18 +174,6 @@ namespace SourceGit
|
||||
return Models.ConfirmEmptyCommitResult.Cancel;
|
||||
}
|
||||
|
||||
public static void RaiseException(string context, string message)
|
||||
{
|
||||
if (Current is App { _launcher: not null } app)
|
||||
app._launcher.DispatchNotification(context, message, true);
|
||||
}
|
||||
|
||||
public static void SendNotification(string context, string message)
|
||||
{
|
||||
if (Current is App { _launcher: not null } app)
|
||||
app._launcher.DispatchNotification(context, message, false);
|
||||
}
|
||||
|
||||
public static void SetLocale(string localeKey)
|
||||
{
|
||||
if (Current is not App app ||
|
||||
@@ -342,19 +283,6 @@ namespace SourceGit
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task CopyTextAsync(string data)
|
||||
{
|
||||
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow.Clipboard: { } clipboard })
|
||||
await clipboard.SetTextAsync(data ?? "");
|
||||
}
|
||||
|
||||
public static async Task<string> GetClipboardTextAsync()
|
||||
{
|
||||
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow.Clipboard: { } clipboard })
|
||||
return await clipboard.TryGetTextAsync();
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string Text(string key, params object[] args)
|
||||
{
|
||||
var fmt = Current?.FindResource($"Text.{key}") as string;
|
||||
@@ -367,19 +295,6 @@ namespace SourceGit
|
||||
return string.Format(fmt, args);
|
||||
}
|
||||
|
||||
public static Avalonia.Controls.Shapes.Path CreateMenuIcon(string key)
|
||||
{
|
||||
var icon = new Avalonia.Controls.Shapes.Path();
|
||||
icon.Width = 12;
|
||||
icon.Height = 12;
|
||||
icon.Stretch = Stretch.Uniform;
|
||||
|
||||
if (Current?.FindResource(key) is StreamGeometry geo)
|
||||
icon.Data = geo;
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
public static ViewModels.Launcher GetLauncher()
|
||||
{
|
||||
return Current is App app ? app._launcher : null;
|
||||
@@ -439,32 +354,12 @@ namespace SourceGit
|
||||
if (TryLaunchAsAskpass(desktop))
|
||||
return;
|
||||
|
||||
_ipcChannel = new Models.IpcChannel();
|
||||
if (!_ipcChannel.IsFirstInstance)
|
||||
{
|
||||
var arg = desktop.Args is { Length: > 0 } ? desktop.Args[0].Trim() : string.Empty;
|
||||
if (!string.IsNullOrEmpty(arg))
|
||||
{
|
||||
if (arg.StartsWith('"') && arg.EndsWith('"'))
|
||||
arg = arg.Substring(1, arg.Length - 2).Trim();
|
||||
|
||||
if (arg.Length > 0 && !Path.IsPathFullyQualified(arg))
|
||||
arg = Path.GetFullPath(arg);
|
||||
}
|
||||
|
||||
_ipcChannel.SendToFirstInstance(arg);
|
||||
Environment.Exit(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ipcChannel.MessageReceived += TryOpenRepository;
|
||||
desktop.Exit += (_, _) => _ipcChannel.Dispose();
|
||||
TryLaunchAsNormal(desktop);
|
||||
}
|
||||
TryLaunchAsNormal(desktop);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Launch Ways
|
||||
private static bool TryLaunchAsRebaseTodoEditor(string[] args, out int exitCode)
|
||||
{
|
||||
exitCode = -1;
|
||||
@@ -625,6 +520,24 @@ namespace SourceGit
|
||||
|
||||
private void TryLaunchAsNormal(IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
_ipcChannel = new Models.IpcChannel();
|
||||
if (!_ipcChannel.IsFirstInstance)
|
||||
{
|
||||
var arg = desktop.Args is { Length: > 0 } ? desktop.Args[0].Trim() : string.Empty;
|
||||
if (!string.IsNullOrEmpty(arg))
|
||||
{
|
||||
if (arg.StartsWith('"') && arg.EndsWith('"'))
|
||||
arg = arg.Substring(1, arg.Length - 2).Trim();
|
||||
|
||||
if (arg.Length > 0 && !Path.IsPathFullyQualified(arg))
|
||||
arg = Path.GetFullPath(arg);
|
||||
}
|
||||
|
||||
_ipcChannel.SendToFirstInstance(arg);
|
||||
Environment.Exit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
Native.OS.SetupExternalTools();
|
||||
Models.AvatarManager.Instance.Start();
|
||||
|
||||
@@ -639,40 +552,26 @@ namespace SourceGit
|
||||
desktop.MainWindow = new Views.Launcher() { DataContext = _launcher };
|
||||
desktop.ShutdownMode = ShutdownMode.OnMainWindowClose;
|
||||
|
||||
_ipcChannel.MessageReceived += repo =>
|
||||
{
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
_launcher.TryOpenRepositoryFromPath(repo);
|
||||
if (desktop.MainWindow is Views.Launcher main)
|
||||
main.BringToTop();
|
||||
});
|
||||
};
|
||||
|
||||
desktop.Exit += (_, _) => _ipcChannel.Dispose();
|
||||
|
||||
#if !DISABLE_UPDATE_DETECTION
|
||||
if (pref.ShouldCheck4UpdateOnStartup())
|
||||
Check4Update();
|
||||
#endif
|
||||
}
|
||||
#endregion
|
||||
|
||||
private void TryOpenRepository(string repo)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(repo) && Directory.Exists(repo))
|
||||
{
|
||||
var test = new Commands.QueryRepositoryRootPath(repo).GetResult();
|
||||
if (test.IsSuccess && !string.IsNullOrEmpty(test.StdOut))
|
||||
{
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
var node = ViewModels.Preferences.Instance.FindOrAddNodeByRepositoryPath(test.StdOut.Trim(), null, false);
|
||||
ViewModels.Welcome.Instance.Refresh();
|
||||
_launcher?.OpenRepositoryInTab(node, null);
|
||||
|
||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: Views.Launcher wnd })
|
||||
wnd.BringToTop();
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: Views.Launcher launcher })
|
||||
launcher.BringToTop();
|
||||
});
|
||||
}
|
||||
|
||||
#region Check for Updates
|
||||
private void Check4Update(bool manually = false)
|
||||
{
|
||||
Task.Run(async () =>
|
||||
@@ -728,6 +627,7 @@ namespace SourceGit
|
||||
// Ignore exceptions.
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
private string FixFontFamilyName(string input)
|
||||
{
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace SourceGit.Commands
|
||||
catch (Exception e)
|
||||
{
|
||||
if (RaiseError)
|
||||
App.RaiseException(Context, e.Message);
|
||||
RaiseException(e.Message);
|
||||
|
||||
Log?.AppendLine(string.Empty);
|
||||
return false;
|
||||
@@ -101,7 +101,7 @@ namespace SourceGit.Commands
|
||||
{
|
||||
var errMsg = string.Join("\n", errs).Trim();
|
||||
if (!string.IsNullOrEmpty(errMsg))
|
||||
App.RaiseException(Context, errMsg);
|
||||
RaiseException(errMsg);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -219,6 +219,11 @@ namespace SourceGit.Commands
|
||||
return start;
|
||||
}
|
||||
|
||||
protected void RaiseException(string error)
|
||||
{
|
||||
Models.Notification.Send(Context, error, true);
|
||||
}
|
||||
|
||||
private void HandleOutput(string line, List<string> errs)
|
||||
{
|
||||
if (line == null)
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace SourceGit.Commands
|
||||
var tool = Native.OS.GetDiffMergeTool(true);
|
||||
if (tool == null)
|
||||
{
|
||||
App.RaiseException(Context, "Invalid diff/merge tool in preference setting!");
|
||||
RaiseException("Invalid diff/merge tool in preference setting!");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace SourceGit.Commands
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
App.RaiseException(Context, ex.Message);
|
||||
RaiseException(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace SourceGit.Commands
|
||||
if (config.TryGetValue("merge.tool", out var mergeTool))
|
||||
return CheckCLIBasedTool(mergeTool);
|
||||
|
||||
App.RaiseException(Context, "Missing git configuration: diff.guitool");
|
||||
RaiseException("Missing git configuration: diff.guitool");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace SourceGit.Commands
|
||||
if (tool.StartsWith("vimdiff", StringComparison.Ordinal) ||
|
||||
tool.StartsWith("nvimdiff", StringComparison.Ordinal))
|
||||
{
|
||||
App.RaiseException(Context, $"CLI based diff tool \"{tool}\" is not supported by this app!");
|
||||
RaiseException($"CLI based diff tool \"{tool}\" is not supported by this app!");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace SourceGit.Commands
|
||||
/// <summary>
|
||||
/// Discard all local changes (unstaged & staged)
|
||||
/// </summary>
|
||||
public static async Task AllAsync(string repo, bool includeUntracked, bool includeIgnored, Models.ICommandLog log)
|
||||
public static async Task AllAsync(string repo, bool includeModified, bool includeUntracked, bool includeIgnored, Models.ICommandLog log)
|
||||
{
|
||||
if (includeUntracked)
|
||||
{
|
||||
@@ -33,11 +33,11 @@ namespace SourceGit.Commands
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
App.RaiseException(repo, $"Failed to discard changes. Reason: {e.Message}");
|
||||
Models.Notification.Send(repo, $"Failed to discard changes. Reason: {e.Message}", true);
|
||||
}
|
||||
|
||||
if (includeIgnored)
|
||||
await new Clean(repo, Models.CleanMode.All).Use(log).ExecAsync().ConfigureAwait(false);
|
||||
await new Clean(repo, Models.CleanMode.UntrackedAndIgnoredFiles).Use(log).ExecAsync().ConfigureAwait(false);
|
||||
else
|
||||
await new Clean(repo, Models.CleanMode.OnlyUntrackedFiles).Use(log).ExecAsync().ConfigureAwait(false);
|
||||
}
|
||||
@@ -46,7 +46,8 @@ namespace SourceGit.Commands
|
||||
await new Clean(repo, Models.CleanMode.OnlyIgnoredFiles).Use(log).ExecAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await new Reset(repo, "", "--hard").Use(log).ExecAsync().ConfigureAwait(false);
|
||||
if (includeModified)
|
||||
await new Reset(repo, "", "--hard").Use(log).ExecAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -79,7 +80,7 @@ namespace SourceGit.Commands
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
App.RaiseException(repo, $"Failed to discard changes. Reason: {e.Message}");
|
||||
Models.Notification.Send(repo, $"Failed to discard changes. Reason: {e.Message}", true);
|
||||
}
|
||||
|
||||
if (restores.Count > 0)
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace SourceGit.Commands
|
||||
start.Args = $"flow hotfix start {name}";
|
||||
break;
|
||||
default:
|
||||
App.RaiseException(repo, "Bad git-flow branch type!!!");
|
||||
Models.Notification.Send(repo, "Bad git-flow branch type!!!", true);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace SourceGit.Commands
|
||||
builder.Append("hotfix");
|
||||
break;
|
||||
default:
|
||||
App.RaiseException(repo, "Bad git-flow branch type!!!");
|
||||
Models.Notification.Send(repo, "Bad git-flow branch type!!!", true);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace SourceGit.Commands
|
||||
var tool = Native.OS.GetDiffMergeTool(false);
|
||||
if (tool == null)
|
||||
{
|
||||
App.RaiseException(Context, "Invalid diff/merge tool in preference setting!");
|
||||
RaiseException("Invalid diff/merge tool in preference setting!");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -46,14 +46,14 @@ namespace SourceGit.Commands
|
||||
|
||||
if (string.IsNullOrEmpty(tool))
|
||||
{
|
||||
App.RaiseException(Context, "Missing git configuration: merge.guitool");
|
||||
RaiseException("Missing git configuration: merge.guitool");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tool.StartsWith("vimdiff", StringComparison.Ordinal) ||
|
||||
tool.StartsWith("nvimdiff", StringComparison.Ordinal))
|
||||
{
|
||||
App.RaiseException(Context, $"CLI based merge tool \"{tool}\" is not supported by this app!");
|
||||
RaiseException($"CLI based merge tool \"{tool}\" is not supported by this app!");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ namespace SourceGit.Commands
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
App.RaiseException(Context, $"Failed to query commits. Reason: {e.Message}");
|
||||
RaiseException($"Failed to query commits. Reason: {e.Message}");
|
||||
}
|
||||
|
||||
return commits;
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace SourceGit.Commands
|
||||
var rs = await ReadToEndAsync().ConfigureAwait(false);
|
||||
if (!rs.IsSuccess)
|
||||
{
|
||||
App.RaiseException(Context, $"Failed to query commits for interactive-rebase. Reason: {rs.StdErr}");
|
||||
RaiseException($"Failed to query commits for interactive-rebase. Reason: {rs.StdErr}");
|
||||
return commits;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace SourceGit.Commands
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
App.RaiseException(repo, $"Failed to query file content: {e}");
|
||||
Models.Notification.Send(repo, $"Failed to query file content: {e}", true);
|
||||
}
|
||||
|
||||
stream.Position = 0;
|
||||
@@ -58,7 +58,7 @@ namespace SourceGit.Commands
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
App.RaiseException(repo, $"Failed to query file content: {e}");
|
||||
Models.Notification.Send(repo, $"Failed to query file content: {e}", true);
|
||||
}
|
||||
|
||||
stream.Position = 0;
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace SourceGit.Commands
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
App.RaiseException(repo, "Save change to patch failed: " + e.Message);
|
||||
Models.Notification.Send(repo, "Save change to patch failed: " + e.Message, true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace SourceGit.Commands
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
App.RaiseException(repo, "Save file failed: " + e.Message);
|
||||
Models.Notification.Send(repo, "Save file failed: " + e.Message, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +76,12 @@ namespace SourceGit.Commands
|
||||
return await ExecAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<bool> CheckoutBranchAsync(string name, string branch)
|
||||
{
|
||||
Args = $"stash branch {branch.Quoted()} {name.Quoted()}";
|
||||
return await ExecAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<bool> PopAsync(string name)
|
||||
{
|
||||
Args = $"stash pop -q --index {name.Quoted()}";
|
||||
|
||||
@@ -74,13 +74,13 @@ namespace SourceGit.Commands
|
||||
var rs = proc.ExitCode == 0;
|
||||
|
||||
if (!rs)
|
||||
App.RaiseException(_repo, err);
|
||||
Models.Notification.Send(_repo, err, true);
|
||||
|
||||
return rs;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
App.RaiseException(_repo, "Failed to update index: " + e.Message);
|
||||
Models.Notification.Send(_repo, "Failed to update index: " + e.Message, true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
{
|
||||
OnlyUntrackedFiles = 0,
|
||||
OnlyIgnoredFiles,
|
||||
All,
|
||||
UntrackedAndIgnoredFiles,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,23 @@
|
||||
namespace SourceGit.Models
|
||||
using System;
|
||||
|
||||
namespace SourceGit.Models
|
||||
{
|
||||
public class Notification
|
||||
{
|
||||
public bool IsError { get; set; } = false;
|
||||
public string Message { get; set; } = string.Empty;
|
||||
public static event Action<Notification> Raised;
|
||||
|
||||
public string Group { get; set; }
|
||||
public string Message { get; set; }
|
||||
public bool IsError { get; set; }
|
||||
|
||||
public static void Send(string group, string message, bool isError = false)
|
||||
{
|
||||
Raised?.Invoke(new Notification
|
||||
{
|
||||
Group = group,
|
||||
Message = message,
|
||||
IsError = isError
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,12 @@ namespace SourceGit.Models
|
||||
|
||||
if (URL.StartsWith("http://", StringComparison.Ordinal) || URL.StartsWith("https://", StringComparison.Ordinal))
|
||||
{
|
||||
url = URL.EndsWith(".git", StringComparison.Ordinal) ? URL.Substring(0, URL.Length - 4) : URL;
|
||||
var trimmed = URL.EndsWith(".git", StringComparison.Ordinal) ? URL.Substring(0, URL.Length - 4) : URL;
|
||||
var uri = new Uri(trimmed);
|
||||
if (uri.Port != 80 && uri.Port != 443)
|
||||
url = $"{uri.Scheme}://{uri.Host}:{uri.Port}{uri.AbsolutePath}";
|
||||
else
|
||||
url = $"{uri.Scheme}://{uri.Host}{uri.AbsolutePath}";
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,18 +30,6 @@ namespace SourceGit.Models
|
||||
set;
|
||||
} = string.Empty;
|
||||
|
||||
public bool EnableAutoFetch
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} = false;
|
||||
|
||||
public int AutoFetchInterval
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} = 10;
|
||||
|
||||
public bool AskBeforeAutoUpdatingSubmodules
|
||||
{
|
||||
get;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
namespace SourceGit.Models
|
||||
{
|
||||
@@ -26,6 +27,13 @@ namespace SourceGit.Models
|
||||
AddedLeft,
|
||||
}
|
||||
|
||||
private enum CharCategory : byte
|
||||
{
|
||||
Other, // default: whitespace, control, punctuation, symbols, etc.
|
||||
Letter, // Ll/Lu/Lt/Lm + digit: ASCII and euro letters (latin, greek, cyrillic, etc.)
|
||||
OtherLetter, // Lo: CJK, hiragana, katakana, hangul, Thai, Arabic, etc.
|
||||
}
|
||||
|
||||
private class EditResult
|
||||
{
|
||||
public Edit State;
|
||||
@@ -100,22 +108,25 @@ namespace SourceGit.Models
|
||||
var start = 0;
|
||||
var size = text.Length;
|
||||
var chunks = new List<Chunk>();
|
||||
var delims = new HashSet<char>(" \t+-*/=!,:;.'\"/?|&#@%`<>()[]{}\\".ToCharArray());
|
||||
if (size == 0)
|
||||
return chunks;
|
||||
|
||||
for (int i = 0; i < size; i++)
|
||||
var prev = GetCategory(text[0]);
|
||||
|
||||
for (var i = 1; i < size; i++)
|
||||
{
|
||||
var ch = text[i];
|
||||
if (delims.Contains(ch))
|
||||
var category = GetCategory(ch);
|
||||
if (prev != category || category == CharCategory.Other)
|
||||
{
|
||||
if (start != i)
|
||||
AddChunk(chunks, hashes, text.Substring(start, i - start), start);
|
||||
AddChunk(chunks, hashes, text.Substring(i, 1), i);
|
||||
start = i + 1;
|
||||
AddChunk(chunks, hashes, text[start..i], start);
|
||||
start = i;
|
||||
}
|
||||
prev = category;
|
||||
}
|
||||
|
||||
if (start < size)
|
||||
AddChunk(chunks, hashes, text.Substring(start), start);
|
||||
AddChunk(chunks, hashes, text[start..], start);
|
||||
return chunks;
|
||||
}
|
||||
|
||||
@@ -302,5 +313,33 @@ namespace SourceGit.Models
|
||||
}
|
||||
chunks.Add(new Chunk(hash, start, data.Length));
|
||||
}
|
||||
|
||||
private static CharCategory[] BuildCategoryCache()
|
||||
{
|
||||
// Pre-compute category for all char values.
|
||||
// All entries default to Other (0).
|
||||
var cache = new CharCategory[65536];
|
||||
for (int i = 0; i < 65536; i++)
|
||||
{
|
||||
var ch = (char)i;
|
||||
// Unicode Lo: CJK, hiragana, katakana, hangul, Thai, Arabic, Hebrew, etc.
|
||||
// → group consecutive chars into one chunk (no space delimiter in these languages)
|
||||
if (char.GetUnicodeCategory(ch) == UnicodeCategory.OtherLetter)
|
||||
cache[i] = CharCategory.OtherLetter;
|
||||
|
||||
// Unicode Ll/Lu/Lt/Lm + digit: latin, greek, cyrillic and their diacritic variants
|
||||
// → group consecutive chars into one chunk (words in space-delimited languages)
|
||||
else if (char.IsLetterOrDigit(ch))
|
||||
cache[i] = CharCategory.Letter;
|
||||
|
||||
// everything else (whitespace, control, punctuation, symbols) → Other (default)
|
||||
}
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
private static CharCategory GetCategory(char ch) => s_charCategoryCache[ch];
|
||||
|
||||
private static readonly CharCategory[] s_charCategoryCache = BuildCategoryCache();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ namespace SourceGit.Native
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
App.RaiseException(workdir, $"Failed to start '{OS.ShellOrTerminal}'. Reason: {e.Message}");
|
||||
Models.Notification.Send(workdir, $"Failed to start '{OS.ShellOrTerminal}'. Reason: {e.Message}", true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ namespace SourceGit.Native
|
||||
proc.WaitForExit();
|
||||
|
||||
if (proc.ExitCode != 0)
|
||||
App.RaiseException("", $"Failed to open: {file}");
|
||||
Models.Notification.Send("", $"Failed to open: {file}", true);
|
||||
|
||||
proc.Close();
|
||||
}
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
using System.Threading;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
|
||||
@@ -153,6 +154,35 @@ namespace SourceGit.Native
|
||||
_backend.SetupWindow(window);
|
||||
}
|
||||
|
||||
public static void LogException(Exception ex)
|
||||
{
|
||||
if (ex == null)
|
||||
return;
|
||||
|
||||
var crashDir = Path.Combine(DataDir, "crashes");
|
||||
if (!Directory.Exists(crashDir))
|
||||
Directory.CreateDirectory(crashDir);
|
||||
|
||||
var time = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
|
||||
var file = Path.Combine(crashDir, $"{time}.log");
|
||||
using var writer = new StreamWriter(file);
|
||||
writer.WriteLine($"Crash::: {ex.GetType().FullName}: {ex.Message}");
|
||||
writer.WriteLine();
|
||||
writer.WriteLine("----------------------------");
|
||||
writer.WriteLine($"Version: {Assembly.GetExecutingAssembly().GetName().Version}");
|
||||
writer.WriteLine($"OS: {Environment.OSVersion}");
|
||||
writer.WriteLine($"Framework: {AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName}");
|
||||
writer.WriteLine($"Source: {ex.Source}");
|
||||
writer.WriteLine($"Thread Name: {Thread.CurrentThread.Name ?? "Unnamed"}");
|
||||
writer.WriteLine($"App Start Time: {Process.GetCurrentProcess().StartTime}");
|
||||
writer.WriteLine($"Exception Time: {DateTime.Now}");
|
||||
writer.WriteLine($"Memory Usage: {Process.GetCurrentProcess().PrivateMemorySize64 / 1024 / 1024} MB");
|
||||
writer.WriteLine("----------------------------");
|
||||
writer.WriteLine();
|
||||
writer.WriteLine(ex);
|
||||
writer.Flush();
|
||||
}
|
||||
|
||||
public static string FindGitExecutable()
|
||||
{
|
||||
return _backend.FindGitExecutable();
|
||||
@@ -217,7 +247,7 @@ namespace SourceGit.Native
|
||||
public static void OpenTerminal(string workdir)
|
||||
{
|
||||
if (string.IsNullOrEmpty(ShellOrTerminal))
|
||||
App.RaiseException(workdir, "Terminal is not specified! Please confirm that the correct shell/terminal has been configured.");
|
||||
Models.Notification.Send(workdir, "Terminal is not specified! Please confirm that the correct shell/terminal has been configured.", true);
|
||||
else
|
||||
_backend.OpenTerminal(workdir, ShellOrTerminalArgs);
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ namespace SourceGit.Native
|
||||
|
||||
if (!File.Exists(terminal))
|
||||
{
|
||||
App.RaiseException(workdir, "Terminal is not specified! Please confirm that the correct shell/terminal has been configured.");
|
||||
Models.Notification.Send(workdir, "Terminal is not specified! Please confirm that the correct shell/terminal has been configured.", true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
<x:String x:Key="Text.AddWorktree.WhatToCheckout.CreateNew" xml:space="preserve">Neuen Branch erstellen</x:String>
|
||||
<x:String x:Key="Text.AddWorktree.WhatToCheckout.Existing" xml:space="preserve">Existierender Branch</x:String>
|
||||
<x:String x:Key="Text.AIAssistant" xml:space="preserve">AI-Assistent</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Model" xml:space="preserve">Modell</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Regen" xml:space="preserve">NEU GENERIEREN</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">Verwende AI, um Commit-Nachrichten zu generieren</x:String>
|
||||
<x:String x:Key="Text.App.Hide" xml:space="preserve">SourceGit minimieren</x:String>
|
||||
@@ -580,11 +581,11 @@ $1, $2, … Werte der Eingabe-Steuerelemente</x:String>
|
||||
<x:String x:Key="Text.OpenInExternalMergeTool" xml:space="preserve">Öffnen in externem Merge-Tool</x:String>
|
||||
<x:String x:Key="Text.Optional" xml:space="preserve">Optional.</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.New" xml:space="preserve">Neue Registerkarte erstellen</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Bookmark" xml:space="preserve">Lesezeichen</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Close" xml:space="preserve">Registerkarte schließen</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CloseOther" xml:space="preserve">Andere Registerkarten schließen</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CloseRight" xml:space="preserve">Registerkarten zur Rechten schließen</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CopyPath" xml:space="preserve">Kopiere Repository-Pfad</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Edit" xml:space="preserve">Bearbeiten</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.MoveToWorkspace" xml:space="preserve">In Arbeitsumgebung verschieben</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Refresh" xml:space="preserve">Aktualisieren</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Welcome.Title" xml:space="preserve">Repositorys</x:String>
|
||||
@@ -602,7 +603,6 @@ $1, $2, … Werte der Eingabe-Steuerelemente</x:String>
|
||||
<x:String x:Key="Text.Preferences" xml:space="preserve">Einstellungen</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI" xml:space="preserve">AI</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.ApiKey" xml:space="preserve">API-Schlüssel</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Model" xml:space="preserve">Modell</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Name" xml:space="preserve">Name</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.ReadApiKeyFromEnv" xml:space="preserve">Der eingegebene Wert ist der Name der Umgebungsvariable, aus der der API-Schlüssel gelesen wird</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Server" xml:space="preserve">Server</x:String>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<x:String x:Key="Text.AddWorktree.WhatToCheckout.CreateNew" xml:space="preserve">Create New Branch</x:String>
|
||||
<x:String x:Key="Text.AddWorktree.WhatToCheckout.Existing" xml:space="preserve">Existing Branch</x:String>
|
||||
<x:String x:Key="Text.AIAssistant" xml:space="preserve">AI Assistant</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Model" xml:space="preserve">MODEL</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Regen" xml:space="preserve">RE-GENERATE</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">Use AI to generate commit message</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Use" xml:space="preserve">Use</x:String>
|
||||
@@ -115,6 +116,9 @@
|
||||
<x:String x:Key="Text.Checkout.WarnUpdatingSubmodules" xml:space="preserve">The following submodules need to be updated:{0}Do you want to update them?</x:String>
|
||||
<x:String x:Key="Text.Checkout.WithFastForward" xml:space="preserve">Checkout & Fast-Forward</x:String>
|
||||
<x:String x:Key="Text.Checkout.WithFastForward.Upstream" xml:space="preserve">Fast-Forward to:</x:String>
|
||||
<x:String x:Key="Text.CheckoutBranchFromStash" xml:space="preserve">Checkout Branch From Stash</x:String>
|
||||
<x:String x:Key="Text.CheckoutBranchFromStash.Branch" xml:space="preserve">New Branch:</x:String>
|
||||
<x:String x:Key="Text.CheckoutBranchFromStash.Stash" xml:space="preserve">Stash:</x:String>
|
||||
<x:String x:Key="Text.CherryPick" xml:space="preserve">Cherry Pick</x:String>
|
||||
<x:String x:Key="Text.CherryPick.AppendSourceToMessage" xml:space="preserve">Append source to commit message</x:String>
|
||||
<x:String x:Key="Text.CherryPick.Commit" xml:space="preserve">Commit(s):</x:String>
|
||||
@@ -382,6 +386,7 @@
|
||||
<x:String x:Key="Text.Discard.All" xml:space="preserve">All local changes in working copy.</x:String>
|
||||
<x:String x:Key="Text.Discard.Changes" xml:space="preserve">Changes:</x:String>
|
||||
<x:String x:Key="Text.Discard.IncludeIgnored" xml:space="preserve">Include ignored files</x:String>
|
||||
<x:String x:Key="Text.Discard.IncludeModified" xml:space="preserve">Include modified/deleted files</x:String>
|
||||
<x:String x:Key="Text.Discard.IncludeUntracked" xml:space="preserve">Include untracked files</x:String>
|
||||
<x:String x:Key="Text.Discard.Total" xml:space="preserve">{0} changes will be discarded</x:String>
|
||||
<x:String x:Key="Text.Discard.Warning" xml:space="preserve">You can't undo this action!!!</x:String>
|
||||
@@ -589,11 +594,11 @@
|
||||
<x:String x:Key="Text.OpenInExternalMergeTool" xml:space="preserve">Open in External Merge Tool</x:String>
|
||||
<x:String x:Key="Text.Optional" xml:space="preserve">Optional.</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.New" xml:space="preserve">Create New Tab</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Bookmark" xml:space="preserve">Bookmark</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Close" xml:space="preserve">Close Tab</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CloseOther" xml:space="preserve">Close Other Tabs</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CloseRight" xml:space="preserve">Close Tabs to the Right</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CopyPath" xml:space="preserve">Copy Repository Path</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Edit" xml:space="preserve">Edit</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.MoveToWorkspace" xml:space="preserve">Move to Workspace</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Refresh" xml:space="preserve">Refresh</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Welcome.Title" xml:space="preserve">Repositories</x:String>
|
||||
@@ -612,7 +617,6 @@
|
||||
<x:String x:Key="Text.Preferences.AI" xml:space="preserve">AI</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.AdditionalPrompt" xml:space="preserve">Additional Prompt (Use `-` to list your requirements)</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.ApiKey" xml:space="preserve">API Key</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Model" xml:space="preserve">Model</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Name" xml:space="preserve">Name</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.ReadApiKeyFromEnv" xml:space="preserve">Entered value is the name to load API key from ENV</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Server" xml:space="preserve">Server</x:String>
|
||||
@@ -857,6 +861,7 @@
|
||||
<x:String x:Key="Text.Stash.Title" xml:space="preserve">Stash Local Changes</x:String>
|
||||
<x:String x:Key="Text.StashCM.Apply" xml:space="preserve">Apply</x:String>
|
||||
<x:String x:Key="Text.StashCM.ApplyFileChanges" xml:space="preserve">Apply Changes</x:String>
|
||||
<x:String x:Key="Text.StashCM.Branch" xml:space="preserve">Checkout New Branch</x:String>
|
||||
<x:String x:Key="Text.StashCM.CopyMessage" xml:space="preserve">Copy Message</x:String>
|
||||
<x:String x:Key="Text.StashCM.Drop" xml:space="preserve">Drop</x:String>
|
||||
<x:String x:Key="Text.StashCM.SaveAsPatch" xml:space="preserve">Save as Patch...</x:String>
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
<x:String x:Key="Text.AddWorktree.WhatToCheckout.CreateNew" xml:space="preserve">Crear Nueva Rama</x:String>
|
||||
<x:String x:Key="Text.AddWorktree.WhatToCheckout.Existing" xml:space="preserve">Rama Existente</x:String>
|
||||
<x:String x:Key="Text.AIAssistant" xml:space="preserve">Asistente OpenAI</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Model" xml:space="preserve">Modelo</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Regen" xml:space="preserve">RE-GENERAR</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">Usar OpenAI para generar mensaje de commit</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Use" xml:space="preserve">Usar</x:String>
|
||||
@@ -119,6 +120,9 @@
|
||||
<x:String x:Key="Text.Checkout.WarnUpdatingSubmodules" xml:space="preserve">Los siguientes submódulos necesitan ser actualizados:{0} ¿Quieres actualizarlos?</x:String>
|
||||
<x:String x:Key="Text.Checkout.WithFastForward" xml:space="preserve">Checkout & Fast-Forward</x:String>
|
||||
<x:String x:Key="Text.Checkout.WithFastForward.Upstream" xml:space="preserve">Fast-Forward a:</x:String>
|
||||
<x:String x:Key="Text.CheckoutBranchFromStash" xml:space="preserve">Checkout a la Rama desde el Stash</x:String>
|
||||
<x:String x:Key="Text.CheckoutBranchFromStash.Branch" xml:space="preserve">Nueva Rama:</x:String>
|
||||
<x:String x:Key="Text.CheckoutBranchFromStash.Stash" xml:space="preserve">Stash:</x:String>
|
||||
<x:String x:Key="Text.CherryPick" xml:space="preserve">Cherry Pick</x:String>
|
||||
<x:String x:Key="Text.CherryPick.AppendSourceToMessage" xml:space="preserve">Añadir fuente al mensaje de commit</x:String>
|
||||
<x:String x:Key="Text.CherryPick.Commit" xml:space="preserve">Commit(s):</x:String>
|
||||
@@ -386,6 +390,7 @@
|
||||
<x:String x:Key="Text.Discard.All" xml:space="preserve">Todos los cambios locales en la copia de trabajo.</x:String>
|
||||
<x:String x:Key="Text.Discard.Changes" xml:space="preserve">Cambios:</x:String>
|
||||
<x:String x:Key="Text.Discard.IncludeIgnored" xml:space="preserve">Incluir archivos ignorados</x:String>
|
||||
<x:String x:Key="Text.Discard.IncludeModified" xml:space="preserve">Incluir archivos modificados/eliminados</x:String>
|
||||
<x:String x:Key="Text.Discard.IncludeUntracked" xml:space="preserve">Incluir archivos no rastreados</x:String>
|
||||
<x:String x:Key="Text.Discard.Total" xml:space="preserve">Total {0} cambios serán descartados</x:String>
|
||||
<x:String x:Key="Text.Discard.Warning" xml:space="preserve">¡No puedes deshacer esta acción!</x:String>
|
||||
@@ -593,11 +598,11 @@
|
||||
<x:String x:Key="Text.OpenInExternalMergeTool" xml:space="preserve">Abrir en Herramienta de Merge</x:String>
|
||||
<x:String x:Key="Text.Optional" xml:space="preserve">Opcional.</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.New" xml:space="preserve">Crear Nueva Página</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Bookmark" xml:space="preserve">Marcador</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Close" xml:space="preserve">Cerrar Pestaña</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CloseOther" xml:space="preserve">Cerrar Otras Pestañas</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CloseRight" xml:space="preserve">Cerrar Pestañas a la Derecha</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CopyPath" xml:space="preserve">Copiar Ruta del Repositorio</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Edit" xml:space="preserve">Editar</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.MoveToWorkspace" xml:space="preserve">Mover al Espacio de trabajo</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Refresh" xml:space="preserve">Actualizar</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Welcome.Title" xml:space="preserve">Repositorios</x:String>
|
||||
@@ -616,7 +621,6 @@
|
||||
<x:String x:Key="Text.Preferences.AI" xml:space="preserve">OPEN AI</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.AdditionalPrompt" xml:space="preserve">Prompt adicional (Usa `-` para listar tus requerimientos)</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.ApiKey" xml:space="preserve">Clave API</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Model" xml:space="preserve">Modelo</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Name" xml:space="preserve">Nombre</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.ReadApiKeyFromEnv" xml:space="preserve">El valor ingresado es el nombre de la clave API a cargar desde ENV</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Server" xml:space="preserve">Servidor</x:String>
|
||||
@@ -861,6 +865,7 @@
|
||||
<x:String x:Key="Text.Stash.Title" xml:space="preserve">Stash Cambios Locales</x:String>
|
||||
<x:String x:Key="Text.StashCM.Apply" xml:space="preserve">Aplicar</x:String>
|
||||
<x:String x:Key="Text.StashCM.ApplyFileChanges" xml:space="preserve">Aplicar Cambios</x:String>
|
||||
<x:String x:Key="Text.StashCM.Branch" xml:space="preserve">Checkout a Nueva Rama</x:String>
|
||||
<x:String x:Key="Text.StashCM.CopyMessage" xml:space="preserve">Copiar Mensaje</x:String>
|
||||
<x:String x:Key="Text.StashCM.Drop" xml:space="preserve">Eliminar</x:String>
|
||||
<x:String x:Key="Text.StashCM.SaveAsPatch" xml:space="preserve">Guardar como Parche...</x:String>
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
<x:String x:Key="Text.AddWorktree.WhatToCheckout.CreateNew" xml:space="preserve">Créer une nouvelle branche</x:String>
|
||||
<x:String x:Key="Text.AddWorktree.WhatToCheckout.Existing" xml:space="preserve">Branche existante</x:String>
|
||||
<x:String x:Key="Text.AIAssistant" xml:space="preserve">Assistant IA</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Model" xml:space="preserve">Modèle</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Regen" xml:space="preserve">RE-GÉNÉRER</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">Utiliser l'IA pour générer un message de commit</x:String>
|
||||
<x:String x:Key="Text.App.Hide" xml:space="preserve">Masquer SourceGit</x:String>
|
||||
@@ -541,11 +542,11 @@
|
||||
<x:String x:Key="Text.OpenInExternalMergeTool" xml:space="preserve">Ouvrir dans l'outil de fusion</x:String>
|
||||
<x:String x:Key="Text.Optional" xml:space="preserve">Optionnel.</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.New" xml:space="preserve">Créer un nouvel onglet</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Bookmark" xml:space="preserve">Bookmark</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Close" xml:space="preserve">Fermer l'onglet</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CloseOther" xml:space="preserve">Fermer les autres onglets</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CloseRight" xml:space="preserve">Fermer les onglets à droite</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CopyPath" xml:space="preserve">Copier le chemin vers le dépôt</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Edit" xml:space="preserve">Éditer</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Welcome.Title" xml:space="preserve">Dépôts</x:String>
|
||||
<x:String x:Key="Text.Paste" xml:space="preserve">Coller</x:String>
|
||||
<x:String x:Key="Text.Period.DaysAgo" xml:space="preserve">il y a {0} jours</x:String>
|
||||
@@ -561,7 +562,6 @@
|
||||
<x:String x:Key="Text.Preferences" xml:space="preserve">Préférences</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI" xml:space="preserve">IA</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.ApiKey" xml:space="preserve">Clé d'API</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Model" xml:space="preserve">Modèle</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Name" xml:space="preserve">Nom</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.ReadApiKeyFromEnv" xml:space="preserve">La valeur saisie est le nom pour charger la clé API depuis l'ENV</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Server" xml:space="preserve">Serveur</x:String>
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
<x:String x:Key="Text.AddWorktree.WhatToCheckout.CreateNew" xml:space="preserve">Buat Branch Baru</x:String>
|
||||
<x:String x:Key="Text.AddWorktree.WhatToCheckout.Existing" xml:space="preserve">Branch Yang Ada</x:String>
|
||||
<x:String x:Key="Text.AIAssistant" xml:space="preserve">Asisten AI</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Model" xml:space="preserve">Model</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Regen" xml:space="preserve">BUAT ULANG</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">Gunakan AI untuk membuat pesan commit</x:String>
|
||||
<x:String x:Key="Text.App.Hide" xml:space="preserve">Sembunyikan SourceGit</x:String>
|
||||
@@ -515,11 +516,11 @@
|
||||
<x:String x:Key="Text.OpenInExternalMergeTool" xml:space="preserve">Buka di Merge Tool</x:String>
|
||||
<x:String x:Key="Text.Optional" xml:space="preserve">Opsional.</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.New" xml:space="preserve">Buat Tab Baru</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Bookmark" xml:space="preserve">Bookmark</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Close" xml:space="preserve">Tutup Tab</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CloseOther" xml:space="preserve">Tutup Tab Lain</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CloseRight" xml:space="preserve">Tutup Tab di Kanan</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CopyPath" xml:space="preserve">Salin Jalur Repositori</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Edit" xml:space="preserve">Sunting</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Welcome.Title" xml:space="preserve">Repositori</x:String>
|
||||
<x:String x:Key="Text.Paste" xml:space="preserve">Tempel</x:String>
|
||||
<x:String x:Key="Text.Period.DaysAgo" xml:space="preserve">{0} hari lalu</x:String>
|
||||
@@ -535,7 +536,6 @@
|
||||
<x:String x:Key="Text.Preferences" xml:space="preserve">Preferensi</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI" xml:space="preserve">AI</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.ApiKey" xml:space="preserve">API Key</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Model" xml:space="preserve">Model</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Name" xml:space="preserve">Nama</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.ReadApiKeyFromEnv" xml:space="preserve">Nilai yang dimasukkan adalah nama untuk memuat API key dari ENV</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Server" xml:space="preserve">Server</x:String>
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
<x:String x:Key="Text.AddWorktree.WhatToCheckout.CreateNew" xml:space="preserve">Crea nuovo branch</x:String>
|
||||
<x:String x:Key="Text.AddWorktree.WhatToCheckout.Existing" xml:space="preserve">Branch esistente</x:String>
|
||||
<x:String x:Key="Text.AIAssistant" xml:space="preserve">Assistente AI</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Model" xml:space="preserve">Modello</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Regen" xml:space="preserve">RIGENERA</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">Usa AI per generare il messaggio di commit</x:String>
|
||||
<x:String x:Key="Text.App.Hide" xml:space="preserve">Nascondi SourceGit</x:String>
|
||||
@@ -576,11 +577,11 @@ ${pure_files:N} Come ${files:N}, ma senza cartelle</x:String>
|
||||
<x:String x:Key="Text.OpenInExternalMergeTool" xml:space="preserve">Apri nello Strumento di Merge</x:String>
|
||||
<x:String x:Key="Text.Optional" xml:space="preserve">Opzionale.</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.New" xml:space="preserve">Crea Nuova Pagina</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Bookmark" xml:space="preserve">Segnalibro</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Close" xml:space="preserve">Chiudi Tab</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CloseOther" xml:space="preserve">Chiudi Altri Tab</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CloseRight" xml:space="preserve">Chiudi i Tab a Destra</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CopyPath" xml:space="preserve">Copia Percorso Repository</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Edit" xml:space="preserve">Modifica</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.MoveToWorkspace" xml:space="preserve">Sposta nel Workspace</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Refresh" xml:space="preserve">Aggiorna</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Welcome.Title" xml:space="preserve">Repository</x:String>
|
||||
@@ -598,7 +599,6 @@ ${pure_files:N} Come ${files:N}, ma senza cartelle</x:String>
|
||||
<x:String x:Key="Text.Preferences" xml:space="preserve">Preferenze</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI" xml:space="preserve">AI</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.ApiKey" xml:space="preserve">Chiave API</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Model" xml:space="preserve">Modello</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Name" xml:space="preserve">Nome</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.ReadApiKeyFromEnv" xml:space="preserve">Il valore inserito è il nome per caricare la chiave API da ENV</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Server" xml:space="preserve">Server</x:String>
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
<x:String x:Key="Text.AddWorktree.WhatToCheckout.CreateNew" xml:space="preserve">新しいブランチを作成</x:String>
|
||||
<x:String x:Key="Text.AddWorktree.WhatToCheckout.Existing" xml:space="preserve">既存のブランチ</x:String>
|
||||
<x:String x:Key="Text.AIAssistant" xml:space="preserve">AI アシスタント</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Model" xml:space="preserve">モデル</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Regen" xml:space="preserve">再生成</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">AI を使用してコミットメッセージを生成</x:String>
|
||||
<x:String x:Key="Text.App.Hide" xml:space="preserve">SourceGit を隠す</x:String>
|
||||
@@ -582,11 +583,11 @@
|
||||
<x:String x:Key="Text.OpenInExternalMergeTool" xml:space="preserve">外部のマージツールで開く</x:String>
|
||||
<x:String x:Key="Text.Optional" xml:space="preserve">省略可能</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.New" xml:space="preserve">新しいタブを作成</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Bookmark" xml:space="preserve">ブックマーク</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Close" xml:space="preserve">タブを閉じる</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CloseOther" xml:space="preserve">他のタブを閉じる</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CloseRight" xml:space="preserve">右側のタブを閉じる</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CopyPath" xml:space="preserve">リポジトリへのパスをコピー</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Edit" xml:space="preserve">編集</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.MoveToWorkspace" xml:space="preserve">ワークスペースに移動</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Refresh" xml:space="preserve">再読み込み</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Welcome.Title" xml:space="preserve">リポジトリ</x:String>
|
||||
@@ -604,7 +605,6 @@
|
||||
<x:String x:Key="Text.Preferences" xml:space="preserve">設定</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI" xml:space="preserve">AI</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.ApiKey" xml:space="preserve">API キー</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Model" xml:space="preserve">モデル</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Name" xml:space="preserve">名前</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.ReadApiKeyFromEnv" xml:space="preserve">この値を環境変数の名前とし、そこから API キーを読み込む</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Server" xml:space="preserve">サーバー</x:String>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<x:String x:Key="Text.AddWorktree.WhatToCheckout.CreateNew" xml:space="preserve">새 브랜치 생성</x:String>
|
||||
<x:String x:Key="Text.AddWorktree.WhatToCheckout.Existing" xml:space="preserve">기존 브랜치</x:String>
|
||||
<x:String x:Key="Text.AIAssistant" xml:space="preserve">AI 어시스턴트</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Model" xml:space="preserve">모델</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Regen" xml:space="preserve">재생성</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">AI를 사용하여 커밋 메시지 생성</x:String>
|
||||
<x:String x:Key="Text.App.Hide" xml:space="preserve">SourceGit 숨기기</x:String>
|
||||
@@ -517,11 +518,11 @@
|
||||
<x:String x:Key="Text.OpenInExternalMergeTool" xml:space="preserve">병합 도구에서 열기</x:String>
|
||||
<x:String x:Key="Text.Optional" xml:space="preserve">선택 사항.</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.New" xml:space="preserve">새 탭 만들기</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Bookmark" xml:space="preserve">북마크</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Close" xml:space="preserve">탭 닫기</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CloseOther" xml:space="preserve">다른 탭 닫기</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CloseRight" xml:space="preserve">오른쪽 탭 닫기</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CopyPath" xml:space="preserve">저장소 경로 복사</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Edit" xml:space="preserve">편집</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Welcome.Title" xml:space="preserve">저장소</x:String>
|
||||
<x:String x:Key="Text.Paste" xml:space="preserve">붙여넣기</x:String>
|
||||
<x:String x:Key="Text.Period.DaysAgo" xml:space="preserve">{0}일 전</x:String>
|
||||
@@ -537,7 +538,6 @@
|
||||
<x:String x:Key="Text.Preferences" xml:space="preserve">환경설정</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI" xml:space="preserve">AI</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.ApiKey" xml:space="preserve">API 키</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Model" xml:space="preserve">모델</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Name" xml:space="preserve">이름</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.ReadApiKeyFromEnv" xml:space="preserve">입력된 값은 환경변수(ENV)에서 API 키를 불러올 이름입니다</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Server" xml:space="preserve">서버</x:String>
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
<x:String x:Key="Text.AddWorktree.WhatToCheckout.CreateNew" xml:space="preserve">Criar Novo Branch</x:String>
|
||||
<x:String x:Key="Text.AddWorktree.WhatToCheckout.Existing" xml:space="preserve">Branch Existente</x:String>
|
||||
<x:String x:Key="Text.AIAssistant" xml:space="preserve">Assietente IA</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Model" xml:space="preserve">Modelo</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Regen" xml:space="preserve">Gerar novamente</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">Utilizar IA para gerar mensagem de commit</x:String>
|
||||
<x:String x:Key="Text.App.Hide" xml:space="preserve">Esconder SourceGit</x:String>
|
||||
@@ -398,11 +399,11 @@
|
||||
<x:String x:Key="Text.OpenInExternalMergeTool" xml:space="preserve">Abrir na Ferramenta de Mesclagem</x:String>
|
||||
<x:String x:Key="Text.Optional" xml:space="preserve">Opcional.</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.New" xml:space="preserve">Criar Nova Página</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Bookmark" xml:space="preserve">Adicionar aos Favoritos</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Close" xml:space="preserve">Fechar Aba</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CloseOther" xml:space="preserve">Fechar Outras Abas</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CloseRight" xml:space="preserve">Fechar Abas à Direita</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CopyPath" xml:space="preserve">Copiar Caminho do Repositório</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Edit" xml:space="preserve">Editar</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Welcome.Title" xml:space="preserve">Repositórios</x:String>
|
||||
<x:String x:Key="Text.Paste" xml:space="preserve">Colar</x:String>
|
||||
<x:String x:Key="Text.Period.DaysAgo" xml:space="preserve">{0} dias atrás</x:String>
|
||||
@@ -418,7 +419,6 @@
|
||||
<x:String x:Key="Text.Preferences" xml:space="preserve">Preferências</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI" xml:space="preserve">INTELIGÊNCIA ARTIFICIAL</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.ApiKey" xml:space="preserve">Chave da API</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Model" xml:space="preserve">Modelo</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Name" xml:space="preserve">Nome</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Server" xml:space="preserve">Servidor</x:String>
|
||||
<x:String x:Key="Text.Preferences.Appearance" xml:space="preserve">APARÊNCIA</x:String>
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
<x:String x:Key="Text.AddWorktree.WhatToCheckout.CreateNew" xml:space="preserve">Создать новую ветку</x:String>
|
||||
<x:String x:Key="Text.AddWorktree.WhatToCheckout.Existing" xml:space="preserve">Ветку из списка</x:String>
|
||||
<x:String x:Key="Text.AIAssistant" xml:space="preserve">Помощник OpenAI</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Model" xml:space="preserve">Модель</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Regen" xml:space="preserve">ПЕРЕСОЗДАТЬ</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">Использовать OpenAI для создания сообщения о ревизии</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Use" xml:space="preserve">Использовать</x:String>
|
||||
@@ -119,6 +120,9 @@
|
||||
<x:String x:Key="Text.Checkout.WarnUpdatingSubmodules" xml:space="preserve">Подмодулям требуется обновление:{0}Обновить их?</x:String>
|
||||
<x:String x:Key="Text.Checkout.WithFastForward" xml:space="preserve">Переключиться и перемотать</x:String>
|
||||
<x:String x:Key="Text.Checkout.WithFastForward.Upstream" xml:space="preserve">Перемотать к:</x:String>
|
||||
<x:String x:Key="Text.CheckoutBranchFromStash" xml:space="preserve">Переключить ветку из отложенного</x:String>
|
||||
<x:String x:Key="Text.CheckoutBranchFromStash.Branch" xml:space="preserve">Новая ветка:</x:String>
|
||||
<x:String x:Key="Text.CheckoutBranchFromStash.Stash" xml:space="preserve">Отложенный:</x:String>
|
||||
<x:String x:Key="Text.CherryPick" xml:space="preserve"> Частичный выбор</x:String>
|
||||
<x:String x:Key="Text.CherryPick.AppendSourceToMessage" xml:space="preserve">Добавить источник для ревизии сообщения</x:String>
|
||||
<x:String x:Key="Text.CherryPick.Commit" xml:space="preserve">Ревизия(и):</x:String>
|
||||
@@ -386,6 +390,7 @@
|
||||
<x:String x:Key="Text.Discard.All" xml:space="preserve">Все локальные изменения в рабочей копии.</x:String>
|
||||
<x:String x:Key="Text.Discard.Changes" xml:space="preserve">Изменения:</x:String>
|
||||
<x:String x:Key="Text.Discard.IncludeIgnored" xml:space="preserve">Включить игнорируемые файлы</x:String>
|
||||
<x:String x:Key="Text.Discard.IncludeModified" xml:space="preserve">Включить изменённые/удалённые файлы</x:String>
|
||||
<x:String x:Key="Text.Discard.IncludeUntracked" xml:space="preserve">Включить неотслеживаемые файлы</x:String>
|
||||
<x:String x:Key="Text.Discard.Total" xml:space="preserve">{0} изменений будут отменены</x:String>
|
||||
<x:String x:Key="Text.Discard.Warning" xml:space="preserve">Вы не можете отменить это действие!!!</x:String>
|
||||
@@ -593,11 +598,11 @@
|
||||
<x:String x:Key="Text.OpenInExternalMergeTool" xml:space="preserve">Открыть в инструменте слияния</x:String>
|
||||
<x:String x:Key="Text.Optional" xml:space="preserve">Необязательно.</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.New" xml:space="preserve">Создать новую вкладку</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Bookmark" xml:space="preserve">Закладка</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Close" xml:space="preserve">Закрыть вкладку</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CloseOther" xml:space="preserve">Закрыть другие вкладки</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CloseRight" xml:space="preserve">Закрыть вкладки справа</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CopyPath" xml:space="preserve">Копировать путь репозитория</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Edit" xml:space="preserve">Редактировать...</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.MoveToWorkspace" xml:space="preserve">Переместить в рабочее пространство</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Refresh" xml:space="preserve">Обновить</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Welcome.Title" xml:space="preserve">Репозитории</x:String>
|
||||
@@ -616,7 +621,6 @@
|
||||
<x:String x:Key="Text.Preferences.AI" xml:space="preserve">ОТКРЫТЬ ИИ</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.AdditionalPrompt" xml:space="preserve">Дополнительная подсказка (Для перечисления ваших требований используйте `-`)</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.ApiKey" xml:space="preserve">Ключ API</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Model" xml:space="preserve">Модель</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Name" xml:space="preserve">Имя:</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.ReadApiKeyFromEnv" xml:space="preserve">Введённое значение — это имя для загрузки API-ключа из ENV</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Server" xml:space="preserve">Сервер</x:String>
|
||||
@@ -861,6 +865,7 @@
|
||||
<x:String x:Key="Text.Stash.Title" xml:space="preserve">Отложить локальные изменения</x:String>
|
||||
<x:String x:Key="Text.StashCM.Apply" xml:space="preserve">Принять</x:String>
|
||||
<x:String x:Key="Text.StashCM.ApplyFileChanges" xml:space="preserve">Применить изменения</x:String>
|
||||
<x:String x:Key="Text.StashCM.Branch" xml:space="preserve">Переключить на новую ветку</x:String>
|
||||
<x:String x:Key="Text.StashCM.CopyMessage" xml:space="preserve">Копировать сообщение</x:String>
|
||||
<x:String x:Key="Text.StashCM.Drop" xml:space="preserve">Отбросить</x:String>
|
||||
<x:String x:Key="Text.StashCM.SaveAsPatch" xml:space="preserve">Сохранить как заплатку...</x:String>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<x:String x:Key="Text.AddWorktree.WhatToCheckout.CreateNew" xml:space="preserve">புதிய கிளையை உருவாக்கு</x:String>
|
||||
<x:String x:Key="Text.AddWorktree.WhatToCheckout.Existing" xml:space="preserve">ஏற்கனவே உள்ள கிளை</x:String>
|
||||
<x:String x:Key="Text.AIAssistant" xml:space="preserve">செநு உதவியாளர்</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Model" xml:space="preserve">மாதிரி</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Regen" xml:space="preserve">மறு-உருவாக்கு</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">உறுதிமொழி செய்தியை உருவாக்க செநுவைப் பயன்படுத்து</x:String>
|
||||
<x:String x:Key="Text.Apply" xml:space="preserve">ஒட்டு</x:String>
|
||||
@@ -395,11 +396,11 @@
|
||||
<x:String x:Key="Text.OpenInExternalMergeTool" xml:space="preserve">ஒன்றிணை கருவியில் திற</x:String>
|
||||
<x:String x:Key="Text.Optional" xml:space="preserve">விருப்பத்தேர்வு.</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.New" xml:space="preserve">புதிய பக்கத்தை உருவாக்கு</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Bookmark" xml:space="preserve">புத்தகக்குறி</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Close" xml:space="preserve">மூடு தாவல்</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CloseOther" xml:space="preserve">பிற தாவல்களை மூடு</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CloseRight" xml:space="preserve">வலதுபுறத்தில் உள்ள தாவல்களை மூடு</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CopyPath" xml:space="preserve">களஞ்சிய பாதை நகலெடு</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Edit" xml:space="preserve">திருத்து</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Welcome.Title" xml:space="preserve">களஞ்சியங்கள்</x:String>
|
||||
<x:String x:Key="Text.Paste" xml:space="preserve">ஒட்டு</x:String>
|
||||
<x:String x:Key="Text.Period.DaysAgo" xml:space="preserve">{0} நாட்களுக்கு முன்பு</x:String>
|
||||
@@ -415,7 +416,6 @@
|
||||
<x:String x:Key="Text.Preferences" xml:space="preserve">விருப்பத்தேர்வுகள்</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI" xml:space="preserve">செநு</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.ApiKey" xml:space="preserve">பநிஇ திறவுகோல்</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Model" xml:space="preserve">மாதிரி</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Name" xml:space="preserve">பெயர்</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Server" xml:space="preserve">சேவையகம்</x:String>
|
||||
<x:String x:Key="Text.Preferences.Appearance" xml:space="preserve">தோற்றம்</x:String>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<x:String x:Key="Text.AddWorktree.WhatToCheckout.CreateNew" xml:space="preserve">Створити нову гілку</x:String>
|
||||
<x:String x:Key="Text.AddWorktree.WhatToCheckout.Existing" xml:space="preserve">Наявна гілка</x:String>
|
||||
<x:String x:Key="Text.AIAssistant" xml:space="preserve">AI Асистент</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Model" xml:space="preserve">Модель</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Regen" xml:space="preserve">ПЕРЕГЕНЕРУВАТИ</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">Використати AI для генерації повідомлення коміту</x:String>
|
||||
<x:String x:Key="Text.Apply" xml:space="preserve">Застосувати</x:String>
|
||||
@@ -399,11 +400,11 @@
|
||||
<x:String x:Key="Text.OpenInExternalMergeTool" xml:space="preserve">Відкрити в інструменті злиття</x:String>
|
||||
<x:String x:Key="Text.Optional" xml:space="preserve">Необов'язково.</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.New" xml:space="preserve">Створити нову вкладку</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Bookmark" xml:space="preserve">Закладка</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Close" xml:space="preserve">Закрити вкладку</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CloseOther" xml:space="preserve">Закрити інші вкладки</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CloseRight" xml:space="preserve">Закрити вкладки праворуч</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CopyPath" xml:space="preserve">Копіювати шлях до сховища</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Edit" xml:space="preserve">Редагувати</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Welcome.Title" xml:space="preserve">Сховища</x:String>
|
||||
<x:String x:Key="Text.Paste" xml:space="preserve">Вставити</x:String>
|
||||
<x:String x:Key="Text.Period.DaysAgo" xml:space="preserve">{0} днів тому</x:String>
|
||||
@@ -419,7 +420,6 @@
|
||||
<x:String x:Key="Text.Preferences" xml:space="preserve">Налаштування</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI" xml:space="preserve">AI</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.ApiKey" xml:space="preserve">Ключ API</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Model" xml:space="preserve">Модель</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Name" xml:space="preserve">Назва</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Server" xml:space="preserve">Сервер</x:String>
|
||||
<x:String x:Key="Text.Preferences.Appearance" xml:space="preserve">ВИГЛЯД</x:String>
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
<x:String x:Key="Text.AddWorktree.WhatToCheckout.CreateNew" xml:space="preserve">创建新分支</x:String>
|
||||
<x:String x:Key="Text.AddWorktree.WhatToCheckout.Existing" xml:space="preserve">已有分支</x:String>
|
||||
<x:String x:Key="Text.AIAssistant" xml:space="preserve">AI助手</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Model" xml:space="preserve">模型</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Regen" xml:space="preserve">重新生成</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">使用AI助手生成提交信息</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Use" xml:space="preserve">应用所选</x:String>
|
||||
@@ -119,6 +120,9 @@
|
||||
<x:String x:Key="Text.Checkout.WarnUpdatingSubmodules" xml:space="preserve">以下子模块需要更新:{0}是否立即更新?</x:String>
|
||||
<x:String x:Key="Text.Checkout.WithFastForward" xml:space="preserve">检出分支并快进</x:String>
|
||||
<x:String x:Key="Text.Checkout.WithFastForward.Upstream" xml:space="preserve">上游分支 :</x:String>
|
||||
<x:String x:Key="Text.CheckoutBranchFromStash" xml:space="preserve">从所选贮藏检出分支</x:String>
|
||||
<x:String x:Key="Text.CheckoutBranchFromStash.Branch" xml:space="preserve">新分支 :</x:String>
|
||||
<x:String x:Key="Text.CheckoutBranchFromStash.Stash" xml:space="preserve">所选贮藏 :</x:String>
|
||||
<x:String x:Key="Text.CherryPick" xml:space="preserve">挑选提交</x:String>
|
||||
<x:String x:Key="Text.CherryPick.AppendSourceToMessage" xml:space="preserve">提交信息中追加来源信息</x:String>
|
||||
<x:String x:Key="Text.CherryPick.Commit" xml:space="preserve">提交列表 :</x:String>
|
||||
@@ -386,6 +390,7 @@
|
||||
<x:String x:Key="Text.Discard.All" xml:space="preserve">所有本仓库未提交的修改。</x:String>
|
||||
<x:String x:Key="Text.Discard.Changes" xml:space="preserve">变更 :</x:String>
|
||||
<x:String x:Key="Text.Discard.IncludeIgnored" xml:space="preserve">包括所有已忽略的文件</x:String>
|
||||
<x:String x:Key="Text.Discard.IncludeModified" xml:space="preserve">包括已修改或删除的文件</x:String>
|
||||
<x:String x:Key="Text.Discard.IncludeUntracked" xml:space="preserve">包括未跟踪的文件</x:String>
|
||||
<x:String x:Key="Text.Discard.Total" xml:space="preserve">总计{0}项选中更改</x:String>
|
||||
<x:String x:Key="Text.Discard.Warning" xml:space="preserve">本操作不支持回退,请确认后继续!!!</x:String>
|
||||
@@ -593,11 +598,11 @@
|
||||
<x:String x:Key="Text.OpenInExternalMergeTool" xml:space="preserve">使用外部对比工具查看</x:String>
|
||||
<x:String x:Key="Text.Optional" xml:space="preserve">选填。</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.New" xml:space="preserve">新建空白页</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Bookmark" xml:space="preserve">设置书签</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Close" xml:space="preserve">关闭标签页</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CloseOther" xml:space="preserve">关闭其他标签页</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CloseRight" xml:space="preserve">关闭右侧标签页</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CopyPath" xml:space="preserve">复制仓库路径</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Edit" xml:space="preserve">编辑</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.MoveToWorkspace" xml:space="preserve">移至工作区</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Refresh" xml:space="preserve">刷新</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Welcome.Title" xml:space="preserve">新标签页</x:String>
|
||||
@@ -616,7 +621,6 @@
|
||||
<x:String x:Key="Text.Preferences.AI" xml:space="preserve">AI</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.AdditionalPrompt" xml:space="preserve">附加提示词 (请使用 `-` 列出您的要求)</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.ApiKey" xml:space="preserve">API密钥</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Model" xml:space="preserve">模型</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Name" xml:space="preserve">配置名称</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.ReadApiKeyFromEnv" xml:space="preserve">从环境变量(填写环境变量名)中读取API密钥</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Server" xml:space="preserve">服务地址</x:String>
|
||||
@@ -861,6 +865,7 @@
|
||||
<x:String x:Key="Text.Stash.Title" xml:space="preserve">贮藏本地变更</x:String>
|
||||
<x:String x:Key="Text.StashCM.Apply" xml:space="preserve">应用(apply)</x:String>
|
||||
<x:String x:Key="Text.StashCM.ApplyFileChanges" xml:space="preserve">应用(apply)选中变更</x:String>
|
||||
<x:String x:Key="Text.StashCM.Branch" xml:space="preserve">检出新分支</x:String>
|
||||
<x:String x:Key="Text.StashCM.CopyMessage" xml:space="preserve">复制描述信息</x:String>
|
||||
<x:String x:Key="Text.StashCM.Drop" xml:space="preserve">删除(drop)</x:String>
|
||||
<x:String x:Key="Text.StashCM.SaveAsPatch" xml:space="preserve">另存为补丁...</x:String>
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
<x:String x:Key="Text.AddWorktree.WhatToCheckout.CreateNew" xml:space="preserve">建立新分支</x:String>
|
||||
<x:String x:Key="Text.AddWorktree.WhatToCheckout.Existing" xml:space="preserve">已有分支</x:String>
|
||||
<x:String x:Key="Text.AIAssistant" xml:space="preserve">AI 助理</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Model" xml:space="preserve">模型</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Regen" xml:space="preserve">重新產生</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">使用 AI 產生提交訊息</x:String>
|
||||
<x:String x:Key="Text.AIAssistant.Use" xml:space="preserve">套用選取</x:String>
|
||||
@@ -119,6 +120,9 @@
|
||||
<x:String x:Key="Text.Checkout.WarnUpdatingSubmodules" xml:space="preserve">以下子模組需要更新: {0},您要立即更新嗎?</x:String>
|
||||
<x:String x:Key="Text.Checkout.WithFastForward" xml:space="preserve">簽出分支並快轉</x:String>
|
||||
<x:String x:Key="Text.Checkout.WithFastForward.Upstream" xml:space="preserve">上游分支: </x:String>
|
||||
<x:String x:Key="Text.CheckoutBranchFromStash" xml:space="preserve">從所選擱置簽出分支</x:String>
|
||||
<x:String x:Key="Text.CheckoutBranchFromStash.Branch" xml:space="preserve">新分支:</x:String>
|
||||
<x:String x:Key="Text.CheckoutBranchFromStash.Stash" xml:space="preserve">所選擱置:</x:String>
|
||||
<x:String x:Key="Text.CherryPick" xml:space="preserve">揀選提交</x:String>
|
||||
<x:String x:Key="Text.CherryPick.AppendSourceToMessage" xml:space="preserve">提交資訊中追加來源資訊</x:String>
|
||||
<x:String x:Key="Text.CherryPick.Commit" xml:space="preserve">提交列表:</x:String>
|
||||
@@ -386,6 +390,7 @@
|
||||
<x:String x:Key="Text.Discard.All" xml:space="preserve">所有本機未提交的變更。</x:String>
|
||||
<x:String x:Key="Text.Discard.Changes" xml:space="preserve">變更:</x:String>
|
||||
<x:String x:Key="Text.Discard.IncludeIgnored" xml:space="preserve">包括所有已忽略的檔案</x:String>
|
||||
<x:String x:Key="Text.Discard.IncludeModified" xml:space="preserve">包括已變更或刪除的檔案</x:String>
|
||||
<x:String x:Key="Text.Discard.IncludeUntracked" xml:space="preserve">包含未追蹤檔案</x:String>
|
||||
<x:String x:Key="Text.Discard.Total" xml:space="preserve">將捨棄總計 {0} 項已選取的變更</x:String>
|
||||
<x:String x:Key="Text.Discard.Warning" xml:space="preserve">您無法復原此操作,請確認後再繼續!</x:String>
|
||||
@@ -593,11 +598,11 @@
|
||||
<x:String x:Key="Text.OpenInExternalMergeTool" xml:space="preserve">使用外部比對工具檢視</x:String>
|
||||
<x:String x:Key="Text.Optional" xml:space="preserve">選填。</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.New" xml:space="preserve">新增分頁</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Bookmark" xml:space="preserve">設定書籤</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Close" xml:space="preserve">關閉分頁</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CloseOther" xml:space="preserve">關閉其他分頁</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CloseRight" xml:space="preserve">關閉右側分頁</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.CopyPath" xml:space="preserve">複製存放庫路徑</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Edit" xml:space="preserve">編輯</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.MoveToWorkspace" xml:space="preserve">移至工作區</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Tab.Refresh" xml:space="preserve">重新整理</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Welcome.Title" xml:space="preserve">新分頁</x:String>
|
||||
@@ -616,7 +621,6 @@
|
||||
<x:String x:Key="Text.Preferences.AI" xml:space="preserve">AI</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.AdditionalPrompt" xml:space="preserve">附加提示詞 (請使用 '-' 列出您的要求)</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.ApiKey" xml:space="preserve">API 金鑰</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Model" xml:space="preserve">模型</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Name" xml:space="preserve">名稱</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.ReadApiKeyFromEnv" xml:space="preserve">從環境變數中 (輸入環境變數名稱) 讀取 API 金鑰</x:String>
|
||||
<x:String x:Key="Text.Preferences.AI.Server" xml:space="preserve">伺服器</x:String>
|
||||
@@ -861,6 +865,7 @@
|
||||
<x:String x:Key="Text.Stash.Title" xml:space="preserve">擱置本機變更</x:String>
|
||||
<x:String x:Key="Text.StashCM.Apply" xml:space="preserve">套用 (apply)</x:String>
|
||||
<x:String x:Key="Text.StashCM.ApplyFileChanges" xml:space="preserve">套用 (apply) 所選變更</x:String>
|
||||
<x:String x:Key="Text.StashCM.Branch" xml:space="preserve">簽出分支</x:String>
|
||||
<x:String x:Key="Text.StashCM.CopyMessage" xml:space="preserve">複製描述訊息</x:String>
|
||||
<x:String x:Key="Text.StashCM.Drop" xml:space="preserve">刪除 (drop)</x:String>
|
||||
<x:String x:Key="Text.StashCM.SaveAsPatch" xml:space="preserve">另存為修補檔 (patch)...</x:String>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<Styles xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:s="using:SourceGit"
|
||||
xmlns:vm="using:SourceGit.ViewModels"
|
||||
xmlns:v="using:SourceGit.Views"
|
||||
xmlns:c="using:SourceGit.Converters"
|
||||
@@ -540,36 +539,6 @@
|
||||
<Setter Property="Foreground" Value="{DynamicResource Brush.InlineCodeFG}"/>
|
||||
</Style>
|
||||
|
||||
<Style Selector="SelectableTextBlock">
|
||||
<Setter Property="HorizontalAlignment" Value="Left"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource Brush.FG1}"/>
|
||||
</Style>
|
||||
<Style Selector="SelectableTextBlock[IsEnabled=True]">
|
||||
<Setter Property="ContextFlyout">
|
||||
<Setter.Value>
|
||||
<MenuFlyout Placement="Bottom">
|
||||
<MenuItem Header="{DynamicResource Text.Copy}"
|
||||
Command="{Binding $parent[SelectableTextBlock].Copy}"
|
||||
IsEnabled="{Binding $parent[SelectableTextBlock].CanCopy}"
|
||||
InputGesture="{x:Static TextBox.CopyGesture}">
|
||||
<MenuItem.Icon>
|
||||
<Path Width="11" Height="11" Data="{StaticResource Icons.Copy}" VerticalAlignment="Center"/>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem Header="{DynamicResource Text.CopyAllText}"
|
||||
Command="{x:Static s:App.CopyTextBlockCommand}"
|
||||
CommandParameter="{Binding $parent[SelectableTextBlock]}">
|
||||
<MenuItem.Icon>
|
||||
<Path Width="11" Height="11" Data="{StaticResource Icons.Copy}" VerticalAlignment="Center"/>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
</MenuFlyout>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style Selector="TextBox">
|
||||
<Setter Property="CornerRadius" Value="0"/>
|
||||
<Setter Property="Padding" Value="4,0"/>
|
||||
|
||||
@@ -47,17 +47,17 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="11.3.12" />
|
||||
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.3.12" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="11.3.12" />
|
||||
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.12" />
|
||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.12" />
|
||||
<PackageReference Include="Avalonia.Diagnostics" Version="11.3.12" Condition="'$(Configuration)' == 'Debug'" />
|
||||
<PackageReference Include="Avalonia" Version="11.3.13" />
|
||||
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.3.13" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="11.3.13" />
|
||||
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.13" />
|
||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.13" />
|
||||
<PackageReference Include="Avalonia.Diagnostics" Version="11.3.13" Condition="'$(Configuration)' == 'Debug'" />
|
||||
<PackageReference Include="Azure.AI.OpenAI" Version="2.9.0-beta.1" />
|
||||
<PackageReference Include="BitMiracle.LibTiff.NET" Version="2.4.660" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||
<PackageReference Include="LiveChartsCore.SkiaSharpView.Avalonia" Version="2.0.0-rc6.1" />
|
||||
<PackageReference Include="OpenAI" Version="2.9.1" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.2" />
|
||||
<PackageReference Include="LiveChartsCore.SkiaSharpView.Avalonia" Version="2.0.0" />
|
||||
<PackageReference Include="OpenAI" Version="2.10.0" />
|
||||
<PackageReference Include="Pfim" Version="0.11.4" />
|
||||
|
||||
<ProjectReference Include="../depends/AvaloniaEdit/src/AvaloniaEdit.TextMate/AvaloniaEdit.TextMate.csproj" />
|
||||
|
||||
@@ -10,6 +10,17 @@ namespace SourceGit.ViewModels
|
||||
{
|
||||
public class AIAssistant : ObservableObject
|
||||
{
|
||||
public List<string> AvailableModels
|
||||
{
|
||||
get => _service.AvailableModels;
|
||||
}
|
||||
|
||||
public string CurrentModel
|
||||
{
|
||||
get => _service.Model;
|
||||
set => _service.Model = value;
|
||||
}
|
||||
|
||||
public bool IsGenerating
|
||||
{
|
||||
get => _isGenerating;
|
||||
|
||||
@@ -37,12 +37,6 @@ namespace SourceGit.ViewModels
|
||||
private set;
|
||||
}
|
||||
|
||||
public List<string> RemoteBranches
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public string SelectedBranch
|
||||
{
|
||||
get => _selectedBranch;
|
||||
@@ -59,10 +53,16 @@ namespace SourceGit.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public string SelectedTrackingBranch
|
||||
public List<Models.Branch> RemoteBranches
|
||||
{
|
||||
get;
|
||||
set;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Models.Branch SelectedTrackingBranch
|
||||
{
|
||||
get => _selectedTrackingBranch;
|
||||
set => SetProperty(ref _selectedTrackingBranch, value);
|
||||
}
|
||||
|
||||
public AddWorktree(Repository repo)
|
||||
@@ -70,13 +70,13 @@ namespace SourceGit.ViewModels
|
||||
_repo = repo;
|
||||
|
||||
LocalBranches = new List<string>();
|
||||
RemoteBranches = new List<string>();
|
||||
RemoteBranches = new List<Models.Branch>();
|
||||
foreach (var branch in repo.Branches)
|
||||
{
|
||||
if (branch.IsLocal)
|
||||
LocalBranches.Add(branch.Name);
|
||||
else
|
||||
RemoteBranches.Add(branch.FriendlyName);
|
||||
RemoteBranches.Add(branch);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ namespace SourceGit.ViewModels
|
||||
ProgressDescription = "Adding worktree ...";
|
||||
|
||||
var branchName = _selectedBranch;
|
||||
var tracking = _setTrackingBranch ? SelectedTrackingBranch : string.Empty;
|
||||
var tracking = (_setTrackingBranch && _selectedTrackingBranch != null) ? _selectedTrackingBranch.FriendlyName : string.Empty;
|
||||
var log = _repo.CreateLog("Add Worktree");
|
||||
|
||||
Use(log);
|
||||
@@ -129,15 +129,11 @@ namespace SourceGit.ViewModels
|
||||
return;
|
||||
|
||||
var name = string.IsNullOrEmpty(_selectedBranch) ? System.IO.Path.GetFileName(_path.TrimEnd('/', '\\')) : _selectedBranch;
|
||||
var remoteBranch = RemoteBranches.Find(b => b.EndsWith(name, StringComparison.Ordinal));
|
||||
if (string.IsNullOrEmpty(remoteBranch))
|
||||
var remoteBranch = RemoteBranches.Find(b => b.Name.EndsWith(name, StringComparison.Ordinal));
|
||||
if (remoteBranch == null)
|
||||
remoteBranch = RemoteBranches[0];
|
||||
|
||||
if (!remoteBranch.Equals(SelectedTrackingBranch, StringComparison.Ordinal))
|
||||
{
|
||||
SelectedTrackingBranch = remoteBranch;
|
||||
OnPropertyChanged(nameof(SelectedTrackingBranch));
|
||||
}
|
||||
SelectedTrackingBranch = remoteBranch;
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
@@ -145,5 +141,6 @@ namespace SourceGit.ViewModels
|
||||
private bool _createNewBranch = true;
|
||||
private string _selectedBranch = string.Empty;
|
||||
private bool _setTrackingBranch = false;
|
||||
private Models.Branch _selectedTrackingBranch = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace SourceGit.ViewModels
|
||||
log.Complete();
|
||||
|
||||
if (succ)
|
||||
App.SendNotification(_repo.FullPath, $"Save archive to : {_saveFile}");
|
||||
_repo.SendNotification($"Save archive to : {_saveFile}");
|
||||
return succ;
|
||||
}
|
||||
|
||||
|
||||
70
src/ViewModels/CheckoutBranchFromStash.cs
Normal file
70
src/ViewModels/CheckoutBranchFromStash.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class CheckoutBranchFromStash : Popup
|
||||
{
|
||||
public Models.Stash Target
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
[Required(ErrorMessage = "Branch name is required!")]
|
||||
[RegularExpression(@"^[\w\-/\.#\+]+$", ErrorMessage = "Bad branch name format!")]
|
||||
[CustomValidation(typeof(CheckoutBranchFromStash), nameof(ValidateBranchName))]
|
||||
public string BranchName
|
||||
{
|
||||
get => _branchName;
|
||||
set => SetProperty(ref _branchName, value, true);
|
||||
}
|
||||
|
||||
public CheckoutBranchFromStash(Repository repo, Models.Stash stash)
|
||||
{
|
||||
_repo = repo;
|
||||
Target = stash;
|
||||
}
|
||||
|
||||
public static ValidationResult ValidateBranchName(string name, ValidationContext ctx)
|
||||
{
|
||||
if (ctx.ObjectInstance is CheckoutBranchFromStash caller)
|
||||
{
|
||||
foreach (var b in caller._repo.Branches)
|
||||
{
|
||||
if (b.FriendlyName.Equals(name, StringComparison.Ordinal))
|
||||
return new ValidationResult("A branch with same name already exists!");
|
||||
}
|
||||
|
||||
return ValidationResult.Success;
|
||||
}
|
||||
|
||||
return new ValidationResult("Missing runtime context to create branch!");
|
||||
}
|
||||
|
||||
public override async Task<bool> Sure()
|
||||
{
|
||||
using var lockWatcher = _repo.LockWatcher();
|
||||
ProgressDescription = "Checkout branch from stash...";
|
||||
|
||||
var log = _repo.CreateLog($"Checkout Branch '{_branchName}'");
|
||||
Use(log);
|
||||
|
||||
var succ = await new Commands.Stash(_repo.FullPath)
|
||||
.Use(log)
|
||||
.CheckoutBranchAsync(Target.Name, _branchName);
|
||||
|
||||
if (succ)
|
||||
{
|
||||
_repo.MarkWorkingCopyDirtyManually();
|
||||
_repo.MarkStashesDirtyManually();
|
||||
}
|
||||
|
||||
log.Complete();
|
||||
return true;
|
||||
}
|
||||
|
||||
private readonly Repository _repo;
|
||||
private string _branchName = string.Empty;
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Threading;
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
@@ -66,20 +65,6 @@ namespace SourceGit.ViewModels
|
||||
_parentFolder = activeWorkspace?.DefaultCloneDir;
|
||||
if (string.IsNullOrEmpty(ParentFolder))
|
||||
_parentFolder = Preferences.Instance.GitDefaultCloneDir;
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var text = await App.GetClipboardTextAsync();
|
||||
if (Models.Remote.IsValidURL(text))
|
||||
Dispatcher.UIThread.Post(() => Remote = text);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static ValidationResult ValidateRemote(string remote, ValidationContext _)
|
||||
@@ -127,7 +112,7 @@ namespace SourceGit.ViewModels
|
||||
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
App.RaiseException(_pageId, $"Folder '{path}' can NOT be found");
|
||||
Models.Notification.Send(_pageId, $"Folder '{path}' can NOT be found", true);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -248,7 +248,7 @@ namespace SourceGit.ViewModels
|
||||
saveTo);
|
||||
|
||||
if (succ)
|
||||
App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
|
||||
_repo.SendNotification(App.Text("SaveAsPatchSuccess"));
|
||||
}
|
||||
|
||||
public async Task ResetToThisRevisionAsync(string path)
|
||||
|
||||
@@ -256,11 +256,9 @@ namespace SourceGit.ViewModels
|
||||
await new Commands.Checkout(_repo).MultipleFilesWithRevisionAsync(checkouts, _toHead.SHA);
|
||||
}
|
||||
|
||||
public async Task SaveChangesAsPatchAsync(List<Models.Change> changes, string saveTo)
|
||||
public async Task<bool> SaveChangesAsPatchAsync(List<Models.Change> changes, string saveTo)
|
||||
{
|
||||
var succ = await Commands.SaveChangesAsPatch.ProcessRevisionCompareChangesAsync(_repo, changes, _based, _to, saveTo);
|
||||
if (succ)
|
||||
App.SendNotification(_repo, App.Text("SaveAsPatchSuccess"));
|
||||
return await Commands.SaveChangesAsPatch.ProcessRevisionCompareChangesAsync(_repo, changes, _based, _to, saveTo);
|
||||
}
|
||||
|
||||
private void Refresh()
|
||||
|
||||
@@ -5,6 +5,12 @@ namespace SourceGit.ViewModels
|
||||
{
|
||||
public class DiscardAllMode
|
||||
{
|
||||
public bool IncludeModified
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} = true;
|
||||
|
||||
public bool IncludeUntracked
|
||||
{
|
||||
get;
|
||||
@@ -72,7 +78,7 @@ namespace SourceGit.ViewModels
|
||||
|
||||
if (Mode is DiscardAllMode all)
|
||||
{
|
||||
await Commands.Discard.AllAsync(_repo.FullPath, all.IncludeUntracked, all.IncludeIgnored, log);
|
||||
await Commands.Discard.AllAsync(_repo.FullPath, all.IncludeModified, all.IncludeUntracked, all.IncludeIgnored, log);
|
||||
_repo.ClearCommitMessage();
|
||||
}
|
||||
else
|
||||
|
||||
@@ -210,7 +210,7 @@ namespace SourceGit.ViewModels
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
App.RaiseException(_repo.FullPath, e.Message);
|
||||
_repo.SendNotification(e.Message, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,12 +258,12 @@ namespace SourceGit.ViewModels
|
||||
{
|
||||
var errMsg = builder.ToString().Trim();
|
||||
if (!string.IsNullOrEmpty(errMsg))
|
||||
App.RaiseException(_repo.FullPath, errMsg);
|
||||
_repo.SendNotification(errMsg, true);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
App.RaiseException(_repo.FullPath, e.Message);
|
||||
_repo.SendNotification(e.Message, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace SourceGit.ViewModels
|
||||
if (SetProperty(ref _commits, value))
|
||||
{
|
||||
if (value.Count > 0 && lastSelected != null)
|
||||
SelectedCommit = value.Find(x => x.SHA == lastSelected.SHA);
|
||||
SelectedCommit = value.Find(x => x.SHA.Equals(lastSelected.SHA, StringComparison.Ordinal));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -222,8 +222,8 @@ namespace SourceGit.ViewModels
|
||||
else if (commits.Count == 1)
|
||||
{
|
||||
var commit = (commits[0] as Models.Commit)!;
|
||||
if (_repo.SearchCommitContext.Selected == null || _repo.SearchCommitContext.Selected.SHA != commit.SHA)
|
||||
_repo.SearchCommitContext.Selected = _repo.SearchCommitContext.Results?.Find(x => x.SHA == commit.SHA);
|
||||
if (_repo.SearchCommitContext.Selected == null || !_repo.SearchCommitContext.Selected.SHA.Equals(commit.SHA, StringComparison.Ordinal))
|
||||
_repo.SearchCommitContext.Selected = _repo.SearchCommitContext.Results?.Find(x => x.SHA.Equals(commit.SHA, StringComparison.Ordinal));
|
||||
|
||||
SelectedCommit = commit;
|
||||
NavigationId = _navigationId + 1;
|
||||
@@ -366,7 +366,7 @@ namespace SourceGit.ViewModels
|
||||
var parents = new List<Models.Commit>();
|
||||
foreach (var sha in commit.Parents)
|
||||
{
|
||||
var parent = _commits.Find(x => x.SHA == sha);
|
||||
var parent = _commits.Find(x => x.SHA.Equals(sha, StringComparison.Ordinal));
|
||||
if (parent == null)
|
||||
parent = await new Commands.QuerySingleCommit(_repo.FullPath, sha).GetResultAsync();
|
||||
|
||||
@@ -429,7 +429,7 @@ namespace SourceGit.ViewModels
|
||||
|
||||
var on = await new Commands.QuerySingleCommit(_repo.FullPath, start).GetResultAsync();
|
||||
if (on == null)
|
||||
App.RaiseException(_repo.FullPath, $"Can not squash current commit into parent!");
|
||||
_repo.SendNotification($"Can not squash current commit into parent!", true);
|
||||
else
|
||||
await App.ShowDialog(new InteractiveRebase(_repo, on, prefill));
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ namespace SourceGit.ViewModels
|
||||
|
||||
public Launcher(string startupRepo)
|
||||
{
|
||||
Models.Notification.Raised += DispatchNotification;
|
||||
_ignoreIndexChange = true;
|
||||
|
||||
Pages = new AvaloniaList<LauncherPage>();
|
||||
@@ -72,6 +73,9 @@ namespace SourceGit.ViewModels
|
||||
|
||||
_ignoreIndexChange = false;
|
||||
|
||||
if (TryOpenRepositoryFromPath(startupRepo))
|
||||
return;
|
||||
|
||||
if (!string.IsNullOrEmpty(startupRepo))
|
||||
{
|
||||
var test = new Commands.QueryRepositoryRootPath(startupRepo).GetResult();
|
||||
@@ -96,6 +100,23 @@ namespace SourceGit.ViewModels
|
||||
PostActivePageChanged();
|
||||
}
|
||||
|
||||
public bool TryOpenRepositoryFromPath(string repo)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(repo) && Directory.Exists(repo))
|
||||
{
|
||||
var test = new Commands.QueryRepositoryRootPath(repo).GetResult();
|
||||
if (test.IsSuccess && !string.IsNullOrEmpty(test.StdOut))
|
||||
{
|
||||
var node = Preferences.Instance.FindOrAddNodeByRepositoryPath(test.StdOut.Trim(), null, false);
|
||||
Welcome.Instance.Refresh();
|
||||
OpenRepositoryInTab(node, null);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Quit()
|
||||
{
|
||||
_ignoreIndexChange = true;
|
||||
@@ -111,15 +132,6 @@ namespace SourceGit.ViewModels
|
||||
if (to == null || to.IsActive)
|
||||
return;
|
||||
|
||||
foreach (var one in Pages)
|
||||
{
|
||||
if (!one.CanCreatePopup() || one.Data is Repository { IsAutoFetching: true })
|
||||
{
|
||||
App.RaiseException(null, "You have unfinished task(s) in opened pages. Please wait!!!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_ignoreIndexChange = true;
|
||||
|
||||
var pref = Preferences.Instance;
|
||||
@@ -297,9 +309,14 @@ namespace SourceGit.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
if (!Path.Exists(node.Id))
|
||||
if (!Directory.Exists(node.Id))
|
||||
{
|
||||
App.RaiseException(node.Id, "Repository does NOT exist any more. Please remove it.");
|
||||
ActivePage.Notifications.Add(new Models.Notification
|
||||
{
|
||||
Group = node.Id,
|
||||
Message = "Repository does NOT exist any more. Please remove it.",
|
||||
IsError = true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -307,7 +324,12 @@ namespace SourceGit.ViewModels
|
||||
var gitDir = isBare ? node.Id : GetRepositoryGitDir(node.Id);
|
||||
if (string.IsNullOrEmpty(gitDir))
|
||||
{
|
||||
App.RaiseException(node.Id, "Given path is not a valid git repository!");
|
||||
ActivePage.Notifications.Add(new Models.Notification
|
||||
{
|
||||
Group = node.Id,
|
||||
Message = "Given path is not a valid git repository!",
|
||||
IsError = true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -347,24 +369,24 @@ namespace SourceGit.ViewModels
|
||||
ActivePage = page;
|
||||
}
|
||||
|
||||
public void DispatchNotification(string pageId, string message, bool isError)
|
||||
private void DispatchNotification(Models.Notification notification)
|
||||
{
|
||||
if (!Dispatcher.UIThread.CheckAccess())
|
||||
{
|
||||
Dispatcher.UIThread.Invoke(() => DispatchNotification(pageId, message, isError));
|
||||
Dispatcher.UIThread.Invoke(() => DispatchNotification(notification));
|
||||
return;
|
||||
}
|
||||
|
||||
var notification = new Models.Notification()
|
||||
if (string.IsNullOrEmpty(notification.Group))
|
||||
{
|
||||
IsError = isError,
|
||||
Message = message,
|
||||
};
|
||||
_activePage?.Notifications.Add(notification);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var page in Pages)
|
||||
{
|
||||
var id = page.Node.Id.Replace('\\', '/').TrimEnd('/');
|
||||
if (id == pageId)
|
||||
if (id.Equals(notification.Group, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
page.Notifications.Add(notification);
|
||||
return;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Avalonia.Collections;
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
@@ -59,12 +57,6 @@ namespace SourceGit.ViewModels
|
||||
Notifications.Clear();
|
||||
}
|
||||
|
||||
public async Task CopyPathAsync()
|
||||
{
|
||||
if (_node.IsRepository)
|
||||
await App.CopyTextAsync(_node.Id);
|
||||
}
|
||||
|
||||
public void ChangeDirtyState(Models.DirtyState flag, bool remove)
|
||||
{
|
||||
var state = _dirtyState;
|
||||
@@ -106,7 +98,7 @@ namespace SourceGit.ViewModels
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
App.LogException(e);
|
||||
Native.OS.LogException(e);
|
||||
}
|
||||
|
||||
dump.InProgress = false;
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Collections;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
@@ -253,6 +254,18 @@ namespace SourceGit.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public bool EnableAutoFetch
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} = false;
|
||||
|
||||
public int AutoFetchInterval
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} = 10;
|
||||
|
||||
public bool IgnoreWhitespaceChangesInDiff
|
||||
{
|
||||
get => _ignoreWhitespaceChangesInDiff;
|
||||
@@ -616,6 +629,21 @@ namespace SourceGit.ViewModels
|
||||
RemoveInvalidRepositoriesRecursive(RepositoryNodes);
|
||||
}
|
||||
|
||||
public async Task UpdateAvailableAIModelsAsync()
|
||||
{
|
||||
foreach (var service in OpenAIServices)
|
||||
{
|
||||
try
|
||||
{
|
||||
await service.FetchAvailableModelsAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore errors.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
if (_isLoading || _isReadonly)
|
||||
|
||||
@@ -134,7 +134,7 @@ namespace SourceGit.ViewModels
|
||||
}
|
||||
else
|
||||
{
|
||||
await Commands.Discard.AllAsync(_repo.FullPath, false, false, log);
|
||||
await Commands.Discard.AllAsync(_repo.FullPath, true, false, false, log);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -456,7 +456,7 @@ namespace SourceGit.ViewModels
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
App.RaiseException(string.Empty, $"Failed to start watcher for repository: '{FullPath}'. You may need to press 'F5' to refresh repository manually!\n\nReason: {ex.Message}");
|
||||
SendNotification($"Failed to start watcher for repository: '{FullPath}'. You may need to press 'F5' to refresh repository manually!\n\nReason: {ex.Message}", true);
|
||||
}
|
||||
|
||||
_historyFilterMode = _uiStates.GetHistoryFilterMode();
|
||||
@@ -530,6 +530,11 @@ namespace SourceGit.ViewModels
|
||||
_visibleSubmodules = null;
|
||||
}
|
||||
|
||||
public void SendNotification(string message, bool isError = false)
|
||||
{
|
||||
Models.Notification.Send(FullPath, message, isError);
|
||||
}
|
||||
|
||||
public bool CanCreatePopup()
|
||||
{
|
||||
var page = GetOwnerPage();
|
||||
@@ -599,7 +604,7 @@ namespace SourceGit.ViewModels
|
||||
var log = CreateLog("Install LFS");
|
||||
var succ = await new Commands.LFS(FullPath).Use(log).InstallAsync();
|
||||
if (succ)
|
||||
App.SendNotification(FullPath, "LFS enabled successfully!");
|
||||
SendNotification("LFS enabled successfully!");
|
||||
|
||||
log.Complete();
|
||||
}
|
||||
@@ -612,7 +617,7 @@ namespace SourceGit.ViewModels
|
||||
.TrackAsync(pattern, isFilenameMode);
|
||||
|
||||
if (succ)
|
||||
App.SendNotification(FullPath, $"Tracking successfully! Pattern: {pattern}");
|
||||
SendNotification($"Tracking successfully! Pattern: {pattern}");
|
||||
|
||||
log.Complete();
|
||||
return succ;
|
||||
@@ -626,7 +631,7 @@ namespace SourceGit.ViewModels
|
||||
.LockAsync(remote, path);
|
||||
|
||||
if (succ)
|
||||
App.SendNotification(FullPath, $"Lock file successfully! File: {path}");
|
||||
SendNotification($"Lock file successfully! File: {path}");
|
||||
|
||||
log.Complete();
|
||||
return succ;
|
||||
@@ -640,7 +645,7 @@ namespace SourceGit.ViewModels
|
||||
.UnlockAsync(remote, path, force);
|
||||
|
||||
if (succ && notify)
|
||||
App.SendNotification(FullPath, $"Unlock file successfully! File: {path}");
|
||||
SendNotification($"Unlock file successfully! File: {path}");
|
||||
|
||||
log.Complete();
|
||||
return succ;
|
||||
@@ -697,7 +702,7 @@ namespace SourceGit.ViewModels
|
||||
|
||||
if (_remotes.Count == 0)
|
||||
{
|
||||
App.RaiseException(FullPath, "No remotes added to this repository!!!");
|
||||
SendNotification("No remotes added to this repository!!!", true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -714,13 +719,13 @@ namespace SourceGit.ViewModels
|
||||
|
||||
if (_remotes.Count == 0)
|
||||
{
|
||||
App.RaiseException(FullPath, "No remotes added to this repository!!!");
|
||||
SendNotification("No remotes added to this repository!!!", true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_currentBranch == null)
|
||||
{
|
||||
App.RaiseException(FullPath, "Can NOT find current branch!!!");
|
||||
SendNotification("Can NOT find current branch!!!", true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -738,13 +743,13 @@ namespace SourceGit.ViewModels
|
||||
|
||||
if (_remotes.Count == 0)
|
||||
{
|
||||
App.RaiseException(FullPath, "No remotes added to this repository!!!");
|
||||
SendNotification("No remotes added to this repository!!!", true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_currentBranch == null)
|
||||
{
|
||||
App.RaiseException(FullPath, "Can NOT find current branch!!!");
|
||||
SendNotification("Can NOT find current branch!!!", true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1120,9 +1125,9 @@ namespace SourceGit.ViewModels
|
||||
|
||||
var head = await new Commands.QueryRevisionByRefName(FullPath, "HEAD").GetResultAsync();
|
||||
if (!succ)
|
||||
App.RaiseException(FullPath, log.Content.Substring(log.Content.IndexOf('\n')).Trim());
|
||||
SendNotification(log.Content.Substring(log.Content.IndexOf('\n')).Trim(), true);
|
||||
else if (log.Content.Contains("is the first bad commit"))
|
||||
App.SendNotification(FullPath, log.Content.Substring(log.Content.IndexOf('\n')).Trim());
|
||||
SendNotification(log.Content.Substring(log.Content.IndexOf('\n')).Trim());
|
||||
|
||||
MarkBranchesDirtyManually();
|
||||
NavigateToCommit(head, true);
|
||||
@@ -1380,7 +1385,7 @@ namespace SourceGit.ViewModels
|
||||
{
|
||||
if (_currentBranch == null)
|
||||
{
|
||||
App.RaiseException(FullPath, "Git cannot create a branch before your first commit.");
|
||||
SendNotification("Git cannot create a branch before your first commit.", true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1463,7 +1468,7 @@ namespace SourceGit.ViewModels
|
||||
{
|
||||
if (_currentBranch == null)
|
||||
{
|
||||
App.RaiseException(FullPath, "Git cannot create a branch before your first commit.");
|
||||
SendNotification("Git cannot create a tag before your first commit.", true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1882,7 +1887,7 @@ namespace SourceGit.ViewModels
|
||||
|
||||
try
|
||||
{
|
||||
if (_settings is not { EnableAutoFetch: true } || !CanCreatePopup())
|
||||
if (Preferences.Instance.EnableAutoFetch || !CanCreatePopup())
|
||||
{
|
||||
_lastFetchTime = DateTime.Now;
|
||||
return;
|
||||
@@ -1893,7 +1898,7 @@ namespace SourceGit.ViewModels
|
||||
return;
|
||||
|
||||
var now = DateTime.Now;
|
||||
var desire = _lastFetchTime.AddMinutes(_settings.AutoFetchInterval);
|
||||
var desire = _lastFetchTime.AddMinutes(Preferences.Instance.AutoFetchInterval);
|
||||
if (desire > now)
|
||||
return;
|
||||
|
||||
|
||||
@@ -101,26 +101,6 @@ namespace SourceGit.ViewModels
|
||||
set => _repo.Settings.AskBeforeAutoUpdatingSubmodules = value;
|
||||
}
|
||||
|
||||
public bool EnableAutoFetch
|
||||
{
|
||||
get => _repo.Settings.EnableAutoFetch;
|
||||
set => _repo.Settings.EnableAutoFetch = value;
|
||||
}
|
||||
|
||||
public int? AutoFetchInterval
|
||||
{
|
||||
get => _repo.Settings.AutoFetchInterval;
|
||||
set
|
||||
{
|
||||
if (value is null || value < 1)
|
||||
return;
|
||||
|
||||
var interval = (int)value;
|
||||
if (_repo.Settings.AutoFetchInterval != interval)
|
||||
_repo.Settings.AutoFetchInterval = interval;
|
||||
}
|
||||
}
|
||||
|
||||
public AvaloniaList<Models.CommitTemplate> CommitTemplates
|
||||
{
|
||||
get => _repo.Settings.CommitTemplates;
|
||||
|
||||
@@ -306,7 +306,7 @@ namespace SourceGit.ViewModels
|
||||
{
|
||||
var succ = await Commands.SaveChangesAsPatch.ProcessRevisionCompareChangesAsync(_repo.FullPath, changes ?? _changes, GetSHA(_startPoint), GetSHA(_endPoint), saveTo);
|
||||
if (succ)
|
||||
App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
|
||||
_repo.SendNotification(App.Text("SaveAsPatchSuccess"));
|
||||
}
|
||||
|
||||
public void ClearSearchFilter()
|
||||
|
||||
@@ -68,10 +68,19 @@ namespace SourceGit.ViewModels
|
||||
.Use(log)
|
||||
.RunAsync();
|
||||
|
||||
if (succ && needAutoStash)
|
||||
await new Commands.Stash(_repo.FullPath)
|
||||
.Use(log)
|
||||
.PopAsync("stash@{0}");
|
||||
if (succ)
|
||||
{
|
||||
if (needAutoStash)
|
||||
await new Commands.Stash(_repo.FullPath)
|
||||
.Use(log)
|
||||
.PopAsync("stash@{0}");
|
||||
|
||||
if (_repo.SelectedViewIndex == 0)
|
||||
{
|
||||
var head = await new Commands.QueryRevisionByRefName(_repo.FullPath, "HEAD").GetResultAsync();
|
||||
_repo.NavigateToCommit(head, true);
|
||||
}
|
||||
}
|
||||
|
||||
log.Complete();
|
||||
return succ;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -56,7 +57,7 @@ namespace SourceGit.ViewModels
|
||||
{
|
||||
if (string.IsNullOrEmpty(_customDir))
|
||||
{
|
||||
App.RaiseException(null, "Missing root directory to scan!");
|
||||
Models.Notification.Send(null, "Missing root directory to scan!", true);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -66,7 +67,7 @@ namespace SourceGit.ViewModels
|
||||
{
|
||||
if (_selected == null || string.IsNullOrEmpty(_selected.Path))
|
||||
{
|
||||
App.RaiseException(null, "Missing root directory to scan!");
|
||||
Models.Notification.Send(null, "Missing root directory to scan!", true);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -95,12 +96,12 @@ namespace SourceGit.ViewModels
|
||||
foreach (var f in found)
|
||||
{
|
||||
var parent = new DirectoryInfo(f).Parent!.FullName.Replace('\\', '/').TrimEnd('/');
|
||||
if (parent.Equals(normalizedRoot, StringComparison.Ordinal))
|
||||
if (parent.Equals(normalizedRoot, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var node = Preferences.Instance.FindOrAddNodeByRepositoryPath(f, null, false, false);
|
||||
await node.UpdateStatusAsync(false, null);
|
||||
}
|
||||
else if (parent.StartsWith(normalizedRoot, StringComparison.Ordinal))
|
||||
else if (parent.StartsWith(normalizedRoot, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var relative = parent.Substring(normalizedRoot.Length).TrimStart('/');
|
||||
var group = FindOrCreateGroupRecursive(Preferences.Instance.RepositoryNodes, relative);
|
||||
@@ -120,7 +121,7 @@ namespace SourceGit.ViewModels
|
||||
foreach (var node in group)
|
||||
{
|
||||
if (node.IsRepository)
|
||||
repos.Add(node.Id);
|
||||
repos.Add(OperatingSystem.IsLinux() ? node.Id : node.Id.ToLower(CultureInfo.CurrentCulture));
|
||||
else
|
||||
GetManagedRepositories(node.SubNodes, repos);
|
||||
}
|
||||
@@ -138,7 +139,7 @@ namespace SourceGit.ViewModels
|
||||
ProgressDescription = $"Scanning {subdir.FullName}...";
|
||||
|
||||
var normalizedSelf = subdir.FullName.Replace('\\', '/').TrimEnd('/');
|
||||
if (_managed.Contains(normalizedSelf))
|
||||
if (IsManaged(normalizedSelf))
|
||||
continue;
|
||||
|
||||
var gitDir = Path.Combine(subdir.FullName, ".git");
|
||||
@@ -148,7 +149,7 @@ namespace SourceGit.ViewModels
|
||||
if (test.IsSuccess && !string.IsNullOrEmpty(test.StdOut))
|
||||
{
|
||||
var normalized = test.StdOut.Trim().Replace('\\', '/').TrimEnd('/');
|
||||
if (!_managed.Contains(normalized))
|
||||
if (!IsManaged(normalized))
|
||||
outs.Add(normalized);
|
||||
}
|
||||
|
||||
@@ -200,6 +201,14 @@ namespace SourceGit.ViewModels
|
||||
return added;
|
||||
}
|
||||
|
||||
private bool IsManaged(string path)
|
||||
{
|
||||
if (OperatingSystem.IsLinux())
|
||||
return _managed.Contains(path);
|
||||
|
||||
return _managed.Contains(path.ToLower(CultureInfo.CurrentCulture));
|
||||
}
|
||||
|
||||
private HashSet<string> _managed = new();
|
||||
private bool _useCustomDir = false;
|
||||
private string _customDir = string.Empty;
|
||||
|
||||
@@ -153,6 +153,12 @@ namespace SourceGit.ViewModels
|
||||
_repo.ShowPopup(new ApplyStash(_repo, stash));
|
||||
}
|
||||
|
||||
public void CheckoutBranch(Models.Stash stash)
|
||||
{
|
||||
if (_repo.CanCreatePopup())
|
||||
_repo.ShowPopup(new CheckoutBranchFromStash(_repo, stash));
|
||||
}
|
||||
|
||||
public void Drop(Models.Stash stash)
|
||||
{
|
||||
if (_repo.CanCreatePopup())
|
||||
@@ -183,7 +189,7 @@ namespace SourceGit.ViewModels
|
||||
|
||||
var succ = await Commands.SaveChangesAsPatch.ProcessStashChangesAsync(_repo.FullPath, opts, saveTo);
|
||||
if (succ)
|
||||
App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
|
||||
_repo.SendNotification(App.Text("SaveAsPatchSuccess"));
|
||||
}
|
||||
|
||||
public void OpenChangeWithExternalDiffTool(Models.Change change)
|
||||
|
||||
@@ -104,7 +104,7 @@ namespace SourceGit.ViewModels
|
||||
{
|
||||
if (!Preferences.Instance.IsGitConfigured())
|
||||
{
|
||||
App.RaiseException(string.Empty, App.Text("NotConfigured"));
|
||||
Models.Notification.Send(null, App.Text("NotConfigured"), true);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ namespace SourceGit.ViewModels
|
||||
{
|
||||
if (!Preferences.Instance.IsGitConfigured())
|
||||
{
|
||||
App.RaiseException(string.Empty, App.Text("NotConfigured"));
|
||||
Models.Notification.Send(null, App.Text("NotConfigured"), true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ namespace SourceGit.ViewModels
|
||||
{
|
||||
if (!Preferences.Instance.IsGitConfigured())
|
||||
{
|
||||
App.RaiseException(string.Empty, App.Text("NotConfigured"));
|
||||
Models.Notification.Send(null, App.Text("NotConfigured"), true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ namespace SourceGit.ViewModels
|
||||
public void OpenTerminal()
|
||||
{
|
||||
if (!Preferences.Instance.IsGitConfigured())
|
||||
App.RaiseException(string.Empty, App.Text("NotConfigured"));
|
||||
Models.Notification.Send(null, App.Text("NotConfigured"), true);
|
||||
else
|
||||
Native.OS.OpenTerminal(null);
|
||||
}
|
||||
@@ -175,7 +175,7 @@ namespace SourceGit.ViewModels
|
||||
{
|
||||
if (!Preferences.Instance.IsGitConfigured())
|
||||
{
|
||||
App.RaiseException(string.Empty, App.Text("NotConfigured"));
|
||||
Models.Notification.Send(null, App.Text("NotConfigured"), true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ namespace SourceGit.ViewModels
|
||||
var currentBranch = _repo.CurrentBranch;
|
||||
if (currentBranch == null)
|
||||
{
|
||||
App.RaiseException(_repo.FullPath, "No commits to amend!!!");
|
||||
_repo.SendNotification("No commits to amend!!!", true);
|
||||
_useAmend = false;
|
||||
OnPropertyChanged();
|
||||
return;
|
||||
@@ -415,7 +415,7 @@ namespace SourceGit.ViewModels
|
||||
{
|
||||
var succ = await Commands.SaveChangesAsPatch.ProcessLocalChangesAsync(_repo.FullPath, changes, isUnstaged, saveTo);
|
||||
if (succ)
|
||||
App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
|
||||
_repo.SendNotification(App.Text("SaveAsPatchSuccess"));
|
||||
}
|
||||
|
||||
public void Discard(List<Models.Change> changes)
|
||||
@@ -614,13 +614,13 @@ namespace SourceGit.ViewModels
|
||||
|
||||
if (!_repo.CanCreatePopup())
|
||||
{
|
||||
App.RaiseException(_repo.FullPath, "Repository has an unfinished job! Please wait!");
|
||||
_repo.SendNotification("Repository has an unfinished job! Please wait!", true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (autoStage && HasUnsolvedConflicts)
|
||||
{
|
||||
App.RaiseException(_repo.FullPath, "Repository has unsolved conflict(s). Auto-stage and commit is disabled!");
|
||||
_repo.SendNotification("Repository has unsolved conflict(s). Auto-stage and commit is disabled!", true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -665,18 +665,15 @@ namespace SourceGit.ViewModels
|
||||
var log = _repo.CreateLog("Commit");
|
||||
var succ = await new Commands.Commit(_repo.FullPath, _commitMessage, EnableSignOff, NoVerifyOnCommit, _useAmend, _resetAuthor)
|
||||
.Use(log)
|
||||
.RunAsync()
|
||||
.ConfigureAwait(false);
|
||||
.RunAsync();
|
||||
|
||||
log.Complete();
|
||||
|
||||
if (succ)
|
||||
{
|
||||
// Do not use property `UseAmend` but manually trigger property changed to avoid refreshing staged changes here.
|
||||
_useAmend = false;
|
||||
OnPropertyChanged(nameof(UseAmend));
|
||||
|
||||
UseAmend = false;
|
||||
CommitMessage = string.Empty;
|
||||
|
||||
if (autoPush && _repo.Remotes.Count > 0)
|
||||
{
|
||||
Models.Branch pushBranch = null;
|
||||
|
||||
@@ -46,18 +46,33 @@
|
||||
Content="{Binding Text}"/>
|
||||
|
||||
<!-- Options -->
|
||||
<Border Grid.Row="2" Margin="0,0,0,8">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
|
||||
<v:LoadingIcon Width="14" Height="14"
|
||||
Margin="0,0,8,0"
|
||||
IsVisible="{Binding IsGenerating}"/>
|
||||
<Button Classes="flat"
|
||||
<Grid Grid.Row="2" Margin="8,0,8,8" ColumnDefinitions="Auto,*,18,Auto">
|
||||
<TextBlock Grid.Column="0"
|
||||
Classes="group_header_label"
|
||||
Text="{DynamicResource Text.AIAssistant.Model}"/>
|
||||
|
||||
<ComboBox Grid.Column="1"
|
||||
Height="28"
|
||||
Padding="12,0"
|
||||
Content="{DynamicResource Text.AIAssistant.Regen}"
|
||||
IsEnabled="{Binding !IsGenerating}"
|
||||
Click="OnRegenClicked"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
Margin="6,0" Padding="4,0"
|
||||
BorderThickness="0"
|
||||
Background="Transparent"
|
||||
VerticalAlignment="Center"
|
||||
ItemsSource="{Binding AvailableModels, Mode=OneWay}"
|
||||
SelectedItem="{Binding CurrentModel, Mode=TwoWay}"
|
||||
SelectionChanged="OnModelChanged"/>
|
||||
|
||||
<v:LoadingIcon Grid.Column="2"
|
||||
Width="14" Height="14"
|
||||
Margin="0,0,8,0"
|
||||
IsVisible="{Binding IsGenerating}"/>
|
||||
|
||||
<Button Grid.Column="3"
|
||||
Classes="flat"
|
||||
Height="28"
|
||||
Padding="12,0"
|
||||
Content="{DynamicResource Text.AIAssistant.Regen}"
|
||||
IsEnabled="{Binding !IsGenerating}"
|
||||
Click="OnRegenClicked"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</v:ChromelessWindow>
|
||||
|
||||
@@ -101,7 +101,7 @@ namespace SourceGit.Views
|
||||
return;
|
||||
|
||||
var apply = new MenuItem() { Header = App.Text("AIAssistant.Use") };
|
||||
apply.Icon = App.CreateMenuIcon("Icons.Check");
|
||||
apply.Icon = this.CreateMenuIcon("Icons.Check");
|
||||
apply.Click += (_, ev) =>
|
||||
{
|
||||
vm.Use(selected);
|
||||
@@ -109,10 +109,10 @@ namespace SourceGit.Views
|
||||
};
|
||||
|
||||
var copy = new MenuItem() { Header = App.Text("Copy") };
|
||||
copy.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copy.Icon = this.CreateMenuIcon("Icons.Copy");
|
||||
copy.Click += async (_, ev) =>
|
||||
{
|
||||
await App.CopyTextAsync(selected);
|
||||
await this.CopyTextAsync(selected);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
@@ -149,6 +149,13 @@ namespace SourceGit.Views
|
||||
(DataContext as ViewModels.AIAssistant)?.Cancel();
|
||||
}
|
||||
|
||||
private async void OnModelChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (DataContext is ViewModels.AIAssistant vm && IsLoaded)
|
||||
await vm.GenAsync();
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private async void OnRegenClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (DataContext is ViewModels.AIAssistant vm)
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace SourceGit.Views
|
||||
{
|
||||
if (attr.Key.Equals("BuildDate", StringComparison.OrdinalIgnoreCase) && DateTime.TryParse(attr.Value, out var date))
|
||||
{
|
||||
TxtReleaseDate.Text = App.Text("About.ReleaseDate", date.ToLocalTime().ToString("MMM d yyyy"));
|
||||
TxtReleaseDate.Text = App.Text("About.ReleaseDate", Models.DateTimeFormat.Format(date, true));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="using:SourceGit.ViewModels"
|
||||
xmlns:v="using:SourceGit.Views"
|
||||
xmlns:c="using:SourceGit.Converters"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="SourceGit.Views.AddWorktree"
|
||||
@@ -84,22 +85,12 @@
|
||||
Margin="0,0,8,0"
|
||||
Text="{DynamicResource Text.AddWorktree.Tracking}"/>
|
||||
</Border>
|
||||
<ComboBox Grid.Row="3" Grid.Column="1"
|
||||
Height="28" Padding="8,0"
|
||||
VerticalAlignment="Center" HorizontalAlignment="Stretch"
|
||||
ItemsSource="{Binding RemoteBranches}"
|
||||
IsTextSearchEnabled="True"
|
||||
SelectedItem="{Binding SelectedTrackingBranch, Mode=TwoWay}"
|
||||
IsVisible="{Binding SetTrackingBranch, Mode=OneWay}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal" Height="20" VerticalAlignment="Center">
|
||||
<Path Margin="0,0,8,0" Width="14" Height="14" Fill="{DynamicResource Brush.FG1}" Data="{StaticResource Icons.Branch}"/>
|
||||
<TextBlock Text="{Binding}"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
<v:BranchSelector Grid.Row="3" Grid.Column="1"
|
||||
Height="28"
|
||||
VerticalAlignment="Center" HorizontalAlignment="Stretch"
|
||||
Branches="{Binding RemoteBranches}"
|
||||
SelectedBranch="{Binding SelectedTrackingBranch, Mode=TwoWay}"
|
||||
IsVisible="{Binding SetTrackingBranch, Mode=OneWay}"/>
|
||||
|
||||
<CheckBox Grid.Row="4" Grid.Column="1"
|
||||
Height="32"
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace SourceGit.Views
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
App.RaiseException(string.Empty, $"Failed to select location: {exception.Message}");
|
||||
Models.Notification.Send(null, $"Failed to select location: {exception.Message}", true);
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
|
||||
51
src/Views/Alert.axaml
Normal file
51
src/Views/Alert.axaml
Normal file
@@ -0,0 +1,51 @@
|
||||
<v:ChromelessWindow xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:v="using:SourceGit.Views"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="SourceGit.Views.Alert"
|
||||
x:Name="ThisControl"
|
||||
Icon="/App.ico"
|
||||
SizeToContent="WidthAndHeight"
|
||||
CanResize="False"
|
||||
WindowStartupLocation="CenterOwner">
|
||||
<Grid RowDefinitions="Auto,Auto,Auto">
|
||||
<!-- TitleBar -->
|
||||
<Grid Grid.Row="0" Height="28" IsVisible="{Binding !#ThisControl.UseSystemWindowFrame}">
|
||||
<Border Background="{DynamicResource Brush.TitleBar}"
|
||||
BorderThickness="0,0,0,1" BorderBrush="{DynamicResource Brush.Border0}"
|
||||
PointerPressed="BeginMoveWindow"/>
|
||||
|
||||
<Path Width="14" Height="14"
|
||||
Margin="10,0,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
Data="{StaticResource Icons.Error}"
|
||||
IsVisible="{OnPlatform True, macOS=False}"/>
|
||||
|
||||
<TextBlock x:Name="TxtTitle"
|
||||
Classes="bold"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
IsHitTestVisible="False"/>
|
||||
|
||||
<v:CaptionButtons HorizontalAlignment="Right"
|
||||
IsCloseButtonOnly="True"
|
||||
IsVisible="{OnPlatform True, macOS=False}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- Body -->
|
||||
<Border Grid.Row="1" Margin="16">
|
||||
<TextBlock x:Name="Message" MaxWidth="520" MinWidth="240" TextWrapping="Wrap"/>
|
||||
</Border>
|
||||
|
||||
<!-- Buttons -->
|
||||
<Button Grid.Row="2"
|
||||
Classes="flat primary"
|
||||
Width="80" Height="30"
|
||||
Margin="0,0,0,16"
|
||||
HorizontalAlignment="Center"
|
||||
HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
|
||||
Content="{DynamicResource Text.Sure}"
|
||||
Click="OnOk"/>
|
||||
</Grid>
|
||||
</v:ChromelessWindow>
|
||||
29
src/Views/Alert.axaml.cs
Normal file
29
src/Views/Alert.axaml.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
|
||||
namespace SourceGit.Views
|
||||
{
|
||||
public partial class Alert : ChromelessWindow
|
||||
{
|
||||
public Alert()
|
||||
{
|
||||
CloseOnESC = true;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public async Task ShowAsync(Window owner, string message, bool isError)
|
||||
{
|
||||
var title = isError ? App.Text("Launcher.Error") : App.Text("Launcher.Info");
|
||||
Title = title;
|
||||
TxtTitle.Text = title;
|
||||
Message.Text = message;
|
||||
await ShowDialog(owner);
|
||||
}
|
||||
|
||||
private void OnOk(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -193,7 +193,7 @@ namespace SourceGit.Views
|
||||
}
|
||||
|
||||
var refetch = new MenuItem();
|
||||
refetch.Icon = App.CreateMenuIcon("Icons.Loading");
|
||||
refetch.Icon = this.CreateMenuIcon("Icons.Loading");
|
||||
refetch.Header = App.Text("Avatar.Refetch");
|
||||
refetch.Click += (_, ev) =>
|
||||
{
|
||||
@@ -204,7 +204,7 @@ namespace SourceGit.Views
|
||||
};
|
||||
|
||||
var load = new MenuItem();
|
||||
load.Icon = App.CreateMenuIcon("Icons.Folder.Open");
|
||||
load.Icon = this.CreateMenuIcon("Icons.Folder.Open");
|
||||
load.Header = App.Text("Avatar.Load");
|
||||
load.Click += async (_, ev) =>
|
||||
{
|
||||
@@ -225,7 +225,7 @@ namespace SourceGit.Views
|
||||
};
|
||||
|
||||
var saveAs = new MenuItem();
|
||||
saveAs.Icon = App.CreateMenuIcon("Icons.Save");
|
||||
saveAs.Icon = this.CreateMenuIcon("Icons.Save");
|
||||
saveAs.Header = App.Text("SaveAs");
|
||||
saveAs.Click += async (_, ev) =>
|
||||
{
|
||||
|
||||
@@ -427,10 +427,10 @@ namespace SourceGit.Views
|
||||
|
||||
var copy = new MenuItem();
|
||||
copy.Header = App.Text("Copy");
|
||||
copy.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copy.Icon = this.CreateMenuIcon("Icons.Copy");
|
||||
copy.Click += async (_, ev) =>
|
||||
{
|
||||
await App.CopyTextAsync(selected);
|
||||
await this.CopyTextAsync(selected);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
|
||||
152
src/Views/BranchSelector.axaml
Normal file
152
src/Views/BranchSelector.axaml
Normal file
@@ -0,0 +1,152 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:m="using:SourceGit.Models"
|
||||
xmlns:v="using:SourceGit.Views"
|
||||
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450"
|
||||
x:Class="SourceGit.Views.BranchSelector">
|
||||
<UserControl.Styles>
|
||||
<Style Selector="v|BranchSelector">
|
||||
<Setter Property="FocusAdorner">
|
||||
<FocusAdornerTemplate>
|
||||
<Border/>
|
||||
</FocusAdornerTemplate>
|
||||
</Setter>
|
||||
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<Grid Background="Transparent">
|
||||
<Border x:Name="PART_Background"
|
||||
Background="{DynamicResource Brush.Contents}"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{DynamicResource Brush.Border1}"
|
||||
CornerRadius="3"/>
|
||||
|
||||
<Grid x:Name="PART_Selected"
|
||||
Background="Transparent"
|
||||
ColumnDefinitions="Auto,*,32"
|
||||
PointerPressed="OnToggleDropDown">
|
||||
<Path Grid.Column="0"
|
||||
Margin="8,0,0,0"
|
||||
Width="14" Height="14"
|
||||
Data="{StaticResource Icons.Branch}"
|
||||
IsHitTestVisible="False"/>
|
||||
|
||||
<ContentControl Grid.Column="1"
|
||||
Margin="8,0,0,0"
|
||||
Content="{TemplateBinding SelectedBranch, Mode=OneWay}">
|
||||
<ContentControl.DataTemplates>
|
||||
<DataTemplate DataType="m:Branch">
|
||||
<TextBlock Text="{Binding FriendlyName, Mode=OneWay}" VerticalAlignment="Center"/>
|
||||
</DataTemplate>
|
||||
</ContentControl.DataTemplates>
|
||||
</ContentControl>
|
||||
|
||||
<Path Grid.Column="2"
|
||||
Width="12" Height="12"
|
||||
Margin="0,0,10,0"
|
||||
HorizontalAlignment="Right" VerticalAlignment="Center"
|
||||
Data="M0 0 M1939 486L2029 576L1024 1581L19 576L109 486L1024 1401L1939 486Z"
|
||||
IsHitTestVisible="False"/>
|
||||
</Grid>
|
||||
|
||||
<Popup x:Name="PART_Popup"
|
||||
WindowManagerAddShadowHint="False"
|
||||
IsOpen="{TemplateBinding IsDropDownOpened, Mode=TwoWay}"
|
||||
Width="{Binding Bounds.Width, ElementName=PART_Background}"
|
||||
MaxHeight="600"
|
||||
PlacementTarget="PART_Background"
|
||||
Placement="BottomEdgeAlignedLeft"
|
||||
VerticalOffset="2"
|
||||
IsLightDismissEnabled="True"
|
||||
InheritsTransform="True">
|
||||
<Border Background="{DynamicResource Brush.Contents}"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{DynamicResource Brush.Accent}"
|
||||
CornerRadius="4"
|
||||
Padding="4"
|
||||
HorizontalAlignment="Stretch">
|
||||
<Grid RowDefinitions="36,Auto">
|
||||
<TextBox Grid.Row="0"
|
||||
x:Name="PART_TextFilter"
|
||||
Height="24"
|
||||
Margin="6,0"
|
||||
BorderThickness="1"
|
||||
CornerRadius="12"
|
||||
Text="{TemplateBinding SearchFilter, Mode=TwoWay}"
|
||||
BorderBrush="{DynamicResource Brush.Border2}"
|
||||
VerticalContentAlignment="Center"
|
||||
KeyDown="OnSearchBoxKeyDown">
|
||||
<TextBox.InnerLeftContent>
|
||||
<Path Width="14" Height="14"
|
||||
Margin="6,0,0,0"
|
||||
Fill="{DynamicResource Brush.FG2}"
|
||||
Data="{StaticResource Icons.Search}"/>
|
||||
</TextBox.InnerLeftContent>
|
||||
|
||||
<TextBox.InnerRightContent>
|
||||
<Button Classes="icon_button"
|
||||
Width="16"
|
||||
Margin="0,0,6,0"
|
||||
Click="OnClearSearchFilter"
|
||||
IsVisible="{Binding ElementName=PART_TextFilter, Path=Text, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
|
||||
HorizontalAlignment="Right">
|
||||
<Path Width="14" Height="14"
|
||||
Margin="0,1,0,0"
|
||||
Fill="{DynamicResource Brush.FG1}"
|
||||
Data="{StaticResource Icons.Clear}"/>
|
||||
</Button>
|
||||
</TextBox.InnerRightContent>
|
||||
</TextBox>
|
||||
|
||||
<ListBox Grid.Row="1"
|
||||
Focusable="True"
|
||||
Margin="0,4"
|
||||
MaxHeight="360"
|
||||
BorderThickness="0"
|
||||
Background="Transparent"
|
||||
ItemsSource="{TemplateBinding VisibleBranches, Mode=OneWay}"
|
||||
SelectedItem="{TemplateBinding SelectedBranch, Mode=TwoWay}"
|
||||
KeyDown="OnDropDownListKeyDown">
|
||||
<ListBox.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<VirtualizingStackPanel Orientation="Vertical"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ListBox.ItemsPanel>
|
||||
|
||||
<ListBox.Styles>
|
||||
<Style Selector="ListBoxItem">
|
||||
<Setter Property="Height" Value="28"/>
|
||||
<Setter Property="CornerRadius" Value="3"/>
|
||||
</Style>
|
||||
</ListBox.Styles>
|
||||
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate DataType="m:Branch">
|
||||
<StackPanel Orientation="Horizontal" Background="Transparent" Height="28" PointerPressed="OnDropDownItemPointerPressed">
|
||||
<Path Width="14" Height="14" Fill="{DynamicResource Brush.FG1}" Data="{StaticResource Icons.Branch}"/>
|
||||
<TextBlock Margin="8,0,0,0" Text="{Binding FriendlyName, Mode=OneWay}" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Popup>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
|
||||
<Style Selector="^:pointerover /template/ Border#PART_Background">
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource Brush.Accent}" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="^:focus-visible">
|
||||
<Style Selector="^ /template/ Border#PART_Background">
|
||||
<Setter Property="Background" Value="{DynamicResource ComboBoxBackgroundUnfocused}" />
|
||||
</Style>
|
||||
</Style>
|
||||
</Style>
|
||||
</UserControl.Styles>
|
||||
</UserControl>
|
||||
224
src/Views/BranchSelector.axaml.cs
Normal file
224
src/Views/BranchSelector.axaml.cs
Normal file
@@ -0,0 +1,224 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.VisualTree;
|
||||
|
||||
namespace SourceGit.Views
|
||||
{
|
||||
public partial class BranchSelector : UserControl
|
||||
{
|
||||
public static readonly StyledProperty<List<Models.Branch>> BranchesProperty =
|
||||
AvaloniaProperty.Register<BranchSelector, List<Models.Branch>>(nameof(Branches));
|
||||
|
||||
public List<Models.Branch> Branches
|
||||
{
|
||||
get => GetValue(BranchesProperty);
|
||||
set => SetValue(BranchesProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<List<Models.Branch>> VisibleBranchesProperty =
|
||||
AvaloniaProperty.Register<BranchSelector, List<Models.Branch>>(nameof(VisibleBranches));
|
||||
|
||||
public List<Models.Branch> VisibleBranches
|
||||
{
|
||||
get => GetValue(VisibleBranchesProperty);
|
||||
set => SetValue(VisibleBranchesProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<Models.Branch> SelectedBranchProperty =
|
||||
AvaloniaProperty.Register<BranchSelector, Models.Branch>(nameof(SelectedBranch));
|
||||
|
||||
public Models.Branch SelectedBranch
|
||||
{
|
||||
get => GetValue(SelectedBranchProperty);
|
||||
set => SetValue(SelectedBranchProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<bool> IsDropDownOpenedProperty =
|
||||
AvaloniaProperty.Register<BranchSelector, bool>(nameof(IsDropDownOpened));
|
||||
|
||||
public bool IsDropDownOpened
|
||||
{
|
||||
get => GetValue(IsDropDownOpenedProperty);
|
||||
set => SetValue(IsDropDownOpenedProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<string> SearchFilterProperty =
|
||||
AvaloniaProperty.Register<BranchSelector, string>(nameof(SearchFilter));
|
||||
|
||||
public string SearchFilter
|
||||
{
|
||||
get => GetValue(SearchFilterProperty);
|
||||
set => SetValue(SearchFilterProperty, value);
|
||||
}
|
||||
|
||||
public BranchSelector()
|
||||
{
|
||||
Focusable = true;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||
{
|
||||
base.OnPropertyChanged(change);
|
||||
|
||||
if (change.Property == BranchesProperty || change.Property == SearchFilterProperty)
|
||||
{
|
||||
var branches = Branches;
|
||||
var filter = SearchFilter;
|
||||
if (branches is not { Count: > 0 })
|
||||
{
|
||||
SetCurrentValue(VisibleBranchesProperty, []);
|
||||
}
|
||||
else if (string.IsNullOrEmpty(filter))
|
||||
{
|
||||
SetCurrentValue(VisibleBranchesProperty, Branches);
|
||||
}
|
||||
else
|
||||
{
|
||||
var visible = new List<Models.Branch>();
|
||||
var oldSelection = SelectedBranch;
|
||||
var keepSelection = false;
|
||||
|
||||
foreach (var b in Branches)
|
||||
{
|
||||
if (b.FriendlyName.Contains(SearchFilter, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
visible.Add(b);
|
||||
if (!keepSelection)
|
||||
keepSelection = (b == oldSelection);
|
||||
}
|
||||
}
|
||||
|
||||
SetCurrentValue(VisibleBranchesProperty, visible);
|
||||
if (!keepSelection && visible.Count > 0)
|
||||
SetCurrentValue(SelectedBranchProperty, visible[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
{
|
||||
base.OnApplyTemplate(e);
|
||||
|
||||
if (_popup != null)
|
||||
{
|
||||
_popup.Opened -= OnPopupOpened;
|
||||
_popup.Closed -= OnPopupClosed;
|
||||
}
|
||||
|
||||
_popup = e.NameScope.Get<Popup>("PART_Popup");
|
||||
_popup.Opened += OnPopupOpened;
|
||||
_popup.Closed += OnPopupClosed;
|
||||
}
|
||||
|
||||
protected override void OnKeyDown(KeyEventArgs e)
|
||||
{
|
||||
base.OnKeyDown(e);
|
||||
|
||||
if (e.Key == Key.Space && !IsDropDownOpened)
|
||||
{
|
||||
IsDropDownOpened = true;
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (e.Key == Key.Escape && IsDropDownOpened)
|
||||
{
|
||||
IsDropDownOpened = false;
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPopupOpened(object sender, EventArgs e)
|
||||
{
|
||||
var listBox = _popup?.Child?.FindDescendantOfType<ListBox>();
|
||||
listBox?.Focus();
|
||||
}
|
||||
|
||||
private void OnPopupClosed(object sender, EventArgs e)
|
||||
{
|
||||
Focus(NavigationMethod.Directional);
|
||||
}
|
||||
|
||||
private void OnToggleDropDown(object sender, PointerPressedEventArgs e)
|
||||
{
|
||||
IsDropDownOpened = !IsDropDownOpened;
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void OnSearchBoxKeyDown(object _, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Key.Tab)
|
||||
{
|
||||
var listBox = _popup?.Child?.FindDescendantOfType<ListBox>();
|
||||
listBox?.Focus();
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (e.Key == Key.Up)
|
||||
{
|
||||
var listBox = _popup?.Child?.FindDescendantOfType<ListBox>();
|
||||
if (listBox != null)
|
||||
{
|
||||
if (listBox.SelectedIndex > 0)
|
||||
listBox.SelectedIndex--;
|
||||
listBox.Focus();
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (e.Key == Key.Down)
|
||||
{
|
||||
var listBox = _popup?.Child?.FindDescendantOfType<ListBox>();
|
||||
if (listBox != null)
|
||||
{
|
||||
if (listBox.SelectedIndex < listBox.Items.Count - 1)
|
||||
listBox.SelectedIndex++;
|
||||
listBox.Focus();
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnClearSearchFilter(object sender, RoutedEventArgs e)
|
||||
{
|
||||
SearchFilter = string.Empty;
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void OnDropDownListKeyDown(object _, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Key.Enter)
|
||||
{
|
||||
IsDropDownOpened = false;
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (e.Key == Key.F && e.KeyModifiers == (OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control))
|
||||
{
|
||||
var searchBox = _popup?.Child?.FindDescendantOfType<TextBox>();
|
||||
if (searchBox != null)
|
||||
{
|
||||
searchBox.CaretIndex = SearchFilter?.Length ?? 0;
|
||||
searchBox.Focus();
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDropDownItemPointerPressed(object sender, PointerPressedEventArgs e)
|
||||
{
|
||||
if (sender is Control { DataContext: Models.Branch branch })
|
||||
SelectedBranch = branch;
|
||||
|
||||
IsDropDownOpened = false;
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private Popup _popup = null;
|
||||
}
|
||||
}
|
||||
@@ -534,7 +534,7 @@ namespace SourceGit.Views
|
||||
{
|
||||
var compare = new MenuItem();
|
||||
compare.Header = App.Text("BranchCM.CompareTwo");
|
||||
compare.Icon = App.CreateMenuIcon("Icons.Compare");
|
||||
compare.Icon = this.CreateMenuIcon("Icons.Compare");
|
||||
compare.Click += (_, ev) =>
|
||||
{
|
||||
App.ShowWindow(new ViewModels.Compare(repo, branches[0], branches[1]));
|
||||
@@ -547,7 +547,7 @@ namespace SourceGit.Views
|
||||
{
|
||||
var mergeMulti = new MenuItem();
|
||||
mergeMulti.Header = App.Text("BranchCM.MergeMultiBranches", branches.Count);
|
||||
mergeMulti.Icon = App.CreateMenuIcon("Icons.Merge");
|
||||
mergeMulti.Icon = this.CreateMenuIcon("Icons.Merge");
|
||||
mergeMulti.Click += (_, ev) =>
|
||||
{
|
||||
repo.MergeMultipleBranches(branches);
|
||||
@@ -556,7 +556,7 @@ namespace SourceGit.Views
|
||||
|
||||
var deleteMulti = new MenuItem();
|
||||
deleteMulti.Header = App.Text("BranchCM.DeleteMultiBranches", branches.Count);
|
||||
deleteMulti.Icon = App.CreateMenuIcon("Icons.Clear");
|
||||
deleteMulti.Icon = this.CreateMenuIcon("Icons.Clear");
|
||||
deleteMulti.Click += (_, ev) =>
|
||||
{
|
||||
repo.DeleteMultipleBranches(branches, branches[0].IsLocal);
|
||||
@@ -689,7 +689,7 @@ namespace SourceGit.Views
|
||||
|
||||
var push = new MenuItem();
|
||||
push.Header = App.Text("BranchCM.Push", branch.Name);
|
||||
push.Icon = App.CreateMenuIcon("Icons.Push");
|
||||
push.Icon = this.CreateMenuIcon("Icons.Push");
|
||||
push.IsEnabled = repo.Remotes.Count > 0;
|
||||
push.Click += (_, e) =>
|
||||
{
|
||||
@@ -706,7 +706,7 @@ namespace SourceGit.Views
|
||||
{
|
||||
var fastForward = new MenuItem();
|
||||
fastForward.Header = App.Text("BranchCM.FastForward", upstream.FriendlyName);
|
||||
fastForward.Icon = App.CreateMenuIcon("Icons.FastForward");
|
||||
fastForward.Icon = this.CreateMenuIcon("Icons.FastForward");
|
||||
fastForward.IsEnabled = branch.Ahead.Count == 0 && branch.Behind.Count > 0;
|
||||
fastForward.Click += async (_, e) =>
|
||||
{
|
||||
@@ -717,7 +717,7 @@ namespace SourceGit.Views
|
||||
|
||||
var pull = new MenuItem();
|
||||
pull.Header = App.Text("BranchCM.Pull", upstream.FriendlyName);
|
||||
pull.Icon = App.CreateMenuIcon("Icons.Pull");
|
||||
pull.Icon = this.CreateMenuIcon("Icons.Pull");
|
||||
pull.Click += (_, e) =>
|
||||
{
|
||||
if (repo.CanCreatePopup())
|
||||
@@ -735,7 +735,7 @@ namespace SourceGit.Views
|
||||
|
||||
var compareWith = new MenuItem();
|
||||
compareWith.Header = App.Text("BranchCM.CompareWith");
|
||||
compareWith.Icon = App.CreateMenuIcon("Icons.Compare");
|
||||
compareWith.Icon = this.CreateMenuIcon("Icons.Compare");
|
||||
compareWith.Click += (_, _) =>
|
||||
{
|
||||
new ViewModels.CompareCommandPalette(repo, branch).Open();
|
||||
@@ -749,7 +749,7 @@ namespace SourceGit.Views
|
||||
|
||||
var checkout = new MenuItem();
|
||||
checkout.Header = App.Text(hasNoWorktree ? "BranchCM.Checkout" : "BranchCM.SwitchToWorktree", branch.Name);
|
||||
checkout.Icon = App.CreateMenuIcon("Icons.Check");
|
||||
checkout.Icon = this.CreateMenuIcon("Icons.Check");
|
||||
checkout.IsEnabled = !repo.IsBare || !hasNoWorktree;
|
||||
checkout.Click += async (_, e) =>
|
||||
{
|
||||
@@ -763,7 +763,7 @@ namespace SourceGit.Views
|
||||
{
|
||||
var fastForward = new MenuItem();
|
||||
fastForward.Header = App.Text("BranchCM.FastForward", upstream.FriendlyName);
|
||||
fastForward.Icon = App.CreateMenuIcon("Icons.FastForward");
|
||||
fastForward.Icon = this.CreateMenuIcon("Icons.FastForward");
|
||||
fastForward.IsEnabled = branch.Ahead.Count == 0 && branch.Behind.Count > 0;
|
||||
fastForward.Click += async (_, e) =>
|
||||
{
|
||||
@@ -775,7 +775,7 @@ namespace SourceGit.Views
|
||||
|
||||
var fetchInto = new MenuItem();
|
||||
fetchInto.Header = App.Text("BranchCM.FetchInto", upstream.FriendlyName, branch.Name);
|
||||
fetchInto.Icon = App.CreateMenuIcon("Icons.Fetch");
|
||||
fetchInto.Icon = this.CreateMenuIcon("Icons.Fetch");
|
||||
fetchInto.IsEnabled = branch.Ahead.Count == 0;
|
||||
fetchInto.Click += async (_, e) =>
|
||||
{
|
||||
@@ -794,7 +794,7 @@ namespace SourceGit.Views
|
||||
{
|
||||
var merge = new MenuItem();
|
||||
merge.Header = App.Text("BranchCM.Merge", branch.Name, current.Name);
|
||||
merge.Icon = App.CreateMenuIcon("Icons.Merge");
|
||||
merge.Icon = this.CreateMenuIcon("Icons.Merge");
|
||||
merge.Click += (_, e) =>
|
||||
{
|
||||
if (repo.CanCreatePopup())
|
||||
@@ -804,7 +804,7 @@ namespace SourceGit.Views
|
||||
|
||||
var rebase = new MenuItem();
|
||||
rebase.Header = App.Text("BranchCM.Rebase", current.Name, branch.Name);
|
||||
rebase.Icon = App.CreateMenuIcon("Icons.Rebase");
|
||||
rebase.Icon = this.CreateMenuIcon("Icons.Rebase");
|
||||
rebase.Click += (_, e) =>
|
||||
{
|
||||
if (repo.CanCreatePopup())
|
||||
@@ -814,7 +814,7 @@ namespace SourceGit.Views
|
||||
|
||||
var interactiveRebase = new MenuItem();
|
||||
interactiveRebase.Header = App.Text("BranchCM.InteractiveRebase.Manually", current.Name, branch.Name);
|
||||
interactiveRebase.Icon = App.CreateMenuIcon("Icons.InteractiveRebase");
|
||||
interactiveRebase.Icon = this.CreateMenuIcon("Icons.InteractiveRebase");
|
||||
interactiveRebase.IsEnabled = !current.Head.Equals(branch.Head, StringComparison.Ordinal);
|
||||
interactiveRebase.Click += async (_, e) =>
|
||||
{
|
||||
@@ -836,7 +836,7 @@ namespace SourceGit.Views
|
||||
{
|
||||
var move = new MenuItem();
|
||||
move.Header = App.Text("BranchCM.ResetToSelectedCommit", branch.Name, selectedCommit.SHA.Substring(0, 10));
|
||||
move.Icon = App.CreateMenuIcon("Icons.Reset");
|
||||
move.Icon = this.CreateMenuIcon("Icons.Reset");
|
||||
move.Click += (_, e) =>
|
||||
{
|
||||
if (repo.CanCreatePopup())
|
||||
@@ -850,7 +850,7 @@ namespace SourceGit.Views
|
||||
|
||||
var compareWithCurrent = new MenuItem();
|
||||
compareWithCurrent.Header = App.Text("BranchCM.CompareWithHead");
|
||||
compareWithCurrent.Icon = App.CreateMenuIcon("Icons.Compare");
|
||||
compareWithCurrent.Icon = this.CreateMenuIcon("Icons.Compare");
|
||||
compareWithCurrent.Click += (_, _) =>
|
||||
{
|
||||
App.ShowWindow(new ViewModels.Compare(repo, branch, current));
|
||||
@@ -858,7 +858,7 @@ namespace SourceGit.Views
|
||||
|
||||
var compareWith = new MenuItem();
|
||||
compareWith.Header = App.Text("BranchCM.CompareWith");
|
||||
compareWith.Icon = App.CreateMenuIcon("Icons.Compare");
|
||||
compareWith.Icon = this.CreateMenuIcon("Icons.Compare");
|
||||
compareWith.Click += (_, _) =>
|
||||
{
|
||||
new ViewModels.CompareCommandPalette(repo, branch).Open();
|
||||
@@ -875,7 +875,7 @@ namespace SourceGit.Views
|
||||
{
|
||||
var finish = new MenuItem();
|
||||
finish.Header = App.Text("BranchCM.Finish", branch.Name);
|
||||
finish.Icon = App.CreateMenuIcon("Icons.GitFlow");
|
||||
finish.Icon = this.CreateMenuIcon("Icons.GitFlow");
|
||||
finish.Click += (_, e) =>
|
||||
{
|
||||
if (repo.CanCreatePopup())
|
||||
@@ -891,7 +891,7 @@ namespace SourceGit.Views
|
||||
{
|
||||
var editDescription = new MenuItem();
|
||||
editDescription.Header = App.Text("BranchCM.EditDescription", branch.Name);
|
||||
editDescription.Icon = App.CreateMenuIcon("Icons.Edit");
|
||||
editDescription.Icon = this.CreateMenuIcon("Icons.Edit");
|
||||
editDescription.Click += async (_, e) =>
|
||||
{
|
||||
var desc = await new Commands.Config(repo.FullPath).GetAsync($"branch.{branch.Name}.description");
|
||||
@@ -902,7 +902,7 @@ namespace SourceGit.Views
|
||||
|
||||
var rename = new MenuItem();
|
||||
rename.Header = App.Text("BranchCM.Rename", branch.Name);
|
||||
rename.Icon = App.CreateMenuIcon("Icons.Rename");
|
||||
rename.Icon = this.CreateMenuIcon("Icons.Rename");
|
||||
rename.Click += (_, e) =>
|
||||
{
|
||||
if (repo.CanCreatePopup())
|
||||
@@ -912,7 +912,7 @@ namespace SourceGit.Views
|
||||
|
||||
var delete = new MenuItem();
|
||||
delete.Header = App.Text("BranchCM.Delete", branch.Name);
|
||||
delete.Icon = App.CreateMenuIcon("Icons.Clear");
|
||||
delete.Icon = this.CreateMenuIcon("Icons.Clear");
|
||||
delete.IsEnabled = !branch.IsCurrent;
|
||||
delete.Click += (_, e) =>
|
||||
{
|
||||
@@ -928,7 +928,7 @@ namespace SourceGit.Views
|
||||
}
|
||||
|
||||
var createBranch = new MenuItem();
|
||||
createBranch.Icon = App.CreateMenuIcon("Icons.Branch.Add");
|
||||
createBranch.Icon = this.CreateMenuIcon("Icons.Branch.Add");
|
||||
createBranch.Header = App.Text("CreateBranch");
|
||||
createBranch.Click += (_, e) =>
|
||||
{
|
||||
@@ -938,7 +938,7 @@ namespace SourceGit.Views
|
||||
};
|
||||
|
||||
var createTag = new MenuItem();
|
||||
createTag.Icon = App.CreateMenuIcon("Icons.Tag.Add");
|
||||
createTag.Icon = this.CreateMenuIcon("Icons.Tag.Add");
|
||||
createTag.Header = App.Text("CreateTag");
|
||||
createTag.Click += (_, e) =>
|
||||
{
|
||||
@@ -958,7 +958,7 @@ namespace SourceGit.Views
|
||||
{
|
||||
var createPR = new MenuItem();
|
||||
createPR.Header = App.Text("BranchCM.CreatePRForUpstream", upstream.FriendlyName);
|
||||
createPR.Icon = App.CreateMenuIcon("Icons.CreatePR");
|
||||
createPR.Icon = this.CreateMenuIcon("Icons.CreatePR");
|
||||
createPR.Click += (_, e) =>
|
||||
{
|
||||
Native.OS.OpenBrowser(prURL);
|
||||
@@ -984,7 +984,7 @@ namespace SourceGit.Views
|
||||
{
|
||||
var tracking = new MenuItem();
|
||||
tracking.Header = App.Text("BranchCM.Tracking");
|
||||
tracking.Icon = App.CreateMenuIcon("Icons.Track");
|
||||
tracking.Icon = this.CreateMenuIcon("Icons.Track");
|
||||
tracking.Click += (_, e) =>
|
||||
{
|
||||
if (repo.CanCreatePopup())
|
||||
@@ -996,7 +996,7 @@ namespace SourceGit.Views
|
||||
}
|
||||
|
||||
var archive = new MenuItem();
|
||||
archive.Icon = App.CreateMenuIcon("Icons.Archive");
|
||||
archive.Icon = this.CreateMenuIcon("Icons.Archive");
|
||||
archive.Header = App.Text("Archive");
|
||||
archive.Click += (_, e) =>
|
||||
{
|
||||
@@ -1009,10 +1009,10 @@ namespace SourceGit.Views
|
||||
|
||||
var copy = new MenuItem();
|
||||
copy.Header = App.Text("BranchCM.CopyName");
|
||||
copy.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copy.Icon = this.CreateMenuIcon("Icons.Copy");
|
||||
copy.Click += async (_, e) =>
|
||||
{
|
||||
await App.CopyTextAsync(branch.Name);
|
||||
await this.CopyTextAsync(branch.Name);
|
||||
e.Handled = true;
|
||||
};
|
||||
menu.Items.Add(copy);
|
||||
@@ -1028,7 +1028,7 @@ namespace SourceGit.Views
|
||||
{
|
||||
var visit = new MenuItem();
|
||||
visit.Header = App.Text("RemoteCM.OpenInBrowser");
|
||||
visit.Icon = App.CreateMenuIcon("Icons.OpenWith");
|
||||
visit.Icon = this.CreateMenuIcon("Icons.OpenWith");
|
||||
visit.Click += (_, e) =>
|
||||
{
|
||||
Native.OS.OpenBrowser(visitURL);
|
||||
@@ -1041,7 +1041,7 @@ namespace SourceGit.Views
|
||||
|
||||
var fetch = new MenuItem();
|
||||
fetch.Header = App.Text("RemoteCM.Fetch");
|
||||
fetch.Icon = App.CreateMenuIcon("Icons.Fetch");
|
||||
fetch.Icon = this.CreateMenuIcon("Icons.Fetch");
|
||||
fetch.Click += (_, e) =>
|
||||
{
|
||||
if (repo.CanCreatePopup())
|
||||
@@ -1051,7 +1051,7 @@ namespace SourceGit.Views
|
||||
|
||||
var prune = new MenuItem();
|
||||
prune.Header = App.Text("RemoteCM.Prune");
|
||||
prune.Icon = App.CreateMenuIcon("Icons.Clean");
|
||||
prune.Icon = this.CreateMenuIcon("Icons.Clean");
|
||||
prune.Click += async (_, e) =>
|
||||
{
|
||||
if (repo.CanCreatePopup())
|
||||
@@ -1061,7 +1061,7 @@ namespace SourceGit.Views
|
||||
|
||||
var edit = new MenuItem();
|
||||
edit.Header = App.Text("RemoteCM.Edit");
|
||||
edit.Icon = App.CreateMenuIcon("Icons.Edit");
|
||||
edit.Icon = this.CreateMenuIcon("Icons.Edit");
|
||||
edit.Click += (_, e) =>
|
||||
{
|
||||
if (repo.CanCreatePopup())
|
||||
@@ -1071,7 +1071,7 @@ namespace SourceGit.Views
|
||||
|
||||
var delete = new MenuItem();
|
||||
delete.Header = App.Text("RemoteCM.Delete");
|
||||
delete.Icon = App.CreateMenuIcon("Icons.Clear");
|
||||
delete.Icon = this.CreateMenuIcon("Icons.Clear");
|
||||
delete.Click += (_, e) =>
|
||||
{
|
||||
if (repo.CanCreatePopup())
|
||||
@@ -1081,10 +1081,10 @@ namespace SourceGit.Views
|
||||
|
||||
var copy = new MenuItem();
|
||||
copy.Header = App.Text("RemoteCM.CopyURL");
|
||||
copy.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copy.Icon = this.CreateMenuIcon("Icons.Copy");
|
||||
copy.Click += async (_, e) =>
|
||||
{
|
||||
await App.CopyTextAsync(remote.URL);
|
||||
await this.CopyTextAsync(remote.URL);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
@@ -1106,7 +1106,7 @@ namespace SourceGit.Views
|
||||
|
||||
var checkout = new MenuItem();
|
||||
checkout.Header = App.Text("BranchCM.Checkout", name);
|
||||
checkout.Icon = App.CreateMenuIcon("Icons.Check");
|
||||
checkout.Icon = this.CreateMenuIcon("Icons.Check");
|
||||
checkout.Click += async (_, e) =>
|
||||
{
|
||||
await repo.CheckoutBranchAsync(branch);
|
||||
@@ -1119,7 +1119,7 @@ namespace SourceGit.Views
|
||||
{
|
||||
var pull = new MenuItem();
|
||||
pull.Header = App.Text("BranchCM.PullInto", name, current.Name);
|
||||
pull.Icon = App.CreateMenuIcon("Icons.Pull");
|
||||
pull.Icon = this.CreateMenuIcon("Icons.Pull");
|
||||
pull.Click += (_, e) =>
|
||||
{
|
||||
if (repo.CanCreatePopup())
|
||||
@@ -1129,7 +1129,7 @@ namespace SourceGit.Views
|
||||
|
||||
var merge = new MenuItem();
|
||||
merge.Header = App.Text("BranchCM.Merge", name, current.Name);
|
||||
merge.Icon = App.CreateMenuIcon("Icons.Merge");
|
||||
merge.Icon = this.CreateMenuIcon("Icons.Merge");
|
||||
merge.Click += (_, e) =>
|
||||
{
|
||||
if (repo.CanCreatePopup())
|
||||
@@ -1139,7 +1139,7 @@ namespace SourceGit.Views
|
||||
|
||||
var rebase = new MenuItem();
|
||||
rebase.Header = App.Text("BranchCM.Rebase", current.Name, name);
|
||||
rebase.Icon = App.CreateMenuIcon("Icons.Rebase");
|
||||
rebase.Icon = this.CreateMenuIcon("Icons.Rebase");
|
||||
rebase.Click += (_, e) =>
|
||||
{
|
||||
if (repo.CanCreatePopup())
|
||||
@@ -1149,7 +1149,7 @@ namespace SourceGit.Views
|
||||
|
||||
var interactiveRebase = new MenuItem();
|
||||
interactiveRebase.Header = App.Text("BranchCM.InteractiveRebase.Manually", current.Name, name);
|
||||
interactiveRebase.Icon = App.CreateMenuIcon("Icons.InteractiveRebase");
|
||||
interactiveRebase.Icon = this.CreateMenuIcon("Icons.InteractiveRebase");
|
||||
interactiveRebase.IsEnabled = !current.Head.Equals(branch.Head, StringComparison.Ordinal);
|
||||
interactiveRebase.Click += async (_, e) =>
|
||||
{
|
||||
@@ -1160,7 +1160,7 @@ namespace SourceGit.Views
|
||||
|
||||
var compareWithHead = new MenuItem();
|
||||
compareWithHead.Header = App.Text("BranchCM.CompareWithHead");
|
||||
compareWithHead.Icon = App.CreateMenuIcon("Icons.Compare");
|
||||
compareWithHead.Icon = this.CreateMenuIcon("Icons.Compare");
|
||||
compareWithHead.Click += (_, _) =>
|
||||
{
|
||||
App.ShowWindow(new ViewModels.Compare(repo, branch, current));
|
||||
@@ -1168,7 +1168,7 @@ namespace SourceGit.Views
|
||||
|
||||
var compareWith = new MenuItem();
|
||||
compareWith.Header = App.Text("BranchCM.CompareWith");
|
||||
compareWith.Icon = App.CreateMenuIcon("Icons.Compare");
|
||||
compareWith.Icon = this.CreateMenuIcon("Icons.Compare");
|
||||
compareWith.Click += (_, _) =>
|
||||
{
|
||||
new ViewModels.CompareCommandPalette(repo, branch).Open();
|
||||
@@ -1188,7 +1188,7 @@ namespace SourceGit.Views
|
||||
|
||||
var editDescription = new MenuItem();
|
||||
editDescription.Header = App.Text("BranchCM.EditDescription", branch.Name);
|
||||
editDescription.Icon = App.CreateMenuIcon("Icons.Edit");
|
||||
editDescription.Icon = this.CreateMenuIcon("Icons.Edit");
|
||||
editDescription.Click += async (_, e) =>
|
||||
{
|
||||
var desc = await new Commands.Config(repo.FullPath).GetAsync($"branch.{branch.Name}.description");
|
||||
@@ -1199,7 +1199,7 @@ namespace SourceGit.Views
|
||||
|
||||
var delete = new MenuItem();
|
||||
delete.Header = App.Text("BranchCM.Delete", name);
|
||||
delete.Icon = App.CreateMenuIcon("Icons.Clear");
|
||||
delete.Icon = this.CreateMenuIcon("Icons.Clear");
|
||||
delete.Click += (_, e) =>
|
||||
{
|
||||
if (repo.CanCreatePopup())
|
||||
@@ -1212,7 +1212,7 @@ namespace SourceGit.Views
|
||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||
|
||||
var createBranch = new MenuItem();
|
||||
createBranch.Icon = App.CreateMenuIcon("Icons.Branch.Add");
|
||||
createBranch.Icon = this.CreateMenuIcon("Icons.Branch.Add");
|
||||
createBranch.Header = App.Text("CreateBranch");
|
||||
createBranch.Click += (_, e) =>
|
||||
{
|
||||
@@ -1222,7 +1222,7 @@ namespace SourceGit.Views
|
||||
};
|
||||
|
||||
var createTag = new MenuItem();
|
||||
createTag.Icon = App.CreateMenuIcon("Icons.Tag.Add");
|
||||
createTag.Icon = this.CreateMenuIcon("Icons.Tag.Add");
|
||||
createTag.Header = App.Text("CreateTag");
|
||||
createTag.Click += (_, e) =>
|
||||
{
|
||||
@@ -1239,7 +1239,7 @@ namespace SourceGit.Views
|
||||
{
|
||||
var createPR = new MenuItem();
|
||||
createPR.Header = App.Text("BranchCM.CreatePR");
|
||||
createPR.Icon = App.CreateMenuIcon("Icons.CreatePR");
|
||||
createPR.Icon = this.CreateMenuIcon("Icons.CreatePR");
|
||||
createPR.Click += (_, e) =>
|
||||
{
|
||||
Native.OS.OpenBrowser(prURL);
|
||||
@@ -1252,7 +1252,7 @@ namespace SourceGit.Views
|
||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||
|
||||
var archive = new MenuItem();
|
||||
archive.Icon = App.CreateMenuIcon("Icons.Archive");
|
||||
archive.Icon = this.CreateMenuIcon("Icons.Archive");
|
||||
archive.Header = App.Text("Archive");
|
||||
archive.Click += (_, e) =>
|
||||
{
|
||||
@@ -1263,10 +1263,10 @@ namespace SourceGit.Views
|
||||
|
||||
var copy = new MenuItem();
|
||||
copy.Header = App.Text("BranchCM.CopyName");
|
||||
copy.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copy.Icon = this.CreateMenuIcon("Icons.Copy");
|
||||
copy.Click += async (_, e) =>
|
||||
{
|
||||
await App.CopyTextAsync(name);
|
||||
await this.CopyTextAsync(name);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
@@ -1285,13 +1285,13 @@ namespace SourceGit.Views
|
||||
|
||||
var custom = new MenuItem();
|
||||
custom.Header = App.Text("BranchCM.CustomAction");
|
||||
custom.Icon = App.CreateMenuIcon("Icons.Action");
|
||||
custom.Icon = this.CreateMenuIcon("Icons.Action");
|
||||
|
||||
foreach (var action in actions)
|
||||
{
|
||||
var (dup, label) = action;
|
||||
var item = new MenuItem();
|
||||
item.Icon = App.CreateMenuIcon("Icons.Action");
|
||||
item.Icon = this.CreateMenuIcon("Icons.Action");
|
||||
item.Header = label;
|
||||
item.Click += async (_, e) =>
|
||||
{
|
||||
@@ -1314,13 +1314,13 @@ namespace SourceGit.Views
|
||||
|
||||
var custom = new MenuItem();
|
||||
custom.Header = App.Text("RemoteCM.CustomAction");
|
||||
custom.Icon = App.CreateMenuIcon("Icons.Action");
|
||||
custom.Icon = this.CreateMenuIcon("Icons.Action");
|
||||
|
||||
foreach (var action in actions)
|
||||
{
|
||||
var (dup, label) = action;
|
||||
var item = new MenuItem();
|
||||
item.Icon = App.CreateMenuIcon("Icons.Action");
|
||||
item.Icon = this.CreateMenuIcon("Icons.Action");
|
||||
item.Header = label;
|
||||
item.Click += async (_, e) =>
|
||||
{
|
||||
|
||||
@@ -37,38 +37,43 @@
|
||||
SelectionChanged="OnRowSelectionChanged">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate DataType="vm:ChangeTreeNode">
|
||||
<Grid ColumnDefinitions="16,16,Auto,*"
|
||||
Margin="{Binding Depth, Converter={x:Static c:IntConverters.ToTreeMargin}}"
|
||||
Background="Transparent"
|
||||
DoubleTapped="OnRowDoubleTapped"
|
||||
DataContextChanged="OnRowDataContextChanged">
|
||||
<v:ChangeTreeNodeToggleButton Grid.Column="0"
|
||||
Classes="tree_expander"
|
||||
Focusable="False"
|
||||
HorizontalAlignment="Center"
|
||||
IsChecked="{Binding IsExpanded, Mode=OneWay}"
|
||||
IsVisible="{Binding IsFolder}"/>
|
||||
|
||||
<ToggleButton Grid.Column="1"
|
||||
Classes="folder"
|
||||
<StackPanel Orientation="Horizontal"
|
||||
Height="24"
|
||||
Margin="{Binding Depth, Converter={x:Static c:IntConverters.ToTreeMargin}}"
|
||||
Background="Transparent"
|
||||
DoubleTapped="OnRowDoubleTapped"
|
||||
DataContextChanged="OnRowDataContextChanged">
|
||||
<Border Width="16">
|
||||
<v:ChangeTreeNodeToggleButton Classes="tree_expander"
|
||||
Focusable="False"
|
||||
HorizontalAlignment="Center"
|
||||
IsChecked="{Binding IsExpanded, Mode=OneWay}"
|
||||
IsVisible="{Binding IsFolder}"/>
|
||||
</Border>
|
||||
|
||||
<ToggleButton Classes="folder"
|
||||
Focusable="False"
|
||||
Width="14" Height="14"
|
||||
Margin="0,1,0,0"
|
||||
Margin="1,1,1,0"
|
||||
Foreground="Goldenrod"
|
||||
IsChecked="{Binding IsExpanded}"
|
||||
IsVisible="{Binding IsFolder}"/>
|
||||
|
||||
<v:ChangeStatusIcon Grid.Column="1"
|
||||
Width="14" Height="14"
|
||||
<v:ChangeStatusIcon Width="14" Height="14"
|
||||
Margin="1,0"
|
||||
IsUnstagedChange="{Binding #ThisControl.IsUnstagedChange}"
|
||||
Change="{Binding Change}"
|
||||
IsVisible="{Binding !IsFolder}"/>
|
||||
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal" Margin="4,0,0,0">
|
||||
<TextBlock Text="{Binding ConflictMarker, Mode=OneWay}" Foreground="DarkOrange" FontWeight="Bold" Margin="0,0,4,0" IsVisible="{Binding ShowConflictMarker}"/>
|
||||
<TextBlock Text="{Binding DisplayName, Mode=OneWay}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<TextBlock Text="{Binding ConflictMarker, Mode=OneWay}"
|
||||
Foreground="DarkOrange"
|
||||
FontWeight="Bold"
|
||||
Margin="4,0,0,0"
|
||||
IsVisible="{Binding ShowConflictMarker}"/>
|
||||
|
||||
<TextBlock Text="{Binding DisplayName, Mode=OneWay}"
|
||||
Margin="4,0,0,0"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</v:ChangeCollectionContainer>
|
||||
@@ -82,25 +87,28 @@
|
||||
SelectionChanged="OnRowSelectionChanged">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate DataType="m:Change">
|
||||
<Grid ColumnDefinitions="Auto,Auto,Auto,*"
|
||||
Background="Transparent"
|
||||
DoubleTapped="OnRowDoubleTapped"
|
||||
DataContextChanged="OnRowDataContextChanged">
|
||||
<v:ChangeStatusIcon Grid.Column="0"
|
||||
Width="14" Height="14"
|
||||
<StackPanel Orientation="Horizontal"
|
||||
Height="24"
|
||||
Background="Transparent"
|
||||
DoubleTapped="OnRowDoubleTapped"
|
||||
DataContextChanged="OnRowDataContextChanged">
|
||||
<v:ChangeStatusIcon Width="14" Height="14"
|
||||
Margin="4,0,0,0"
|
||||
IsUnstagedChange="{Binding #ThisControl.IsUnstagedChange}"
|
||||
Change="{Binding}" />
|
||||
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal" Margin="4,0">
|
||||
<TextBlock Text="{Binding ConflictMarker}" Foreground="DarkOrange" FontWeight="Bold" Margin="0,0,4,0" IsVisible="{Binding IsConflicted}"/>
|
||||
<TextBlock Text="{Binding Path, Converter={x:Static c:PathConverters.PureFileName}}"/>
|
||||
</StackPanel>
|
||||
<TextBlock Text="{Binding ConflictMarker}"
|
||||
Foreground="DarkOrange"
|
||||
FontWeight="Bold"
|
||||
Margin="4,0,0,0" IsVisible="{Binding IsConflicted}"/>
|
||||
|
||||
<TextBlock Text="{Binding Path, Converter={x:Static c:PathConverters.PureFileName}}"
|
||||
Margin="4,0,0,0"/>
|
||||
|
||||
<TextBlock Grid.Column="2"
|
||||
Text="{Binding Path, Converter={x:Static c:PathConverters.PureDirectoryName}}"
|
||||
Foreground="{DynamicResource Brush.FG2}"/>
|
||||
</Grid>
|
||||
<TextBlock Text="{Binding Path, Converter={x:Static c:PathConverters.PureDirectoryName}}"
|
||||
Foreground="{DynamicResource Brush.FG2}"
|
||||
Margin="4,0,0,0"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</v:ChangeCollectionContainer>
|
||||
@@ -114,21 +122,25 @@
|
||||
SelectionChanged="OnRowSelectionChanged">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate DataType="m:Change">
|
||||
<Grid ColumnDefinitions="Auto,Auto,*"
|
||||
Background="Transparent"
|
||||
DoubleTapped="OnRowDoubleTapped"
|
||||
DataContextChanged="OnRowDataContextChanged">
|
||||
<v:ChangeStatusIcon Grid.Column="0"
|
||||
Width="14" Height="14"
|
||||
<StackPanel Orientation="Horizontal"
|
||||
Height="24"
|
||||
Background="Transparent"
|
||||
DoubleTapped="OnRowDoubleTapped"
|
||||
DataContextChanged="OnRowDataContextChanged">
|
||||
<v:ChangeStatusIcon Width="14" Height="14"
|
||||
Margin="4,0,0,0"
|
||||
IsUnstagedChange="{Binding #ThisControl.IsUnstagedChange}"
|
||||
Change="{Binding}" />
|
||||
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal" Margin="4,0">
|
||||
<TextBlock Text="{Binding ConflictMarker}" Foreground="DarkOrange" FontWeight="Bold" Margin="0,0,4,0" IsVisible="{Binding IsConflicted}"/>
|
||||
<TextBlock Text="{Binding Path}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<TextBlock Text="{Binding ConflictMarker}"
|
||||
Foreground="DarkOrange"
|
||||
FontWeight="Bold"
|
||||
Margin="4,0,0,0"
|
||||
IsVisible="{Binding IsConflicted}"/>
|
||||
|
||||
<TextBlock Margin="4,0,0,0"
|
||||
Text="{Binding Path}"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</v:ChangeCollectionContainer>
|
||||
|
||||
@@ -231,17 +231,17 @@ namespace SourceGit.Views
|
||||
|
||||
private void OnRowDataContextChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (sender is not Control control)
|
||||
if (sender is not Control { DataContext: { } ctx } control)
|
||||
return;
|
||||
|
||||
if (control.DataContext is ViewModels.ChangeTreeNode node)
|
||||
if (ctx is ViewModels.ChangeTreeNode node)
|
||||
{
|
||||
if (node.Change is { } c)
|
||||
UpdateRowTips(control, c);
|
||||
else
|
||||
ToolTip.SetTip(control, node.FullPath);
|
||||
}
|
||||
else if (control.DataContext is Models.Change change)
|
||||
else if (ctx is Models.Change change)
|
||||
{
|
||||
UpdateRowTips(control, change);
|
||||
}
|
||||
@@ -253,8 +253,10 @@ namespace SourceGit.Views
|
||||
|
||||
private void OnRowDoubleTapped(object sender, TappedEventArgs e)
|
||||
{
|
||||
var grid = sender as Grid;
|
||||
if (grid?.DataContext is ViewModels.ChangeTreeNode node)
|
||||
if (sender is not Control { DataContext: { } ctx })
|
||||
return;
|
||||
|
||||
if (ctx is ViewModels.ChangeTreeNode node)
|
||||
{
|
||||
if (node.IsFolder)
|
||||
{
|
||||
@@ -269,7 +271,7 @@ namespace SourceGit.Views
|
||||
RaiseEvent(new RoutedEventArgs(ChangeDoubleTappedEvent));
|
||||
}
|
||||
}
|
||||
else if (grid?.DataContext is Models.Change)
|
||||
else if (ctx is Models.Change)
|
||||
{
|
||||
RaiseEvent(new RoutedEventArgs(ChangeDoubleTappedEvent));
|
||||
}
|
||||
|
||||
48
src/Views/CheckoutBranchFromStash.axaml
Normal file
48
src/Views/CheckoutBranchFromStash.axaml
Normal file
@@ -0,0 +1,48 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="using:SourceGit.ViewModels"
|
||||
xmlns:v="using:SourceGit.Views"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="SourceGit.Views.CheckoutBranchFromStash"
|
||||
x:DataType="vm:CheckoutBranchFromStash">
|
||||
<StackPanel Orientation="Vertical" Margin="8,0,0,0">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Path Width="16" Height="16"
|
||||
Data="{StaticResource Icons.Branch.Add}"/>
|
||||
|
||||
<TextBlock FontSize="18"
|
||||
Margin="8,0,0,0"
|
||||
Classes="bold"
|
||||
Text="{DynamicResource Text.CheckoutBranchFromStash}"/>
|
||||
</StackPanel>
|
||||
|
||||
<Grid Margin="0,16,8,0" RowDefinitions="32,32" ColumnDefinitions="120,*">
|
||||
<TextBlock Grid.Row="0" Grid.Column="0"
|
||||
HorizontalAlignment="Right" VerticalAlignment="Center"
|
||||
Margin="0,0,8,0"
|
||||
Text="{DynamicResource Text.CheckoutBranchFromStash.Stash}"/>
|
||||
<Grid Grid.Row="0" Grid.Column="1" ColumnDefinitions="Auto,Auto,*">
|
||||
<Path Grid.Column="0"
|
||||
Width="12" Height="12"
|
||||
Margin="2,0,8,0"
|
||||
HorizontalAlignment="Left" VerticalAlignment="Center"
|
||||
Data="{StaticResource Icons.Stashes}"/>
|
||||
|
||||
<TextBlock Grid.Column="1" VerticalAlignment="Center" Text="{Binding Target.Name}" Foreground="DarkOrange"/>
|
||||
<TextBlock Grid.Column="2" VerticalAlignment="Center" Text="{Binding Target.Subject}" TextTrimming="CharacterEllipsis" Margin="4,0,0,0"/>
|
||||
</Grid>
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0"
|
||||
HorizontalAlignment="Right" VerticalAlignment="Center"
|
||||
Margin="0,0,8,0"
|
||||
Text="{DynamicResource Text.CheckoutBranchFromStash.Branch}"/>
|
||||
<v:BranchOrTagNameTextBox Grid.Row="1" Grid.Column="1"
|
||||
Height="26"
|
||||
VerticalAlignment="Center"
|
||||
CornerRadius="3"
|
||||
Text="{Binding BranchName, Mode=TwoWay}"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
12
src/Views/CheckoutBranchFromStash.axaml.cs
Normal file
12
src/Views/CheckoutBranchFromStash.axaml.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace SourceGit.Views
|
||||
{
|
||||
public partial class CheckoutBranchFromStash : UserControl
|
||||
{
|
||||
public CheckoutBranchFromStash()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input.Platform;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Platform.Storage;
|
||||
|
||||
@@ -12,6 +13,29 @@ namespace SourceGit.Views
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override async void OnLoaded(RoutedEventArgs e)
|
||||
{
|
||||
base.OnLoaded(e);
|
||||
|
||||
if (DataContext is not ViewModels.Clone vm)
|
||||
return;
|
||||
|
||||
var clipboard = TopLevel.GetTopLevel(this)?.Clipboard;
|
||||
if (clipboard != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var text = await clipboard.TryGetTextAsync();
|
||||
if (Models.Remote.IsValidURL(text))
|
||||
vm.Remote = text;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore exceptions here.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void SelectParentFolder(object _, RoutedEventArgs e)
|
||||
{
|
||||
var options = new FolderPickerOpenOptions() { AllowMultiple = false };
|
||||
@@ -31,7 +55,7 @@ namespace SourceGit.Views
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
App.RaiseException(string.Empty, $"Failed to select parent folder: {exception.Message}");
|
||||
Models.Notification.Send(null, $"Failed to select parent folder: {exception.Message}", true);
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace SourceGit.Views
|
||||
{
|
||||
public class CommandPaletteDataTemplates : IDataTemplate
|
||||
{
|
||||
public Control Build(object param) => App.CreateViewForViewModel(param);
|
||||
public Control Build(object param) => ControlExtensions.CreateFromViewModels(param);
|
||||
public bool Match(object data) => data is ViewModels.ICommandPalette;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,6 +203,7 @@
|
||||
<TextBlock Grid.Row="4" Grid.Column="0" Classes="info_label" VerticalAlignment="Top" Margin="0,4,0,0" Text="{DynamicResource Text.CommitDetail.Info.Message}" />
|
||||
<v:CommitMessagePresenter Grid.Row="4" Grid.Column="1"
|
||||
Margin="12,4,0,0"
|
||||
Foreground="{DynamicResource Brush.FG1}"
|
||||
FullMessage="{Binding #ThisControl.FullMessage}"
|
||||
HorizontalAlignment="Stretch"
|
||||
TextWrapping="Wrap">
|
||||
@@ -213,6 +214,26 @@
|
||||
</MultiBinding>
|
||||
</ToolTip.IsOpen>
|
||||
|
||||
<v:CommitMessagePresenter.ContextFlyout>
|
||||
<MenuFlyout Placement="Bottom">
|
||||
<MenuItem Header="{DynamicResource Text.Copy}"
|
||||
Command="{Binding $parent[SelectableTextBlock].Copy}"
|
||||
IsEnabled="{Binding $parent[SelectableTextBlock].CanCopy}"
|
||||
InputGesture="{x:Static TextBox.CopyGesture}">
|
||||
<MenuItem.Icon>
|
||||
<Path Width="11" Height="11" Data="{StaticResource Icons.Copy}" VerticalAlignment="Center"/>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem Header="{DynamicResource Text.CopyAllText}"
|
||||
Click="OnCopyAllCommitMessage">
|
||||
<MenuItem.Icon>
|
||||
<Path Width="11" Height="11" Data="{StaticResource Icons.Copy}" VerticalAlignment="Center"/>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
</MenuFlyout>
|
||||
</v:CommitMessagePresenter.ContextFlyout>
|
||||
|
||||
<v:CommitMessagePresenter.DataTemplates>
|
||||
<DataTemplate DataType="m:Commit">
|
||||
<StackPanel MinWidth="400" Orientation="Vertical">
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace SourceGit.Views
|
||||
private async void OnCopyCommitSHA(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is Button { DataContext: Models.Commit commit })
|
||||
await App.CopyTextAsync(commit.SHA);
|
||||
await this.CopyTextAsync(commit.SHA);
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
@@ -151,28 +151,28 @@ namespace SourceGit.Views
|
||||
|
||||
var copyName = new MenuItem();
|
||||
copyName.Header = App.Text("CommitDetail.Info.CopyName");
|
||||
copyName.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyName.Icon = this.CreateMenuIcon("Icons.Copy");
|
||||
copyName.Click += async (_, ev) =>
|
||||
{
|
||||
await App.CopyTextAsync(user.Name);
|
||||
await this.CopyTextAsync(user.Name);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var copyEmail = new MenuItem();
|
||||
copyEmail.Header = App.Text("CommitDetail.Info.CopyEmail");
|
||||
copyEmail.Icon = App.CreateMenuIcon("Icons.Email");
|
||||
copyEmail.Icon = this.CreateMenuIcon("Icons.Email");
|
||||
copyEmail.Click += async (_, ev) =>
|
||||
{
|
||||
await App.CopyTextAsync(user.Email);
|
||||
await this.CopyTextAsync(user.Email);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var copyUser = new MenuItem();
|
||||
copyUser.Header = App.Text("CommitDetail.Info.CopyNameAndEmail");
|
||||
copyUser.Icon = App.CreateMenuIcon("Icons.User");
|
||||
copyUser.Icon = this.CreateMenuIcon("Icons.User");
|
||||
copyUser.Click += async (_, ev) =>
|
||||
{
|
||||
await App.CopyTextAsync(user.ToString());
|
||||
await this.CopyTextAsync(user.ToString());
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
@@ -183,5 +183,12 @@ namespace SourceGit.Views
|
||||
menu.Open(control);
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private async void OnCopyAllCommitMessage(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (DataContext is ViewModels.CommitDetail detail)
|
||||
await this.CopyTextAsync(detail.FullMessage.Message);
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace SourceGit.Views
|
||||
builder.AppendLine(copyAbsPath ? vm.GetAbsPath(c.Path) : c.Path);
|
||||
}
|
||||
|
||||
await App.CopyTextAsync(builder.ToString());
|
||||
await this.CopyTextAsync(builder.ToString());
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (e.Key == Key.F && e.KeyModifiers == cmdKey)
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace SourceGit.Views
|
||||
var fullPath = Native.OS.GetAbsPath(repo.FullPath, node.FullPath);
|
||||
var explore = new MenuItem();
|
||||
explore.Header = App.Text("RevealFile");
|
||||
explore.Icon = App.CreateMenuIcon("Icons.Explore");
|
||||
explore.Icon = this.CreateMenuIcon("Icons.Explore");
|
||||
explore.IsEnabled = Directory.Exists(fullPath);
|
||||
explore.Click += (_, ev) =>
|
||||
{
|
||||
@@ -35,7 +35,7 @@ namespace SourceGit.Views
|
||||
|
||||
var history = new MenuItem();
|
||||
history.Header = App.Text("DirHistories");
|
||||
history.Icon = App.CreateMenuIcon("Icons.Histories");
|
||||
history.Icon = this.CreateMenuIcon("Icons.Histories");
|
||||
history.Click += (_, ev) =>
|
||||
{
|
||||
App.ShowWindow(new ViewModels.DirHistories(repo, node.FullPath, commit.SHA));
|
||||
@@ -44,7 +44,7 @@ namespace SourceGit.Views
|
||||
|
||||
var patch = new MenuItem();
|
||||
patch.Header = App.Text("FileCM.SaveAsPatch");
|
||||
patch.Icon = App.CreateMenuIcon("Icons.Save");
|
||||
patch.Icon = this.CreateMenuIcon("Icons.Save");
|
||||
patch.Click += async (_, e) =>
|
||||
{
|
||||
var storageProvider = TopLevel.GetTopLevel(this)?.StorageProvider;
|
||||
@@ -67,7 +67,7 @@ namespace SourceGit.Views
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
App.RaiseException(repo.FullPath, $"Failed to save as patch: {exception.Message}");
|
||||
repo.SendNotification($"Failed to save as patch: {exception.Message}", true);
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
@@ -75,21 +75,21 @@ namespace SourceGit.Views
|
||||
|
||||
var copyPath = new MenuItem();
|
||||
copyPath.Header = App.Text("CopyPath");
|
||||
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyPath.Icon = this.CreateMenuIcon("Icons.Copy");
|
||||
copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C";
|
||||
copyPath.Click += async (_, ev) =>
|
||||
{
|
||||
await App.CopyTextAsync(node.FullPath);
|
||||
await this.CopyTextAsync(node.FullPath);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var copyFullPath = new MenuItem();
|
||||
copyFullPath.Header = App.Text("CopyFullPath");
|
||||
copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyFullPath.Icon = this.CreateMenuIcon("Icons.Copy");
|
||||
copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C";
|
||||
copyFullPath.Click += async (_, e) =>
|
||||
{
|
||||
await App.CopyTextAsync(fullPath);
|
||||
await this.CopyTextAsync(fullPath);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
@@ -112,7 +112,7 @@ namespace SourceGit.Views
|
||||
|
||||
var patch = new MenuItem();
|
||||
patch.Header = App.Text("FileCM.SaveAsPatch");
|
||||
patch.Icon = App.CreateMenuIcon("Icons.Save");
|
||||
patch.Icon = this.CreateMenuIcon("Icons.Save");
|
||||
patch.Click += async (_, e) =>
|
||||
{
|
||||
var storageProvider = TopLevel.GetTopLevel(this)?.StorageProvider;
|
||||
@@ -135,7 +135,7 @@ namespace SourceGit.Views
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
App.RaiseException(repo.FullPath, $"Failed to save as patch: {exception.Message}");
|
||||
repo.SendNotification($"Failed to save as patch: {exception.Message}", true);
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
@@ -149,7 +149,7 @@ namespace SourceGit.Views
|
||||
{
|
||||
var resetToThisRevision = new MenuItem();
|
||||
resetToThisRevision.Header = App.Text("ChangeCM.CheckoutThisRevision");
|
||||
resetToThisRevision.Icon = App.CreateMenuIcon("Icons.File.Checkout");
|
||||
resetToThisRevision.Icon = this.CreateMenuIcon("Icons.File.Checkout");
|
||||
resetToThisRevision.Click += async (_, ev) =>
|
||||
{
|
||||
await vm.ResetMultipleToThisRevisionAsync(changes);
|
||||
@@ -158,7 +158,7 @@ namespace SourceGit.Views
|
||||
|
||||
var resetToFirstParent = new MenuItem();
|
||||
resetToFirstParent.Header = App.Text("ChangeCM.CheckoutFirstParentRevision");
|
||||
resetToFirstParent.Icon = App.CreateMenuIcon("Icons.File.Checkout");
|
||||
resetToFirstParent.Icon = this.CreateMenuIcon("Icons.File.Checkout");
|
||||
resetToFirstParent.IsEnabled = commit.Parents.Count > 0;
|
||||
resetToFirstParent.Click += async (_, ev) =>
|
||||
{
|
||||
@@ -173,7 +173,7 @@ namespace SourceGit.Views
|
||||
|
||||
var copyPath = new MenuItem();
|
||||
copyPath.Header = App.Text("CopyPath");
|
||||
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyPath.Icon = this.CreateMenuIcon("Icons.Copy");
|
||||
copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C";
|
||||
copyPath.Click += async (_, ev) =>
|
||||
{
|
||||
@@ -181,13 +181,13 @@ namespace SourceGit.Views
|
||||
foreach (var c in changes)
|
||||
builder.AppendLine(c.Path);
|
||||
|
||||
await App.CopyTextAsync(builder.ToString());
|
||||
await this.CopyTextAsync(builder.ToString());
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var copyFullPath = new MenuItem();
|
||||
copyFullPath.Header = App.Text("CopyFullPath");
|
||||
copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyFullPath.Icon = this.CreateMenuIcon("Icons.Copy");
|
||||
copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C";
|
||||
copyFullPath.Click += async (_, e) =>
|
||||
{
|
||||
@@ -195,7 +195,7 @@ namespace SourceGit.Views
|
||||
foreach (var c in changes)
|
||||
builder.AppendLine(Native.OS.GetAbsPath(repo.FullPath, c.Path));
|
||||
|
||||
await App.CopyTextAsync(builder.ToString());
|
||||
await this.CopyTextAsync(builder.ToString());
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
@@ -211,7 +211,7 @@ namespace SourceGit.Views
|
||||
|
||||
var openWith = new MenuItem();
|
||||
openWith.Header = App.Text("Open");
|
||||
openWith.Icon = App.CreateMenuIcon("Icons.OpenWith");
|
||||
openWith.Icon = this.CreateMenuIcon("Icons.OpenWith");
|
||||
openWith.IsEnabled = change.Index != Models.ChangeState.Deleted;
|
||||
if (openWith.IsEnabled)
|
||||
{
|
||||
@@ -249,7 +249,7 @@ namespace SourceGit.Views
|
||||
|
||||
var openWithMerger = new MenuItem();
|
||||
openWithMerger.Header = App.Text("OpenInExternalMergeTool");
|
||||
openWithMerger.Icon = App.CreateMenuIcon("Icons.OpenWith");
|
||||
openWithMerger.Icon = this.CreateMenuIcon("Icons.OpenWith");
|
||||
openWithMerger.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+D" : "Ctrl+Shift+D";
|
||||
openWithMerger.Click += (_, ev) =>
|
||||
{
|
||||
@@ -260,7 +260,7 @@ namespace SourceGit.Views
|
||||
var fullPath = Native.OS.GetAbsPath(repo.FullPath, change.Path);
|
||||
var explore = new MenuItem();
|
||||
explore.Header = App.Text("RevealFile");
|
||||
explore.Icon = App.CreateMenuIcon("Icons.Explore");
|
||||
explore.Icon = this.CreateMenuIcon("Icons.Explore");
|
||||
explore.IsEnabled = File.Exists(fullPath);
|
||||
explore.Click += (_, ev) =>
|
||||
{
|
||||
@@ -270,7 +270,7 @@ namespace SourceGit.Views
|
||||
|
||||
var history = new MenuItem();
|
||||
history.Header = App.Text("FileHistory");
|
||||
history.Icon = App.CreateMenuIcon("Icons.Histories");
|
||||
history.Icon = this.CreateMenuIcon("Icons.Histories");
|
||||
history.Click += (_, ev) =>
|
||||
{
|
||||
App.ShowWindow(new ViewModels.FileHistories(repo.FullPath, change.Path, commit.SHA));
|
||||
@@ -279,7 +279,7 @@ namespace SourceGit.Views
|
||||
|
||||
var blame = new MenuItem();
|
||||
blame.Header = App.Text("Blame");
|
||||
blame.Icon = App.CreateMenuIcon("Icons.Blame");
|
||||
blame.Icon = this.CreateMenuIcon("Icons.Blame");
|
||||
blame.IsEnabled = change.Index != Models.ChangeState.Deleted;
|
||||
blame.Click += (_, ev) =>
|
||||
{
|
||||
@@ -289,7 +289,7 @@ namespace SourceGit.Views
|
||||
|
||||
var patch = new MenuItem();
|
||||
patch.Header = App.Text("FileCM.SaveAsPatch");
|
||||
patch.Icon = App.CreateMenuIcon("Icons.Save");
|
||||
patch.Icon = this.CreateMenuIcon("Icons.Save");
|
||||
patch.Click += async (_, e) =>
|
||||
{
|
||||
var storageProvider = TopLevel.GetTopLevel(this)?.StorageProvider;
|
||||
@@ -312,7 +312,7 @@ namespace SourceGit.Views
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
App.RaiseException(repo.FullPath, $"Failed to save as patch: {exception.Message}");
|
||||
repo.SendNotification($"Failed to save as patch: {exception.Message}", true);
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
@@ -332,7 +332,7 @@ namespace SourceGit.Views
|
||||
{
|
||||
var resetToThisRevision = new MenuItem();
|
||||
resetToThisRevision.Header = App.Text("ChangeCM.CheckoutThisRevision");
|
||||
resetToThisRevision.Icon = App.CreateMenuIcon("Icons.File.Checkout");
|
||||
resetToThisRevision.Icon = this.CreateMenuIcon("Icons.File.Checkout");
|
||||
resetToThisRevision.Click += async (_, ev) =>
|
||||
{
|
||||
await vm.ResetToThisRevisionAsync(change);
|
||||
@@ -341,7 +341,7 @@ namespace SourceGit.Views
|
||||
|
||||
var resetToFirstParent = new MenuItem();
|
||||
resetToFirstParent.Header = App.Text("ChangeCM.CheckoutFirstParentRevision");
|
||||
resetToFirstParent.Icon = App.CreateMenuIcon("Icons.File.Checkout");
|
||||
resetToFirstParent.Icon = this.CreateMenuIcon("Icons.File.Checkout");
|
||||
resetToFirstParent.IsEnabled = commit.Parents.Count > 0;
|
||||
resetToFirstParent.Click += async (_, ev) =>
|
||||
{
|
||||
@@ -357,11 +357,11 @@ namespace SourceGit.Views
|
||||
{
|
||||
var lfs = new MenuItem();
|
||||
lfs.Header = App.Text("GitLFS");
|
||||
lfs.Icon = App.CreateMenuIcon("Icons.LFS");
|
||||
lfs.Icon = this.CreateMenuIcon("Icons.LFS");
|
||||
|
||||
var lfsLock = new MenuItem();
|
||||
lfsLock.Header = App.Text("GitLFS.Locks.Lock");
|
||||
lfsLock.Icon = App.CreateMenuIcon("Icons.Lock");
|
||||
lfsLock.Icon = this.CreateMenuIcon("Icons.Lock");
|
||||
if (repo.Remotes.Count == 1)
|
||||
{
|
||||
lfsLock.Click += async (_, e) =>
|
||||
@@ -389,7 +389,7 @@ namespace SourceGit.Views
|
||||
|
||||
var lfsUnlock = new MenuItem();
|
||||
lfsUnlock.Header = App.Text("GitLFS.Locks.Unlock");
|
||||
lfsUnlock.Icon = App.CreateMenuIcon("Icons.Unlock");
|
||||
lfsUnlock.Icon = this.CreateMenuIcon("Icons.Unlock");
|
||||
if (repo.Remotes.Count == 1)
|
||||
{
|
||||
lfsUnlock.Click += async (_, e) =>
|
||||
@@ -426,13 +426,13 @@ namespace SourceGit.Views
|
||||
var target = new Models.CustomActionTargetFile(change.Path, vm.Commit);
|
||||
var custom = new MenuItem();
|
||||
custom.Header = App.Text("FileCM.CustomAction");
|
||||
custom.Icon = App.CreateMenuIcon("Icons.Action");
|
||||
custom.Icon = this.CreateMenuIcon("Icons.Action");
|
||||
|
||||
foreach (var action in actions)
|
||||
{
|
||||
var (dup, label) = action;
|
||||
var item = new MenuItem();
|
||||
item.Icon = App.CreateMenuIcon("Icons.Action");
|
||||
item.Icon = this.CreateMenuIcon("Icons.Action");
|
||||
item.Header = label;
|
||||
item.Click += async (_, e) =>
|
||||
{
|
||||
@@ -449,21 +449,21 @@ namespace SourceGit.Views
|
||||
|
||||
var copyPath = new MenuItem();
|
||||
copyPath.Header = App.Text("CopyPath");
|
||||
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyPath.Icon = this.CreateMenuIcon("Icons.Copy");
|
||||
copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C";
|
||||
copyPath.Click += async (_, ev) =>
|
||||
{
|
||||
await App.CopyTextAsync(change.Path);
|
||||
await this.CopyTextAsync(change.Path);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var copyFullPath = new MenuItem();
|
||||
copyFullPath.Header = App.Text("CopyFullPath");
|
||||
copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyFullPath.Icon = this.CreateMenuIcon("Icons.Copy");
|
||||
copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C";
|
||||
copyFullPath.Click += async (_, e) =>
|
||||
{
|
||||
await App.CopyTextAsync(fullPath);
|
||||
await this.CopyTextAsync(fullPath);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
@@ -484,9 +484,9 @@ namespace SourceGit.Views
|
||||
e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control))
|
||||
{
|
||||
if (e.KeyModifiers.HasFlag(KeyModifiers.Shift))
|
||||
await App.CopyTextAsync(vm.GetAbsPath(change.Path));
|
||||
await this.CopyTextAsync(vm.GetAbsPath(change.Path));
|
||||
else
|
||||
await App.CopyTextAsync(change.Path);
|
||||
await this.CopyTextAsync(change.Path);
|
||||
|
||||
e.Handled = true;
|
||||
return;
|
||||
|
||||
@@ -134,7 +134,7 @@ namespace SourceGit.Views
|
||||
{
|
||||
var open = new MenuItem();
|
||||
open.Header = App.Text("SHALinkCM.NavigateTo");
|
||||
open.Icon = App.CreateMenuIcon("Icons.Commit");
|
||||
open.Icon = this.CreateMenuIcon("Icons.Commit");
|
||||
open.Click += (_, ev) =>
|
||||
{
|
||||
detail.NavigateTo(link);
|
||||
@@ -143,10 +143,10 @@ namespace SourceGit.Views
|
||||
|
||||
var copy = new MenuItem();
|
||||
copy.Header = App.Text("SHALinkCM.CopySHA");
|
||||
copy.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copy.Icon = this.CreateMenuIcon("Icons.Copy");
|
||||
copy.Click += async (_, ev) =>
|
||||
{
|
||||
await App.CopyTextAsync(link);
|
||||
await this.CopyTextAsync(link);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
@@ -167,7 +167,7 @@ namespace SourceGit.Views
|
||||
{
|
||||
var open = new MenuItem();
|
||||
open.Header = App.Text("IssueLinkCM.OpenInBrowser");
|
||||
open.Icon = App.CreateMenuIcon("Icons.OpenWith");
|
||||
open.Icon = this.CreateMenuIcon("Icons.OpenWith");
|
||||
open.Click += (_, ev) =>
|
||||
{
|
||||
Native.OS.OpenBrowser(link);
|
||||
@@ -176,10 +176,10 @@ namespace SourceGit.Views
|
||||
|
||||
var copy = new MenuItem();
|
||||
copy.Header = App.Text("IssueLinkCM.CopyLink");
|
||||
copy.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copy.Icon = this.CreateMenuIcon("Icons.Copy");
|
||||
copy.Click += async (_, ev) =>
|
||||
{
|
||||
await App.CopyTextAsync(link);
|
||||
await this.CopyTextAsync(link);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
@@ -268,7 +268,7 @@ namespace SourceGit.Views
|
||||
{
|
||||
var currentParent = this.FindAncestorOfType<CommitBaseInfo>();
|
||||
if (currentParent is { DataContext: ViewModels.CommitDetail currentDetail } &&
|
||||
currentDetail.Commit.SHA == lastDetailCommit)
|
||||
currentDetail.Commit.SHA.Equals(lastDetailCommit, StringComparison.Ordinal))
|
||||
{
|
||||
_inlineCommits.TryAdd(sha, c);
|
||||
if (_lastHover == link && c != null)
|
||||
|
||||
@@ -337,7 +337,7 @@ namespace SourceGit.Views
|
||||
|
||||
var copy = new MenuItem();
|
||||
copy.Header = App.Text("Copy");
|
||||
copy.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copy.Icon = this.CreateMenuIcon("Icons.Copy");
|
||||
copy.IsEnabled = hasSelected;
|
||||
copy.Click += (_, ev) =>
|
||||
{
|
||||
@@ -347,7 +347,7 @@ namespace SourceGit.Views
|
||||
|
||||
var cut = new MenuItem();
|
||||
cut.Header = App.Text("Cut");
|
||||
cut.Icon = App.CreateMenuIcon("Icons.Cut");
|
||||
cut.Icon = this.CreateMenuIcon("Icons.Cut");
|
||||
cut.IsEnabled = hasSelected;
|
||||
cut.Click += (_, ev) =>
|
||||
{
|
||||
@@ -357,7 +357,7 @@ namespace SourceGit.Views
|
||||
|
||||
var paste = new MenuItem();
|
||||
paste.Header = App.Text("Paste");
|
||||
paste.Icon = App.CreateMenuIcon("Icons.Paste");
|
||||
paste.Icon = this.CreateMenuIcon("Icons.Paste");
|
||||
paste.Click += (_, ev) =>
|
||||
{
|
||||
Paste();
|
||||
@@ -447,7 +447,7 @@ namespace SourceGit.Views
|
||||
menu.Items.Add(new MenuItem()
|
||||
{
|
||||
Header = App.Text("WorkingCopy.NoCommitTemplates"),
|
||||
Icon = App.CreateMenuIcon("Icons.Code"),
|
||||
Icon = this.CreateMenuIcon("Icons.Code"),
|
||||
IsEnabled = false
|
||||
});
|
||||
}
|
||||
@@ -455,7 +455,7 @@ namespace SourceGit.Views
|
||||
{
|
||||
for (int i = 0; i < templateCount; i++)
|
||||
{
|
||||
var icon = App.CreateMenuIcon("Icons.Code");
|
||||
var icon = this.CreateMenuIcon("Icons.Code");
|
||||
icon.Fill = foreground;
|
||||
|
||||
var template = repo.Settings.CommitTemplates[i];
|
||||
@@ -484,7 +484,7 @@ namespace SourceGit.Views
|
||||
friendlyName = $"~{gitTemplate.AsSpan(prefixLen)}";
|
||||
}
|
||||
|
||||
var icon = App.CreateMenuIcon("Icons.Code");
|
||||
var icon = this.CreateMenuIcon("Icons.Code");
|
||||
icon.Fill = foreground;
|
||||
|
||||
var gitTemplateItem = new MenuItem();
|
||||
@@ -508,7 +508,7 @@ namespace SourceGit.Views
|
||||
menu.Items.Add(new MenuItem()
|
||||
{
|
||||
Header = App.Text("WorkingCopy.NoCommitHistories"),
|
||||
Icon = App.CreateMenuIcon("Icons.Histories"),
|
||||
Icon = this.CreateMenuIcon("Icons.Histories"),
|
||||
IsEnabled = false
|
||||
});
|
||||
}
|
||||
@@ -524,7 +524,7 @@ namespace SourceGit.Views
|
||||
TextTrimming = TextTrimming.CharacterEllipsis
|
||||
};
|
||||
|
||||
var icon = App.CreateMenuIcon("Icons.Histories");
|
||||
var icon = this.CreateMenuIcon("Icons.Histories");
|
||||
icon.Fill = foreground;
|
||||
|
||||
var item = new MenuItem();
|
||||
@@ -541,7 +541,7 @@ namespace SourceGit.Views
|
||||
|
||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||
|
||||
var clearIcon = App.CreateMenuIcon("Icons.Clear");
|
||||
var clearIcon = this.CreateMenuIcon("Icons.Clear");
|
||||
clearIcon.Fill = foreground;
|
||||
|
||||
var clearHistoryItem = new MenuItem();
|
||||
@@ -573,7 +573,7 @@ namespace SourceGit.Views
|
||||
|
||||
if (vm.Staged == null || vm.Staged.Count == 0)
|
||||
{
|
||||
App.RaiseException(repo.FullPath, "No files added to commit!");
|
||||
repo.SendNotification("No files added to commit!", true);
|
||||
e.Handled = true;
|
||||
return;
|
||||
}
|
||||
@@ -581,7 +581,7 @@ namespace SourceGit.Views
|
||||
var services = repo.GetPreferredOpenAIServices();
|
||||
if (services.Count == 0)
|
||||
{
|
||||
App.RaiseException(repo.FullPath, "Bad configuration for OpenAI");
|
||||
repo.SendNotification("Bad configuration for OpenAI", true);
|
||||
e.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace SourceGit.Views
|
||||
|
||||
var patch = new MenuItem();
|
||||
patch.Header = App.Text("FileCM.SaveAsPatch");
|
||||
patch.Icon = App.CreateMenuIcon("Icons.Save");
|
||||
patch.Icon = this.CreateMenuIcon("Icons.Save");
|
||||
patch.Click += async (_, e) =>
|
||||
{
|
||||
var storageProvider = this.StorageProvider;
|
||||
@@ -42,12 +42,14 @@ namespace SourceGit.Views
|
||||
if (storageFile != null)
|
||||
{
|
||||
var saveTo = storageFile.Path.LocalPath;
|
||||
await vm.SaveChangesAsPatchAsync(selected, saveTo);
|
||||
var succ = await vm.SaveChangesAsPatchAsync(selected, saveTo);
|
||||
if (succ)
|
||||
await new Alert().ShowAsync(this, "Save patch successfully.", false);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
App.RaiseException(null, $"Failed to save as patch: {exception.Message}");
|
||||
await new Alert().ShowAsync(this, $"Failed to save as patch: {exception.Message}", true);
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
@@ -58,7 +60,7 @@ namespace SourceGit.Views
|
||||
var change = selected[0];
|
||||
var openWithMerger = new MenuItem();
|
||||
openWithMerger.Header = App.Text("OpenInExternalMergeTool");
|
||||
openWithMerger.Icon = App.CreateMenuIcon("Icons.OpenWith");
|
||||
openWithMerger.Icon = this.CreateMenuIcon("Icons.OpenWith");
|
||||
openWithMerger.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+D" : "Ctrl+Shift+D";
|
||||
openWithMerger.Click += (_, ev) =>
|
||||
{
|
||||
@@ -72,7 +74,7 @@ namespace SourceGit.Views
|
||||
var full = vm.GetAbsPath(change.Path);
|
||||
var explore = new MenuItem();
|
||||
explore.Header = App.Text("RevealFile");
|
||||
explore.Icon = App.CreateMenuIcon("Icons.Explore");
|
||||
explore.Icon = this.CreateMenuIcon("Icons.Explore");
|
||||
explore.IsEnabled = File.Exists(full);
|
||||
explore.Click += (_, ev) =>
|
||||
{
|
||||
@@ -89,7 +91,7 @@ namespace SourceGit.Views
|
||||
{
|
||||
var resetToLeft = new MenuItem();
|
||||
resetToLeft.Header = App.Text("ChangeCM.ResetFileTo", vm.BaseName);
|
||||
resetToLeft.Icon = App.CreateMenuIcon("Icons.File.Checkout");
|
||||
resetToLeft.Icon = this.CreateMenuIcon("Icons.File.Checkout");
|
||||
resetToLeft.Click += async (_, ev) =>
|
||||
{
|
||||
await vm.ResetToLeftAsync(change);
|
||||
@@ -98,7 +100,7 @@ namespace SourceGit.Views
|
||||
|
||||
var resetToRight = new MenuItem();
|
||||
resetToRight.Header = App.Text("ChangeCM.ResetFileTo", vm.ToName);
|
||||
resetToRight.Icon = App.CreateMenuIcon("Icons.File.Checkout");
|
||||
resetToRight.Icon = this.CreateMenuIcon("Icons.File.Checkout");
|
||||
resetToRight.Click += async (_, ev) =>
|
||||
{
|
||||
await vm.ResetToRightAsync(change);
|
||||
@@ -112,21 +114,21 @@ namespace SourceGit.Views
|
||||
|
||||
var copyPath = new MenuItem();
|
||||
copyPath.Header = App.Text("CopyPath");
|
||||
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyPath.Icon = this.CreateMenuIcon("Icons.Copy");
|
||||
copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C";
|
||||
copyPath.Click += async (_, ev) =>
|
||||
{
|
||||
await App.CopyTextAsync(change.Path);
|
||||
await this.CopyTextAsync(change.Path);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var copyFullPath = new MenuItem();
|
||||
copyFullPath.Header = App.Text("CopyFullPath");
|
||||
copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyFullPath.Icon = this.CreateMenuIcon("Icons.Copy");
|
||||
copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C";
|
||||
copyFullPath.Click += async (_, ev) =>
|
||||
{
|
||||
await App.CopyTextAsync(vm.GetAbsPath(change.Path));
|
||||
await this.CopyTextAsync(vm.GetAbsPath(change.Path));
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
@@ -142,7 +144,7 @@ namespace SourceGit.Views
|
||||
{
|
||||
var resetToLeft = new MenuItem();
|
||||
resetToLeft.Header = App.Text("ChangeCM.ResetFileTo", vm.BaseName);
|
||||
resetToLeft.Icon = App.CreateMenuIcon("Icons.File.Checkout");
|
||||
resetToLeft.Icon = this.CreateMenuIcon("Icons.File.Checkout");
|
||||
resetToLeft.Click += async (_, ev) =>
|
||||
{
|
||||
await vm.ResetMultipleToLeftAsync(selected);
|
||||
@@ -151,7 +153,7 @@ namespace SourceGit.Views
|
||||
|
||||
var resetToRight = new MenuItem();
|
||||
resetToRight.Header = App.Text("ChangeCM.ResetFileTo", vm.ToName);
|
||||
resetToRight.Icon = App.CreateMenuIcon("Icons.File.Checkout");
|
||||
resetToRight.Icon = this.CreateMenuIcon("Icons.File.Checkout");
|
||||
resetToRight.Click += async (_, ev) =>
|
||||
{
|
||||
await vm.ResetMultipleToRightAsync(selected);
|
||||
@@ -165,7 +167,7 @@ namespace SourceGit.Views
|
||||
|
||||
var copyPath = new MenuItem();
|
||||
copyPath.Header = App.Text("CopyPath");
|
||||
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyPath.Icon = this.CreateMenuIcon("Icons.Copy");
|
||||
copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C";
|
||||
copyPath.Click += async (_, ev) =>
|
||||
{
|
||||
@@ -173,13 +175,13 @@ namespace SourceGit.Views
|
||||
foreach (var c in selected)
|
||||
builder.AppendLine(c.Path);
|
||||
|
||||
await App.CopyTextAsync(builder.ToString());
|
||||
await this.CopyTextAsync(builder.ToString());
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var copyFullPath = new MenuItem();
|
||||
copyFullPath.Header = App.Text("CopyFullPath");
|
||||
copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyFullPath.Icon = this.CreateMenuIcon("Icons.Copy");
|
||||
copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C";
|
||||
copyFullPath.Click += async (_, ev) =>
|
||||
{
|
||||
@@ -187,7 +189,7 @@ namespace SourceGit.Views
|
||||
foreach (var c in selected)
|
||||
builder.AppendLine(vm.GetAbsPath(c.Path));
|
||||
|
||||
await App.CopyTextAsync(builder.ToString());
|
||||
await this.CopyTextAsync(builder.ToString());
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
@@ -233,7 +235,7 @@ namespace SourceGit.Views
|
||||
builder.AppendLine(copyAbsPath ? vm.GetAbsPath(c.Path) : c.Path);
|
||||
}
|
||||
|
||||
await App.CopyTextAsync(builder.ToString());
|
||||
await this.CopyTextAsync(builder.ToString());
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (e.Key == Key.F && e.KeyModifiers == cmdKey)
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace SourceGit.Views
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
App.RaiseException(string.Empty, $"Failed to select default clone directory: {ex.Message}");
|
||||
await new Alert().ShowAsync(this, $"Failed to select default clone directory: {ex.Message}", true);
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
|
||||
48
src/Views/ControlExtensions.cs
Normal file
48
src/Views/ControlExtensions.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Shapes;
|
||||
using Avalonia.Media;
|
||||
|
||||
namespace SourceGit.Views
|
||||
{
|
||||
public static class ControlExtensions
|
||||
{
|
||||
public static Control CreateFromViewModels(object data)
|
||||
{
|
||||
var dataTypeName = data.GetType().FullName;
|
||||
if (string.IsNullOrEmpty(dataTypeName) || !dataTypeName.Contains(".ViewModels.", StringComparison.Ordinal))
|
||||
return null;
|
||||
|
||||
var viewTypeName = dataTypeName.Replace(".ViewModels.", ".Views.");
|
||||
var viewType = Type.GetType(viewTypeName);
|
||||
if (viewType != null)
|
||||
return Activator.CreateInstance(viewType) as Control;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static async Task CopyTextAsync(this Control control, string text)
|
||||
{
|
||||
var clipboard = TopLevel.GetTopLevel(control)?.Clipboard;
|
||||
if (clipboard != null)
|
||||
await clipboard.SetTextAsync(text);
|
||||
}
|
||||
|
||||
public static Path CreateMenuIcon(this Control control, string iconKey)
|
||||
{
|
||||
if (control?.FindResource(iconKey) is StreamGeometry geo)
|
||||
{
|
||||
return new Path()
|
||||
{
|
||||
Data = geo,
|
||||
Width = 12,
|
||||
Height = 12,
|
||||
Stretch = Stretch.Uniform
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,7 @@
|
||||
<DataTemplate DataType="m:Branch">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Path Width="14" Height="14" Data="{StaticResource Icons.Branch}"/>
|
||||
<SelectableTextBlock VerticalAlignment="Center" Text="{Binding FriendlyName}" Margin="8,0,0,0"/>
|
||||
<TextBlock VerticalAlignment="Center" Text="{Binding FriendlyName}" Margin="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<ContentControl Margin="0,16,0,8" Content="{Binding Mode}">
|
||||
<ContentControl.DataTemplates>
|
||||
<DataTemplate DataType="vm:DiscardAllMode">
|
||||
<Grid RowDefinitions="32,32,32,Auto" ColumnDefinitions="120,*">
|
||||
<Grid RowDefinitions="32,32,32,32,Auto" ColumnDefinitions="120,*">
|
||||
<TextBlock Grid.Row="0" Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
HorizontalAlignment="Right"
|
||||
@@ -30,14 +30,18 @@
|
||||
Text="{DynamicResource Text.Discard.All}"/>
|
||||
|
||||
<CheckBox Grid.Row="1" Grid.Column="1"
|
||||
Content="{DynamicResource Text.Discard.IncludeModified}"
|
||||
IsChecked="{Binding IncludeModified, Mode=TwoWay}"/>
|
||||
|
||||
<CheckBox Grid.Row="2" Grid.Column="1"
|
||||
Content="{DynamicResource Text.Discard.IncludeUntracked}"
|
||||
IsChecked="{Binding IncludeUntracked, Mode=TwoWay}"/>
|
||||
|
||||
<CheckBox Grid.Row="2" Grid.Column="1"
|
||||
<CheckBox Grid.Row="3" Grid.Column="1"
|
||||
Content="{DynamicResource Text.Discard.IncludeIgnored}"
|
||||
IsChecked="{Binding IncludeIgnored, Mode=TwoWay}"/>
|
||||
|
||||
<Grid Grid.Row="3" Grid.Column="1" ColumnDefinitions="Auto,*" Margin="0,6,0,0">
|
||||
<Grid Grid.Row="4" Grid.Column="1" ColumnDefinitions="Auto,*" Margin="0,6,0,0">
|
||||
<Path Grid.Column="0"
|
||||
Width="14" Height="14"
|
||||
Data="{StaticResource Icons.Error}"
|
||||
|
||||
@@ -30,14 +30,14 @@
|
||||
<DataTemplate DataType="m:Null">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Path Width="14" Height="14" Data="{StaticResource Icons.Repositories}"/>
|
||||
<SelectableTextBlock VerticalAlignment="Center" Text="{DynamicResource Text.ExecuteCustomAction.Repository}" Margin="8,0,0,0" IsTabStop="False"/>
|
||||
<TextBlock VerticalAlignment="Center" Text="{DynamicResource Text.ExecuteCustomAction.Repository}" Margin="8,0,0,0" IsTabStop="False"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate DataType="m:Branch">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Path Width="14" Height="14" Data="{StaticResource Icons.Branch}"/>
|
||||
<SelectableTextBlock VerticalAlignment="Center" Text="{Binding FriendlyName}" Margin="8,0,0,0" IsTabStop="False"/>
|
||||
<TextBlock VerticalAlignment="Center" Text="{Binding FriendlyName}" Margin="8,0,0,0" IsTabStop="False"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace SourceGit.Views
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
App.RaiseException(string.Empty, $"Failed to select parent folder: {exception.Message}");
|
||||
Models.Notification.Send(null, $"Failed to select parent folder: {exception.Message}", true);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -268,13 +268,5 @@
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
IsVisible="{Binding IsLoading}"/>
|
||||
</Grid>
|
||||
|
||||
<Border Grid.Row="3" x:Name="NotifyDonePanel" Background="Transparent" IsVisible="False" PointerPressed="OnCloseNotifyPanel">
|
||||
<Border HorizontalAlignment="Center" VerticalAlignment="Center" Effect="drop-shadow(0 0 12 #80000000)">
|
||||
<Border CornerRadius="8" Background="{DynamicResource Brush.Popup}" Padding="32">
|
||||
<Path Width="52" Height="52" Data="{StaticResource Icons.Check}" Fill="Green"/>
|
||||
</Border>
|
||||
</Border>
|
||||
</Border>
|
||||
</Grid>
|
||||
</v:ChromelessWindow>
|
||||
|
||||
@@ -60,18 +60,12 @@ namespace SourceGit.Views
|
||||
if (sender is Button { DataContext: ViewModels.FileHistoriesSingleRevision single })
|
||||
{
|
||||
await single.ResetToSelectedRevisionAsync();
|
||||
NotifyDonePanel.IsVisible = true;
|
||||
await new Alert().ShowAsync(this, "Reset to selected revision successfully.", false);
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void OnCloseNotifyPanel(object _, PointerPressedEventArgs e)
|
||||
{
|
||||
NotifyDonePanel.IsVisible = false;
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private async void OnSaveAsPatch(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is Button { DataContext: ViewModels.FileHistoriesCompareRevisions compare })
|
||||
@@ -84,14 +78,16 @@ namespace SourceGit.Views
|
||||
try
|
||||
{
|
||||
var storageFile = await StorageProvider.SaveFilePickerAsync(options);
|
||||
if (storageFile != null)
|
||||
await compare.SaveAsPatch(storageFile.Path.LocalPath);
|
||||
if (storageFile == null)
|
||||
return;
|
||||
|
||||
NotifyDonePanel.IsVisible = true;
|
||||
var succ = await compare.SaveAsPatch(storageFile.Path.LocalPath);
|
||||
if (succ)
|
||||
await new Alert().ShowAsync(this, "Saved as patch successfully.", false);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
App.RaiseException(string.Empty, $"Failed to save as patch: {exception.Message}");
|
||||
await new Alert().ShowAsync(this, $"Failed to save as patch: {exception.Message}", true);
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user