Merge branch 'release/v2026.09'

This commit is contained in:
leo
2026-04-20 10:25:23 +08:00
82 changed files with 2022 additions and 607 deletions

View File

@@ -6,32 +6,46 @@ This document shows the translation status of each locale file in the repository
### ![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen)
### ![de__DE](https://img.shields.io/badge/de__DE-97.53%25-yellow)
### ![de__DE](https://img.shields.io/badge/de__DE-96.15%25-yellow)
<details>
<summary>Missing keys in de_DE.axaml</summary>
- Text.AIAssistant.Use
- Text.App.HideOthers
- Text.Apply.3Way
- Text.CheckoutBranchFromStash
- Text.CheckoutBranchFromStash.Branch
- Text.CheckoutBranchFromStash.Stash
- Text.Clone.Bookmark
- Text.Clone.Group
- Text.CommandPalette.Branches
- Text.CommandPalette.BranchesAndTags
- Text.CommandPalette.RepositoryActions
- Text.CommandPalette.RevisionFiles
- Text.CommitMessageTextBox.Column
- Text.ConfigureCustomActionControls.StringFormatter
- Text.ConfigureCustomActionControls.StringFormatter.Tip
- Text.ConfigureCustomActionControls.UseFriendlyName
- Text.ConfirmEmptyCommit.StageSelectedThenCommit
- Text.Diff.Submodule.UncommittedChanges
- Text.Discard.IncludeModified
- Text.GotoRevisionSelector
- Text.Hotkeys.Global.OpenLocalRepository
- Text.Hotkeys.Repo.CreateBranch
- Text.Hotkeys.Repo.GoToChild
- Text.Init.CommandTip
- Text.Init.ErrorMessageTip
- Text.OpenLocalRepository
- Text.OpenLocalRepository.Bookmark
- Text.OpenLocalRepository.Group
- Text.OpenLocalRepository.Path
- Text.Preferences.AI.AdditionalPrompt
- Text.Preferences.General.Use24Hours
- Text.StashCM.ApplyFileChanges
- Text.StashCM.Branch
- Text.SubmoduleRevisionCompare
- Text.SubmoduleRevisionCompare.OpenDetails
- Text.Worktree.Branch
- Text.Worktree.Head
- Text.Worktree.Path
@@ -40,13 +54,14 @@ This document shows the translation status of each locale file in the repository
### ![es__ES](https://img.shields.io/badge/es__ES-%E2%88%9A-brightgreen)
### ![fr__FR](https://img.shields.io/badge/fr__FR-91.37%25-yellow)
### ![fr__FR](https://img.shields.io/badge/fr__FR-90.06%25-yellow)
<details>
<summary>Missing keys in fr_FR.axaml</summary>
- Text.About.ReleaseDate
- Text.AIAssistant.Use
- Text.App.HideOthers
- Text.Apply.3Way
- Text.Blame.IgnoreWhitespace
- Text.BranchCM.CompareTwo
@@ -60,6 +75,8 @@ This document shows the translation status of each locale file in the repository
- Text.CheckoutBranchFromStash
- Text.CheckoutBranchFromStash.Branch
- Text.CheckoutBranchFromStash.Stash
- Text.Clone.Bookmark
- Text.Clone.Group
- Text.CommandPalette.Branches
- Text.CommandPalette.BranchesAndTags
- Text.CommandPalette.RepositoryActions
@@ -68,7 +85,11 @@ This document shows the translation status of each locale file in the repository
- Text.CommitMessageTextBox.Placeholder
- Text.Compare.WithHead
- Text.Configure.Git.AskBeforeAutoUpdatingSubmodules
- Text.ConfigureCustomActionControls.StringFormatter
- Text.ConfigureCustomActionControls.StringFormatter.Tip
- Text.ConfigureCustomActionControls.UseFriendlyName
- Text.ConfirmEmptyCommit.StageSelectedThenCommit
- Text.Diff.Submodule.UncommittedChanges
- Text.Discard.IncludeModified
- Text.EditBranchDescription
- Text.EditBranchDescription.Target
@@ -76,6 +97,7 @@ This document shows the translation status of each locale file in the repository
- Text.GotoRevisionSelector
- Text.Histories.Header.DateTime
- Text.Histories.ShowColumns
- Text.Hotkeys.Global.OpenLocalRepository
- Text.Hotkeys.Global.ShowWorkspaceDropdownMenu
- Text.Hotkeys.Global.Zoom
- Text.Hotkeys.Repo.CreateBranch
@@ -101,6 +123,10 @@ This document shows the translation status of each locale file in the repository
- Text.MergeConflictEditor.Undo
- Text.No
- Text.OpenFile
- Text.OpenLocalRepository
- Text.OpenLocalRepository.Bookmark
- Text.OpenLocalRepository.Group
- Text.OpenLocalRepository.Path
- Text.PageTabBar.Tab.MoveToWorkspace
- Text.PageTabBar.Tab.Refresh
- Text.Preferences.AI.AdditionalPrompt
@@ -120,6 +146,8 @@ This document shows the translation status of each locale file in the repository
- Text.SquashOrFixup.Into
- Text.StashCM.ApplyFileChanges
- Text.StashCM.Branch
- Text.SubmoduleRevisionCompare
- Text.SubmoduleRevisionCompare.OpenDetails
- Text.TagCM.CompareTwo
- Text.TagCM.CompareWith
- Text.TagCM.CompareWithHead
@@ -132,7 +160,7 @@ This document shows the translation status of each locale file in the repository
</details>
### ![id__ID](https://img.shields.io/badge/id__ID-89.21%25-yellow)
### ![id__ID](https://img.shields.io/badge/id__ID-87.93%25-yellow)
<details>
<summary>Missing keys in id_ID.axaml</summary>
@@ -140,6 +168,7 @@ This document shows the translation status of each locale file in the repository
- Text.About.ReleaseDate
- Text.About.ReleaseNotes
- Text.AIAssistant.Use
- Text.App.HideOthers
- Text.Apply.3Way
- Text.Blame.BlameOnPreviousRevision
- Text.Blame.IgnoreWhitespace
@@ -156,6 +185,8 @@ This document shows the translation status of each locale file in the repository
- Text.CheckoutBranchFromStash
- Text.CheckoutBranchFromStash.Branch
- Text.CheckoutBranchFromStash.Stash
- Text.Clone.Bookmark
- Text.Clone.Group
- Text.CommandPalette.Branches
- Text.CommandPalette.BranchesAndTags
- Text.CommandPalette.RepositoryActions
@@ -167,9 +198,13 @@ This document shows the translation status of each locale file in the repository
- Text.Configure.CommitMessageTemplate.BuiltinVars
- Text.Configure.Git.AskBeforeAutoUpdatingSubmodules
- Text.Configure.Git.ConventionalTypesOverride
- Text.ConfigureCustomActionControls.StringFormatter
- Text.ConfigureCustomActionControls.StringFormatter.Tip
- Text.ConfigureCustomActionControls.StringValue.Tip
- Text.ConfigureCustomActionControls.UseFriendlyName
- Text.ConfirmEmptyCommit.StageSelectedThenCommit
- Text.DealWithLocalChanges.DoNothing
- Text.Diff.Submodule.UncommittedChanges
- Text.Discard.IncludeModified
- Text.DropHead
- Text.DropHead.Commit
@@ -182,6 +217,7 @@ This document shows the translation status of each locale file in the repository
- Text.GotoRevisionSelector
- Text.Histories.Header.DateTime
- Text.Histories.ShowColumns
- Text.Hotkeys.Global.OpenLocalRepository
- Text.Hotkeys.Global.ShowWorkspaceDropdownMenu
- Text.Hotkeys.Global.Zoom
- Text.Hotkeys.Repo.CreateBranch
@@ -212,6 +248,10 @@ This document shows the translation status of each locale file in the repository
- Text.Open
- Text.Open.SystemDefaultEditor
- Text.OpenFile
- Text.OpenLocalRepository
- Text.OpenLocalRepository.Bookmark
- Text.OpenLocalRepository.Group
- Text.OpenLocalRepository.Path
- Text.PageTabBar.Tab.MoveToWorkspace
- Text.PageTabBar.Tab.Refresh
- Text.Preferences.AI.AdditionalPrompt
@@ -233,6 +273,8 @@ This document shows the translation status of each locale file in the repository
- Text.SquashOrFixup.Into
- Text.StashCM.ApplyFileChanges
- Text.StashCM.Branch
- Text.SubmoduleRevisionCompare
- Text.SubmoduleRevisionCompare.OpenDetails
- Text.TagCM.CompareTwo
- Text.TagCM.CompareWith
- Text.TagCM.CompareWithHead
@@ -245,80 +287,109 @@ This document shows the translation status of each locale file in the repository
</details>
### ![it__IT](https://img.shields.io/badge/it__IT-96.92%25-yellow)
### ![it__IT](https://img.shields.io/badge/it__IT-95.54%25-yellow)
<details>
<summary>Missing keys in it_IT.axaml</summary>
- Text.AIAssistant.Use
- Text.App.HideOthers
- Text.Apply.3Way
- Text.ChangeCM.ResetFileTo
- Text.CheckoutBranchFromStash
- Text.CheckoutBranchFromStash.Branch
- Text.CheckoutBranchFromStash.Stash
- Text.Clone.Bookmark
- Text.Clone.Group
- Text.CommandPalette.Branches
- Text.CommandPalette.BranchesAndTags
- Text.CommandPalette.RepositoryActions
- Text.CommandPalette.RevisionFiles
- Text.CommitMessageTextBox.Column
- Text.ConfigureCustomActionControls.StringFormatter
- Text.ConfigureCustomActionControls.StringFormatter.Tip
- Text.ConfigureCustomActionControls.UseFriendlyName
- Text.ConfirmEmptyCommit.StageSelectedThenCommit
- Text.Diff.Submodule.UncommittedChanges
- Text.Discard.IncludeModified
- Text.GotoRevisionSelector
- Text.Histories.Header.DateTime
- Text.Histories.ShowColumns
- Text.Hotkeys.Global.OpenLocalRepository
- Text.Hotkeys.Repo.CreateBranch
- Text.Hotkeys.Repo.GoToChild
- Text.Hotkeys.Repo.GoToParent
- Text.Init.CommandTip
- Text.Init.ErrorMessageTip
- Text.OpenLocalRepository
- Text.OpenLocalRepository.Bookmark
- Text.OpenLocalRepository.Group
- Text.OpenLocalRepository.Path
- Text.Preferences.AI.AdditionalPrompt
- Text.Preferences.General.Use24Hours
- Text.SelfUpdate.CurrentVersion
- Text.SelfUpdate.ReleaseDate
- Text.StashCM.ApplyFileChanges
- Text.StashCM.Branch
- Text.SubmoduleRevisionCompare
- Text.SubmoduleRevisionCompare.OpenDetails
- Text.Worktree.Branch
- Text.Worktree.Head
- Text.Worktree.Path
</details>
### ![ja__JP](https://img.shields.io/badge/ja__JP-97.84%25-yellow)
### ![ja__JP](https://img.shields.io/badge/ja__JP-96.45%25-yellow)
<details>
<summary>Missing keys in ja_JP.axaml</summary>
- Text.AIAssistant.Use
- Text.App.HideOthers
- Text.Apply.3Way
- Text.CheckoutBranchFromStash
- Text.CheckoutBranchFromStash.Branch
- Text.CheckoutBranchFromStash.Stash
- Text.Clone.Bookmark
- Text.Clone.Group
- Text.CommandPalette.Branches
- Text.CommandPalette.BranchesAndTags
- Text.CommandPalette.RepositoryActions
- Text.CommandPalette.RevisionFiles
- Text.ConfigureCustomActionControls.StringFormatter
- Text.ConfigureCustomActionControls.StringFormatter.Tip
- Text.ConfigureCustomActionControls.UseFriendlyName
- Text.ConfirmEmptyCommit.StageSelectedThenCommit
- Text.DealWithLocalChanges.DoNothing
- Text.Diff.Submodule.UncommittedChanges
- Text.Discard.IncludeModified
- Text.Hotkeys.Global.OpenLocalRepository
- Text.Hotkeys.Repo.CreateBranch
- Text.Init.CommandTip
- Text.Init.ErrorMessageTip
- Text.OpenLocalRepository
- Text.OpenLocalRepository.Bookmark
- Text.OpenLocalRepository.Group
- Text.OpenLocalRepository.Path
- Text.Preferences.AI.AdditionalPrompt
- Text.Preferences.General.Use24Hours
- Text.StashCM.Branch
- Text.SubmoduleRevisionCompare
- Text.SubmoduleRevisionCompare.OpenDetails
- Text.Worktree.Branch
- Text.Worktree.Head
- Text.Worktree.Path
</details>
### ![ko__KR](https://img.shields.io/badge/ko__KR-89.52%25-yellow)
### ![ko__KR](https://img.shields.io/badge/ko__KR-88.24%25-yellow)
<details>
<summary>Missing keys in ko_KR.axaml</summary>
- Text.About.ReleaseDate
- Text.AIAssistant.Use
- Text.App.HideOthers
- Text.Apply.3Way
- Text.Blame.BlameOnPreviousRevision
- Text.Blame.IgnoreWhitespace
@@ -336,6 +407,8 @@ This document shows the translation status of each locale file in the repository
- Text.CheckoutBranchFromStash
- Text.CheckoutBranchFromStash.Branch
- Text.CheckoutBranchFromStash.Stash
- Text.Clone.Bookmark
- Text.Clone.Group
- Text.CommandPalette.Branches
- Text.CommandPalette.BranchesAndTags
- Text.CommandPalette.RepositoryActions
@@ -345,9 +418,13 @@ This document shows the translation status of each locale file in the repository
- Text.Compare.WithHead
- Text.Configure.Git.AskBeforeAutoUpdatingSubmodules
- Text.Configure.Git.ConventionalTypesOverride
- Text.ConfigureCustomActionControls.StringFormatter
- Text.ConfigureCustomActionControls.StringFormatter.Tip
- Text.ConfigureCustomActionControls.StringValue.Tip
- Text.ConfigureCustomActionControls.UseFriendlyName
- Text.ConfirmEmptyCommit.StageSelectedThenCommit
- Text.DealWithLocalChanges.DoNothing
- Text.Diff.Submodule.UncommittedChanges
- Text.Discard.IncludeModified
- Text.EditBranchDescription
- Text.EditBranchDescription.Target
@@ -357,6 +434,7 @@ This document shows the translation status of each locale file in the repository
- Text.GotoRevisionSelector
- Text.Histories.Header.DateTime
- Text.Histories.ShowColumns
- Text.Hotkeys.Global.OpenLocalRepository
- Text.Hotkeys.Global.ShowWorkspaceDropdownMenu
- Text.Hotkeys.Global.Zoom
- Text.Hotkeys.Repo.CreateBranch
@@ -387,6 +465,10 @@ This document shows the translation status of each locale file in the repository
- Text.Open
- Text.Open.SystemDefaultEditor
- Text.OpenFile
- Text.OpenLocalRepository
- Text.OpenLocalRepository.Bookmark
- Text.OpenLocalRepository.Group
- Text.OpenLocalRepository.Path
- Text.PageTabBar.Tab.MoveToWorkspace
- Text.PageTabBar.Tab.Refresh
- Text.Preferences.AI.AdditionalPrompt
@@ -410,6 +492,8 @@ This document shows the translation status of each locale file in the repository
- Text.StashCM.ApplyFileChanges
- Text.StashCM.Branch
- Text.Submodule.Status.Unmerged
- Text.SubmoduleRevisionCompare
- Text.SubmoduleRevisionCompare.OpenDetails
- Text.TagCM.CompareTwo
- Text.TagCM.CompareWith
- Text.TagCM.CompareWithHead
@@ -422,12 +506,13 @@ This document shows the translation status of each locale file in the repository
</details>
### ![pt__BR](https://img.shields.io/badge/pt__BR-67.73%25-red)
### ![pt__BR](https://img.shields.io/badge/pt__BR-66.84%25-red)
<details>
<summary>Missing keys in pt_BR.axaml</summary>
- Text.AIAssistant.Use
- Text.App.HideOthers
- Text.Apply.3Way
- Text.Blame.BlameOnPreviousRevision
- Text.BranchCM.InteractiveRebase.Manually
@@ -449,6 +534,8 @@ This document shows the translation status of each locale file in the repository
- Text.CheckoutBranchFromStash
- Text.CheckoutBranchFromStash.Branch
- Text.CheckoutBranchFromStash.Stash
- Text.Clone.Bookmark
- Text.Clone.Group
- Text.Clone.RecurseSubmodules
- Text.CommandPalette.Branches
- Text.CommandPalette.BranchesAndTags
@@ -509,8 +596,11 @@ This document shows the translation status of each locale file in the repository
- Text.ConfigureCustomActionControls.Label
- Text.ConfigureCustomActionControls.Options
- Text.ConfigureCustomActionControls.Options.Tip
- Text.ConfigureCustomActionControls.StringFormatter
- Text.ConfigureCustomActionControls.StringFormatter.Tip
- Text.ConfigureCustomActionControls.StringValue.Tip
- Text.ConfigureCustomActionControls.Type
- Text.ConfigureCustomActionControls.UseFriendlyName
- Text.ConfirmEmptyCommit.Continue
- Text.ConfirmEmptyCommit.NoLocalChanges
- Text.ConfirmEmptyCommit.StageAllThenCommit
@@ -538,6 +628,7 @@ This document shows the translation status of each locale file in the repository
- Text.Diff.New
- Text.Diff.Old
- Text.Diff.Submodule.Deleted
- Text.Diff.Submodule.UncommittedChanges
- Text.DirHistories
- Text.DirtyState.HasLocalChanges
- Text.DirtyState.HasPendingPullOrPush
@@ -554,7 +645,6 @@ This document shows the translation status of each locale file in the repository
- Text.Fetch.Force
- Text.FileCM.CustomAction
- Text.FileCM.ResolveUsing
- Text.GitFlow.FinishWithPush
- Text.GitFlow.FinishWithSquash
- Text.GitLFS.Locks.UnlockAllMyLocks
- Text.GitLFS.Locks.UnlockAllMyLocks.Confirm
@@ -562,6 +652,7 @@ This document shows the translation status of each locale file in the repository
- Text.Histories.Header.DateTime
- Text.Histories.ShowColumns
- Text.Hotkeys.Global.Clone
- Text.Hotkeys.Global.OpenLocalRepository
- Text.Hotkeys.Global.ShowWorkspaceDropdownMenu
- Text.Hotkeys.Global.SwitchTab
- Text.Hotkeys.Global.Zoom
@@ -610,6 +701,10 @@ This document shows the translation status of each locale file in the repository
- Text.Open
- Text.Open.SystemDefaultEditor
- Text.OpenFile
- Text.OpenLocalRepository
- Text.OpenLocalRepository.Bookmark
- Text.OpenLocalRepository.Group
- Text.OpenLocalRepository.Path
- Text.PageTabBar.Tab.MoveToWorkspace
- Text.PageTabBar.Tab.Refresh
- Text.Preferences.AI.AdditionalPrompt
@@ -706,6 +801,8 @@ This document shows the translation status of each locale file in the repository
- Text.Submodule.Status.Unmerged
- Text.Submodule.Update
- Text.Submodule.URL
- Text.SubmoduleRevisionCompare
- Text.SubmoduleRevisionCompare.OpenDetails
- Text.Tag.Tagger
- Text.Tag.Time
- Text.TagCM.CompareTwo
@@ -744,9 +841,29 @@ This document shows the translation status of each locale file in the repository
</details>
### ![ru__RU](https://img.shields.io/badge/ru__RU-%E2%88%9A-brightgreen)
### ![ru__RU](https://img.shields.io/badge/ru__RU-98.58%25-yellow)
### ![ta__IN](https://img.shields.io/badge/ta__IN-69.78%25-red)
<details>
<summary>Missing keys in ru_RU.axaml</summary>
- Text.App.HideOthers
- Text.Clone.Bookmark
- Text.Clone.Group
- Text.ConfigureCustomActionControls.StringFormatter
- Text.ConfigureCustomActionControls.StringFormatter.Tip
- Text.ConfigureCustomActionControls.UseFriendlyName
- Text.Diff.Submodule.UncommittedChanges
- Text.Hotkeys.Global.OpenLocalRepository
- Text.OpenLocalRepository
- Text.OpenLocalRepository.Bookmark
- Text.OpenLocalRepository.Group
- Text.OpenLocalRepository.Path
- Text.SubmoduleRevisionCompare
- Text.SubmoduleRevisionCompare.OpenDetails
</details>
### ![ta__IN](https://img.shields.io/badge/ta__IN-68.86%25-red)
<details>
<summary>Missing keys in ta_IN.axaml</summary>
@@ -758,6 +875,7 @@ This document shows the translation status of each locale file in the repository
- Text.AddToIgnore.Storage
- Text.AIAssistant.Use
- Text.App.Hide
- Text.App.HideOthers
- Text.App.ShowAll
- Text.Apply.3Way
- Text.Askpass.Passphrase
@@ -802,6 +920,8 @@ This document shows the translation status of each locale file in the repository
- Text.CheckoutBranchFromStash
- Text.CheckoutBranchFromStash.Branch
- Text.CheckoutBranchFromStash.Stash
- Text.Clone.Bookmark
- Text.Clone.Group
- Text.CommandPalette.Branches
- Text.CommandPalette.BranchesAndTags
- Text.CommandPalette.RepositoryActions
@@ -853,8 +973,11 @@ This document shows the translation status of each locale file in the repository
- Text.ConfigureCustomActionControls.Label
- Text.ConfigureCustomActionControls.Options
- Text.ConfigureCustomActionControls.Options.Tip
- Text.ConfigureCustomActionControls.StringFormatter
- Text.ConfigureCustomActionControls.StringFormatter.Tip
- Text.ConfigureCustomActionControls.StringValue.Tip
- Text.ConfigureCustomActionControls.Type
- Text.ConfigureCustomActionControls.UseFriendlyName
- Text.ConfirmEmptyCommit.Continue
- Text.ConfirmEmptyCommit.NoLocalChanges
- Text.ConfirmEmptyCommit.StageAllThenCommit
@@ -877,6 +1000,7 @@ This document shows the translation status of each locale file in the repository
- Text.Diff.New
- Text.Diff.Old
- Text.Diff.Submodule.Deleted
- Text.Diff.Submodule.UncommittedChanges
- Text.DirHistories
- Text.DirtyState.HasLocalChanges
- Text.DirtyState.HasPendingPullOrPush
@@ -891,13 +1015,13 @@ This document shows the translation status of each locale file in the repository
- Text.ExecuteCustomAction.Target
- Text.ExecuteCustomAction.Repository
- Text.FileCM.CustomAction
- Text.GitFlow.FinishWithPush
- Text.GitFlow.FinishWithSquash
- Text.GitLFS.Locks.UnlockAllMyLocks
- Text.GitLFS.Locks.UnlockAllMyLocks.Confirm
- Text.GotoRevisionSelector
- Text.Histories.Header.DateTime
- Text.Histories.ShowColumns
- Text.Hotkeys.Global.OpenLocalRepository
- Text.Hotkeys.Global.ShowWorkspaceDropdownMenu
- Text.Hotkeys.Global.SwitchTab
- Text.Hotkeys.Global.Zoom
@@ -937,6 +1061,10 @@ This document shows the translation status of each locale file in the repository
- Text.Open
- Text.Open.SystemDefaultEditor
- Text.OpenFile
- Text.OpenLocalRepository
- Text.OpenLocalRepository.Bookmark
- Text.OpenLocalRepository.Group
- Text.OpenLocalRepository.Path
- Text.PageTabBar.Tab.MoveToWorkspace
- Text.PageTabBar.Tab.Refresh
- Text.Preferences.AI.AdditionalPrompt
@@ -1012,6 +1140,8 @@ This document shows the translation status of each locale file in the repository
- Text.Submodule.Status.Unmerged
- Text.Submodule.Update
- Text.Submodule.URL
- Text.SubmoduleRevisionCompare
- Text.SubmoduleRevisionCompare.OpenDetails
- Text.Tag.Tagger
- Text.Tag.Time
- Text.TagCM.CompareTwo
@@ -1048,7 +1178,7 @@ This document shows the translation status of each locale file in the repository
</details>
### ![uk__UA](https://img.shields.io/badge/uk__UA-70.61%25-red)
### ![uk__UA](https://img.shields.io/badge/uk__UA-69.68%25-red)
<details>
<summary>Missing keys in uk_UA.axaml</summary>
@@ -1060,6 +1190,7 @@ This document shows the translation status of each locale file in the repository
- Text.AddToIgnore.Storage
- Text.AIAssistant.Use
- Text.App.Hide
- Text.App.HideOthers
- Text.App.ShowAll
- Text.Apply.3Way
- Text.Askpass.Passphrase
@@ -1104,6 +1235,8 @@ This document shows the translation status of each locale file in the repository
- Text.CheckoutBranchFromStash
- Text.CheckoutBranchFromStash.Branch
- Text.CheckoutBranchFromStash.Stash
- Text.Clone.Bookmark
- Text.Clone.Group
- Text.CommandPalette.Branches
- Text.CommandPalette.BranchesAndTags
- Text.CommandPalette.RepositoryActions
@@ -1154,8 +1287,11 @@ This document shows the translation status of each locale file in the repository
- Text.ConfigureCustomActionControls.Label
- Text.ConfigureCustomActionControls.Options
- Text.ConfigureCustomActionControls.Options.Tip
- Text.ConfigureCustomActionControls.StringFormatter
- Text.ConfigureCustomActionControls.StringFormatter.Tip
- Text.ConfigureCustomActionControls.StringValue.Tip
- Text.ConfigureCustomActionControls.Type
- Text.ConfigureCustomActionControls.UseFriendlyName
- Text.ConfigureWorkspace.Name
- Text.ConfirmEmptyCommit.StageSelectedThenCommit
- Text.ConfirmRestart.Title
@@ -1175,6 +1311,7 @@ This document shows the translation status of each locale file in the repository
- Text.Diff.New
- Text.Diff.Old
- Text.Diff.Submodule.Deleted
- Text.Diff.Submodule.UncommittedChanges
- Text.DirHistories
- Text.DirtyState.HasLocalChanges
- Text.DirtyState.HasPendingPullOrPush
@@ -1189,13 +1326,13 @@ This document shows the translation status of each locale file in the repository
- Text.ExecuteCustomAction.Target
- Text.ExecuteCustomAction.Repository
- Text.FileCM.CustomAction
- Text.GitFlow.FinishWithPush
- Text.GitFlow.FinishWithSquash
- Text.GitLFS.Locks.UnlockAllMyLocks
- Text.GitLFS.Locks.UnlockAllMyLocks.Confirm
- Text.GotoRevisionSelector
- Text.Histories.Header.DateTime
- Text.Histories.ShowColumns
- Text.Hotkeys.Global.OpenLocalRepository
- Text.Hotkeys.Global.ShowWorkspaceDropdownMenu
- Text.Hotkeys.Global.SwitchTab
- Text.Hotkeys.Global.Zoom
@@ -1235,6 +1372,10 @@ This document shows the translation status of each locale file in the repository
- Text.Open
- Text.Open.SystemDefaultEditor
- Text.OpenFile
- Text.OpenLocalRepository
- Text.OpenLocalRepository.Bookmark
- Text.OpenLocalRepository.Group
- Text.OpenLocalRepository.Path
- Text.PageTabBar.Tab.MoveToWorkspace
- Text.PageTabBar.Tab.Refresh
- Text.Preferences.AI.AdditionalPrompt
@@ -1310,6 +1451,8 @@ This document shows the translation status of each locale file in the repository
- Text.Submodule.Status.Unmerged
- Text.Submodule.Update
- Text.Submodule.URL
- Text.SubmoduleRevisionCompare
- Text.SubmoduleRevisionCompare.OpenDetails
- Text.Tag.Tagger
- Text.Tag.Time
- Text.TagCM.CompareTwo

View File

@@ -1 +1 @@
2026.08
2026.09

View File

@@ -2,7 +2,6 @@
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;
@@ -55,7 +54,7 @@ namespace SourceGit.AI
set;
} = string.Empty;
public async Task<List<string>> FetchAvailableModelsAsync()
public void FetchAvailableModels()
{
var allModels = GetOpenAIClient().GetOpenAIModelClient().GetModels();
AvailableModels = new List<string>();
@@ -71,8 +70,6 @@ namespace SourceGit.AI
{
Model = null;
}
return AvailableModels;
}
public ChatClient GetChatClient()
@@ -84,8 +81,8 @@ namespace SourceGit.AI
{
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 });
? new AzureOpenAIClient(new Uri(Server), credential)
: new OpenAIClient(credential, new() { Endpoint = new Uri(Server) });
}
private string _name = string.Empty;

View File

@@ -37,23 +37,61 @@ namespace SourceGit
}
}
public static readonly Command OpenPreferencesCommand = new Command(async _ => await ShowDialog(new Views.Preferences()));
public static readonly Command OpenHotkeysCommand = new Command(async _ => await ShowDialog(new Views.Hotkeys()));
public static readonly Command OpenAppDataDirCommand = new Command(_ => Native.OS.OpenInFileManager(Native.OS.DataDir));
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 OpenPreferencesCommand = new Command(async _ =>
{
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner })
{
var dialog = new Views.Preferences();
await dialog.ShowDialog(owner);
}
});
public static readonly Command OpenHotkeysCommand = new Command(async _ =>
{
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner })
{
var dialog = new Views.Hotkeys();
await dialog.ShowDialog(owner);
}
});
public static readonly Command OpenAppDataDirCommand = new Command(_ =>
{
Native.OS.OpenInFileManager(Native.OS.DataDir);
});
public static readonly Command OpenAboutCommand = new Command(async _ =>
{
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner })
{
var dialog = new Views.About();
await dialog.ShowDialog(owner);
}
});
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 HideAppCommand = new Command(_ =>
{
if (Current is App app && app.TryGetFeature(typeof(IActivatableLifetime)) is IActivatableLifetime lifetime)
lifetime.TryEnterBackground();
Native.OS.HideSelf();
});
public static readonly Command ShowAppCommand = new Command(_ =>
public static readonly Command HideOtherApplicationsCommand = new Command(_ =>
{
if (Current is App app && app.TryGetFeature(typeof(IActivatableLifetime)) is IActivatableLifetime lifetime)
lifetime.TryLeaveBackground();
Native.OS.HideOtherApplications();
});
public static readonly Command ShowAllApplicationsCommand = new Command(_ =>
{
Native.OS.ShowAllApplications();
});
}
}

View File

@@ -1,5 +1,8 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Avalonia.Media;
namespace SourceGit
{
@@ -14,6 +17,47 @@ namespace SourceGit
{
return value.Replace("\"", "\\\"", StringComparison.Ordinal);
}
public static string FormatFontNames(string input)
{
if (string.IsNullOrEmpty(input))
return string.Empty;
var parts = input.Split(',');
var trimmed = new List<string>();
foreach (var part in parts)
{
var t = part.Trim();
if (string.IsNullOrEmpty(t))
continue;
var sb = new StringBuilder();
var prevChar = '\0';
foreach (var c in t)
{
if (c == ' ' && prevChar == ' ')
continue;
sb.Append(c);
prevChar = c;
}
var name = sb.ToString();
try
{
var fontFamily = FontFamily.Parse(name);
if (fontFamily.FamilyTypefaces.Count > 0)
trimmed.Add(name);
}
catch
{
// Ignore exceptions.
}
}
return trimmed.Count > 0 ? string.Join(',', trimmed) : string.Empty;
}
}
public static class CommandExtensions

View File

@@ -45,7 +45,8 @@
<NativeMenuItem Header="{DynamicResource Text.OpenAppDataDir}" Command="{x:Static s:App.OpenAppDataDirCommand}"/>
<NativeMenuItemSeparator/>
<NativeMenuItem Header="{DynamicResource Text.App.Hide}" Command="{x:Static s:App.HideAppCommand}" Gesture="⌘+H"/>
<NativeMenuItem Header="{DynamicResource Text.App.ShowAll}" Command="{x:Static s:App.ShowAppCommand}"/>
<NativeMenuItem Header="{DynamicResource Text.App.HideOthers}" Command="{x:Static s:App.HideOtherApplicationsCommand}" Gesture="⌘+Alt+H"/>
<NativeMenuItem Header="{DynamicResource Text.App.ShowAll}" Command="{x:Static s:App.ShowAllApplicationsCommand}"/>
<NativeMenuItemSeparator/>
<NativeMenuItem Header="{DynamicResource Text.Quit}" Command="{x:Static s:App.QuitCommand}" Gesture="⌘+Q"/>
</NativeMenu>

View File

@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
@@ -75,79 +73,6 @@ namespace SourceGit
#endregion
#region Utility Functions
public static Task ShowDialog(object data, Window owner = null)
{
if (owner == null)
{
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } mainWindow })
owner = mainWindow;
else
return null;
}
if (data is Views.ChromelessWindow window)
return window.ShowDialog(owner);
window = Views.ControlExtensions.CreateFromViewModels(data) as Views.ChromelessWindow;
if (window != null)
{
window.DataContext = data;
return window.ShowDialog(owner);
}
return null;
}
public static void ShowWindow(object data)
{
if (data is not Views.ChromelessWindow window)
{
window = Views.ControlExtensions.CreateFromViewModels(data) as Views.ChromelessWindow;
if (window == null)
return;
window.DataContext = data;
}
do
{
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { Windows: { Count: > 0 } windows })
{
// Try to find the actived window (fall back to `MainWindow`)
Window actived = windows[0];
if (!actived.IsActive)
{
for (var i = 1; i < windows.Count; i++)
{
var test = windows[i];
if (test.IsActive)
{
actived = test;
break;
}
}
}
// Get the screen where current window locates.
var screen = actived.Screens.ScreenFromWindow(actived) ?? actived.Screens.Primary;
if (screen == null)
break;
// Calculate the startup position (Center Screen Mode) of target window
var rect = new PixelRect(PixelSize.FromSize(window.ClientSize, actived.DesktopScaling));
var centeredRect = screen.WorkingArea.CenterRect(rect);
if (actived.Screens.ScreenFromPoint(centeredRect.Position) == null)
break;
// Use the startup position
window.WindowStartupLocation = WindowStartupLocation.Manual;
window.Position = centeredRect.Position;
}
} while (false);
window.Show();
}
public static async Task<bool> AskConfirmAsync(string message, Models.ConfirmButtonType buttonType = Models.ConfirmButtonType.OkCancel)
{
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner })
@@ -253,8 +178,8 @@ namespace SourceGit
app._fontsOverrides = null;
}
defaultFont = app.FixFontFamilyName(defaultFont);
monospaceFont = app.FixFontFamilyName(monospaceFont);
defaultFont = StringExtensions.FormatFontNames(defaultFont);
monospaceFont = StringExtensions.FormatFontNames(monospaceFont);
var resDic = new ResourceDictionary();
if (!string.IsNullOrEmpty(defaultFont))
@@ -303,15 +228,9 @@ namespace SourceGit
public static void Quit(int exitCode)
{
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.ShutdownMode = ShutdownMode.OnExplicitShutdown;
desktop.MainWindow?.Close();
desktop.Shutdown(exitCode);
}
else
{
Environment.Exit(exitCode);
}
}
#endregion
@@ -547,10 +466,27 @@ namespace SourceGit
var pref = ViewModels.Preferences.Instance;
pref.SetCanModify();
pref.UpdateAvailableAIModels();
_launcher = new ViewModels.Launcher(startupRepo);
desktop.MainWindow = new Views.Launcher() { DataContext = _launcher };
desktop.ShutdownMode = ShutdownMode.OnMainWindowClose;
desktop.ShutdownMode = ShutdownMode.OnExplicitShutdown;
// Fix macOS crash when quiting from Dock
if (OperatingSystem.IsMacOS())
{
desktop.ShutdownRequested += (_, e) =>
{
e.Cancel = true;
Dispatcher.UIThread.Post(() => Quit(0));
};
}
desktop.Exit += (_, _) =>
{
_ipcChannel?.Dispose();
_ipcChannel = null;
};
_ipcChannel.MessageReceived += repo =>
{
@@ -562,8 +498,6 @@ namespace SourceGit
});
};
desktop.Exit += (_, _) => _ipcChannel.Dispose();
#if !DISABLE_UPDATE_DETECTION
if (pref.ShouldCheck4UpdateOnStartup())
Check4Update();
@@ -619,7 +553,12 @@ namespace SourceGit
{
Dispatcher.UIThread.Invoke(async () =>
{
await ShowDialog(new ViewModels.SelfUpdate { Data = data });
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner })
{
var ctx = new ViewModels.SelfUpdate { Data = data };
var dialog = new Views.SelfUpdate() { DataContext = ctx };
await dialog.ShowDialog(owner);
}
});
}
catch
@@ -629,47 +568,6 @@ namespace SourceGit
}
#endregion
private string FixFontFamilyName(string input)
{
if (string.IsNullOrEmpty(input))
return string.Empty;
var parts = input.Split(',');
var trimmed = new List<string>();
foreach (var part in parts)
{
var t = part.Trim();
if (string.IsNullOrEmpty(t))
continue;
var sb = new StringBuilder();
var prevChar = '\0';
foreach (var c in t)
{
if (c == ' ' && prevChar == ' ')
continue;
sb.Append(c);
prevChar = c;
}
var name = sb.ToString();
try
{
var fontFamily = FontFamily.Parse(name);
if (fontFamily.FamilyTypefaces.Count > 0)
trimmed.Add(name);
}
catch
{
// Ignore exceptions.
}
}
return trimmed.Count > 0 ? string.Join(',', trimmed) : string.Empty;
}
private Models.IpcChannel _ipcChannel = null;
private ViewModels.Launcher _launcher = null;
private ResourceDictionary _activeLocale = null;

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
@@ -46,23 +47,28 @@ namespace SourceGit.Commands
proc.StartInfo = CreateGitStartInfo(true);
proc.Start();
var text = await proc.StandardOutput.ReadToEndAsync().ConfigureAwait(false);
using var ms = new MemoryStream();
await proc.StandardOutput.BaseStream.CopyToAsync(ms, CancellationToken).ConfigureAwait(false);
var bytes = ms.ToArray();
var start = 0;
var end = text.IndexOf('\n', start);
while (end > 0)
while (start < bytes.Length)
{
var line = text[start..end];
ParseLine(line);
var end = Array.IndexOf(bytes, (byte)'\n', start);
if (end < 0)
{
ParseLine(bytes[start..]);
break;
}
ParseLine(bytes[start..end]);
if (_result.IsBinary)
break;
start = end + 1;
end = text.IndexOf('\n', start);
}
if (start < text.Length)
ParseLine(text[start..]);
await proc.WaitForExitAsync().ConfigureAwait(false);
await proc.WaitForExitAsync(CancellationToken).ConfigureAwait(false);
}
catch
{
@@ -82,66 +88,14 @@ namespace SourceGit.Commands
return _result;
}
private void ParseLine(string line)
private void ParseLine(byte[] lineBytes)
{
if (_result.IsBinary)
var line = Encoding.UTF8.GetString(lineBytes);
if (ParseFileModeChange(line))
return;
if (line.StartsWith("old mode ", StringComparison.Ordinal))
{
_result.OldMode = line.Substring(9);
if (ParseLFSChange(line))
return;
}
if (line.StartsWith("new mode ", StringComparison.Ordinal))
{
_result.NewMode = line.Substring(9);
return;
}
if (line.StartsWith("deleted file mode ", StringComparison.Ordinal))
{
_result.OldMode = line.Substring(18);
return;
}
if (line.StartsWith("new file mode ", StringComparison.Ordinal))
{
_result.NewMode = line.Substring(14);
return;
}
if (_result.IsLFS)
{
var ch = line[0];
if (ch == '-')
{
if (line.StartsWith("-oid sha256:", StringComparison.Ordinal))
{
_result.LFSDiff.Old.Oid = line.Substring(12);
}
else if (line.StartsWith("-size ", StringComparison.Ordinal))
{
_result.LFSDiff.Old.Size = long.Parse(line.AsSpan(6));
}
}
else if (ch == '+')
{
if (line.StartsWith("+oid sha256:", StringComparison.Ordinal))
{
_result.LFSDiff.New.Oid = line.Substring(12);
}
else if (line.StartsWith("+size ", StringComparison.Ordinal))
{
_result.LFSDiff.New.Size = long.Parse(line.AsSpan(6));
}
}
else if (line.StartsWith(" size ", StringComparison.Ordinal))
{
_result.LFSDiff.New.Size = _result.LFSDiff.Old.Size = long.Parse(line.AsSpan(6));
}
return;
}
if (_result.TextDiff.Lines.Count == 0)
{
@@ -168,7 +122,7 @@ namespace SourceGit.Commands
_oldLine = int.Parse(match.Groups[1].Value);
_newLine = int.Parse(match.Groups[2].Value);
_last = new Models.TextDiffLine(Models.TextDiffLineType.Indicator, line, 0, 0);
_last = new Models.TextDiffLine(Models.TextDiffLineType.Indicator, line, lineBytes, 0, 0);
_result.TextDiff.Lines.Add(_last);
}
}
@@ -177,7 +131,7 @@ namespace SourceGit.Commands
if (line.Length == 0)
{
ProcessInlineHighlights();
_last = new Models.TextDiffLine(Models.TextDiffLineType.Normal, "", _oldLine, _newLine);
_last = new Models.TextDiffLine(Models.TextDiffLineType.Normal, "", [], _oldLine, _newLine);
_result.TextDiff.Lines.Add(_last);
_oldLine++;
_newLine++;
@@ -187,29 +141,15 @@ namespace SourceGit.Commands
var ch = line[0];
if (ch == '-')
{
if (_oldLine == 1 && _newLine == 0 && line.StartsWith(PREFIX_LFS_DEL, StringComparison.Ordinal))
{
_result.IsLFS = true;
_result.LFSDiff = new Models.LFSDiff();
return;
}
_result.TextDiff.DeletedLines++;
_last = new Models.TextDiffLine(Models.TextDiffLineType.Deleted, line.Substring(1), _oldLine, 0);
_last = new Models.TextDiffLine(Models.TextDiffLineType.Deleted, line.Substring(1), lineBytes[1..], _oldLine, 0);
_deleted.Add(_last);
_oldLine++;
}
else if (ch == '+')
{
if (_oldLine == 0 && _newLine == 1 && line.StartsWith(PREFIX_LFS_NEW, StringComparison.Ordinal))
{
_result.IsLFS = true;
_result.LFSDiff = new Models.LFSDiff();
return;
}
_result.TextDiff.AddedLines++;
_last = new Models.TextDiffLine(Models.TextDiffLineType.Added, line.Substring(1), 0, _newLine);
_last = new Models.TextDiffLine(Models.TextDiffLineType.Added, line.Substring(1), lineBytes[1..], 0, _newLine);
_added.Add(_last);
_newLine++;
}
@@ -221,19 +161,12 @@ namespace SourceGit.Commands
{
_oldLine = int.Parse(match.Groups[1].Value);
_newLine = int.Parse(match.Groups[2].Value);
_last = new Models.TextDiffLine(Models.TextDiffLineType.Indicator, line, 0, 0);
_last = new Models.TextDiffLine(Models.TextDiffLineType.Indicator, line, lineBytes, 0, 0);
_result.TextDiff.Lines.Add(_last);
}
else
{
if (_oldLine == 1 && _newLine == 1 && line.StartsWith(PREFIX_LFS_MODIFY, StringComparison.Ordinal))
{
_result.IsLFS = true;
_result.LFSDiff = new Models.LFSDiff();
return;
}
_last = new Models.TextDiffLine(Models.TextDiffLineType.Normal, line.Substring(1), _oldLine, _newLine);
_last = new Models.TextDiffLine(Models.TextDiffLineType.Normal, line.Substring(1), lineBytes[1..], _oldLine, _newLine);
_result.TextDiff.Lines.Add(_last);
_oldLine++;
_newLine++;
@@ -246,6 +179,70 @@ namespace SourceGit.Commands
}
}
private bool ParseFileModeChange(string line)
{
if (line.StartsWith("old mode ", StringComparison.Ordinal))
{
_result.OldMode = line.Substring(9);
return true;
}
if (line.StartsWith("new mode ", StringComparison.Ordinal))
{
_result.NewMode = line.Substring(9);
return true;
}
if (line.StartsWith("deleted file mode ", StringComparison.Ordinal))
{
_result.OldMode = line.Substring(18);
return true;
}
if (line.StartsWith("new file mode ", StringComparison.Ordinal))
{
_result.NewMode = line.Substring(14);
return true;
}
return false;
}
private bool ParseLFSChange(string line)
{
if (_result.IsLFS)
{
if (line.StartsWith("-oid sha256:", StringComparison.Ordinal))
_result.LFSDiff.Old.Oid = line.Substring(12);
else if (line.StartsWith("-size ", StringComparison.Ordinal))
_result.LFSDiff.Old.Size = long.Parse(line.AsSpan(6));
else if (line.StartsWith("+oid sha256:", StringComparison.Ordinal))
_result.LFSDiff.New.Oid = line.Substring(12);
else if (line.StartsWith("+size ", StringComparison.Ordinal))
_result.LFSDiff.New.Size = long.Parse(line.AsSpan(6));
else if (line.StartsWith(" size ", StringComparison.Ordinal))
_result.LFSDiff.New.Size = _result.LFSDiff.Old.Size = long.Parse(line.AsSpan(6));
return true;
}
if (_result.TextDiff.Lines.Count != 1)
return false;
var isLFS = (_oldLine == 1 && _newLine == 1 && line.StartsWith(PREFIX_LFS_MODIFY, StringComparison.Ordinal)) ||
(_oldLine == 1 && _newLine == 0 && line.StartsWith(PREFIX_LFS_DEL, StringComparison.Ordinal)) ||
(_oldLine == 0 && _newLine == 1 && line.StartsWith(PREFIX_LFS_NEW, StringComparison.Ordinal));
if (isLFS)
{
_result.IsLFS = true;
_result.LFSDiff = new Models.LFSDiff();
return true;
}
return false;
}
private void ProcessInlineHighlights()
{
if (_deleted.Count > 0)

View File

@@ -49,7 +49,7 @@ namespace SourceGit.Commands
return await start.Use(log).ExecAsync().ConfigureAwait(false);
}
public static async Task<bool> FinishAsync(string repo, Models.GitFlowBranchType type, string name, bool squash, bool push, bool keepBranch, Models.ICommandLog log)
public static async Task<bool> FinishAsync(string repo, Models.GitFlowBranchType type, string name, bool squash, bool keepBranch, Models.ICommandLog log)
{
var builder = new StringBuilder();
builder.Append("flow ");
@@ -73,8 +73,6 @@ namespace SourceGit.Commands
builder.Append(" finish ");
if (squash)
builder.Append("--squash ");
if (push)
builder.Append("--push ");
if (keepBranch)
builder.Append("-k ");
builder.Append(name);

View File

@@ -12,7 +12,7 @@ namespace SourceGit.Commands
WorkingDirectory = repo;
Context = repo;
Args = $"log --topo-order --cherry-pick --right-only --no-merges --no-show-signature --decorate=full --format=\"%H%n%P%n%D%n%aN±%aE%n%at%n%cN±%cE%n%ct%n%B%n{_boundary}\" {on}...HEAD";
Args = $"log --topo-order --cherry-pick --right-only --no-merges --no-show-signature --decorate=full --format=\"%H%n%P%n%D%n%aN±%aE%n%at%n%cN±%cE%n%ct%n%s%n%B%n{_boundary}\" {on}...HEAD";
}
public async Task<List<Models.InteractiveCommit>> GetResultAsync()
@@ -58,6 +58,9 @@ namespace SourceGit.Commands
case 6:
current.Commit.CommitterTime = ulong.Parse(line);
break;
case 7:
current.Commit.Subject = line;
break;
default:
var boundary = rs.StdOut.IndexOf(_boundary, end + 1, StringComparison.Ordinal);
if (boundary > end)

View File

@@ -19,6 +19,8 @@ namespace SourceGit.Models
PathSelector,
CheckBox,
ComboBox,
LocalBranchSelector,
RemoteBranchSelector,
}
public record CustomActionTargetFile(string File, Commit Revision);
@@ -49,6 +51,12 @@ namespace SourceGit.Models
set => SetProperty(ref _stringValue, value);
}
public string StringFormatter
{
get => _stringFormatter;
set => SetProperty(ref _stringFormatter, value);
}
public bool BoolValue
{
get => _boolValue;
@@ -59,6 +67,7 @@ namespace SourceGit.Models
private string _label = string.Empty;
private string _description = string.Empty;
private string _stringValue = string.Empty;
private string _stringFormatter = string.Empty;
private bool _boolValue = false;
}

View File

@@ -23,6 +23,7 @@ namespace SourceGit.Models
public class TextDiffLine
{
public TextDiffLineType Type { get; set; } = TextDiffLineType.None;
public byte[] RawContent { get; set; } = [];
public string Content { get; set; } = "";
public int OldLineNumber { get; set; } = 0;
public int NewLineNumber { get; set; } = 0;
@@ -33,10 +34,11 @@ namespace SourceGit.Models
public string NewLine => NewLineNumber == 0 ? string.Empty : NewLineNumber.ToString();
public TextDiffLine() { }
public TextDiffLine(TextDiffLineType type, string content, int oldLine, int newLine)
public TextDiffLine(TextDiffLineType type, string line, byte[] rawContent, int oldLine, int newLine)
{
Type = type;
Content = content;
Content = line;
RawContent = rawContent;
OldLineNumber = oldLine;
NewLineNumber = newLine;
}
@@ -97,19 +99,19 @@ namespace SourceGit.Models
return rs;
}
public void GenerateNewPatchFromSelection(Change change, string fileBlobGuid, TextDiffSelection selection, bool revert, string output)
public void GenerateNewPatchFromSelection(string file, string fileBlobGuid, TextDiffSelection selection, bool revert, string output)
{
var isTracked = !string.IsNullOrEmpty(fileBlobGuid);
var fileGuid = isTracked ? fileBlobGuid : "00000000";
using var writer = new StreamWriter(output);
writer.NewLine = "\n";
writer.WriteLine($"diff --git a/{change.Path} b/{change.Path}");
writer.WriteLine($"diff --git a/{file} b/{file}");
if (!revert && !isTracked)
writer.WriteLine("new file mode 100644");
writer.WriteLine($"index 00000000...{fileGuid}");
writer.WriteLine($"--- {(revert || isTracked ? $"a/{change.Path}" : "/dev/null")}");
writer.WriteLine($"+++ b/{change.Path}");
writer.WriteLine($"--- {(revert || isTracked ? $"a/{file}" : "/dev/null")}");
writer.WriteLine($"+++ b/{file}");
var additions = selection.EndLine - selection.StartLine;
if (selection.StartLine != 1)
@@ -146,19 +148,17 @@ namespace SourceGit.Models
writer.Flush();
}
public void GeneratePatchFromSelection(Change change, string fileTreeGuid, TextDiffSelection selection, bool revert, string output)
public void GeneratePatchFromSelection(string file, string fileTreeGuid, TextDiffSelection selection, bool revert, string output)
{
var orgFile = !string.IsNullOrEmpty(change.OriginalPath) ? change.OriginalPath : change.Path;
using var writer = new StreamWriter(output);
writer.NewLine = "\n";
writer.WriteLine($"diff --git a/{change.Path} b/{change.Path}");
writer.WriteLine($"diff --git a/{file} b/{file}");
writer.WriteLine($"index 00000000...{fileTreeGuid} 100644");
writer.WriteLine($"--- a/{orgFile}");
writer.WriteLine($"+++ b/{change.Path}");
writer.WriteLine($"--- a/{file}");
writer.WriteLine($"+++ b/{file}");
// If last line of selection is a change. Find one more line.
string tail = null;
TextDiffLine tail = null;
if (selection.EndLine < Lines.Count)
{
var lastLine = Lines[selection.EndLine - 1];
@@ -173,7 +173,7 @@ namespace SourceGit.Models
(revert && line.Type == TextDiffLineType.Added) ||
(!revert && line.Type == TextDiffLineType.Deleted))
{
tail = line.Content;
tail = line;
break;
}
}
@@ -256,24 +256,22 @@ namespace SourceGit.Models
}
}
if (!string.IsNullOrEmpty(tail))
writer.WriteLine($" {tail}");
if (tail != null)
WriteLine(writer, ' ', tail);
writer.Flush();
}
public void GeneratePatchFromSelectionSingleSide(Change change, string fileTreeGuid, TextDiffSelection selection, bool revert, bool isOldSide, string output)
public void GeneratePatchFromSelectionSingleSide(string file, string fileTreeGuid, TextDiffSelection selection, bool revert, bool isOldSide, string output)
{
var orgFile = !string.IsNullOrEmpty(change.OriginalPath) ? change.OriginalPath : change.Path;
using var writer = new StreamWriter(output);
writer.NewLine = "\n";
writer.WriteLine($"diff --git a/{change.Path} b/{change.Path}");
writer.WriteLine($"diff --git a/{file} b/{file}");
writer.WriteLine($"index 00000000...{fileTreeGuid} 100644");
writer.WriteLine($"--- a/{orgFile}");
writer.WriteLine($"+++ b/{change.Path}");
writer.WriteLine($"--- a/{file}");
writer.WriteLine($"+++ b/{file}");
// If last line of selection is a change. Find one more line.
string tail = null;
TextDiffLine tail = null;
if (selection.EndLine < Lines.Count)
{
var lastLine = Lines[selection.EndLine - 1];
@@ -288,7 +286,7 @@ namespace SourceGit.Models
{
if (line.Type == TextDiffLineType.Normal || line.Type == TextDiffLineType.Added)
{
tail = line.Content;
tail = line;
break;
}
}
@@ -296,7 +294,7 @@ namespace SourceGit.Models
{
if (line.Type == TextDiffLineType.Normal || line.Type == TextDiffLineType.Deleted)
{
tail = line.Content;
tail = line;
break;
}
}
@@ -408,8 +406,8 @@ namespace SourceGit.Models
}
}
if (!string.IsNullOrEmpty(tail))
writer.WriteLine($" {tail}");
if (tail != null)
WriteLine(writer, ' ', tail);
writer.Flush();
}
@@ -564,7 +562,11 @@ namespace SourceGit.Models
private static void WriteLine(StreamWriter writer, char prefix, TextDiffLine line)
{
writer.WriteLine($"{prefix}{line.Content}");
writer.Flush();
writer.BaseStream.WriteByte((byte)prefix);
writer.BaseStream.Write(line.RawContent);
writer.BaseStream.WriteByte((byte)'\n');
if (line.NoNewLineEndOfFile)
writer.WriteLine("\\ No newline at end of file");
@@ -602,8 +604,13 @@ namespace SourceGit.Models
public class SubmoduleDiff
{
public string FullPath { get; set; } = string.Empty;
public RevisionSubmodule Old { get; set; } = null;
public RevisionSubmodule New { get; set; } = null;
public bool CanOpenDetails => File.Exists(Path.Combine(FullPath, ".git")) &&
Old != null && Old.Commit.Author != User.Invalid &&
New != null && New.Commit.Author != User.Invalid;
}
public class DiffResult

View File

@@ -39,5 +39,6 @@ namespace SourceGit.Models
{
public Commit Commit { get; set; } = null;
public CommitFullMessage FullMessage { get; set; } = null;
public int UncommittedChanges { get; set; } = 0;
}
}

View File

@@ -33,6 +33,21 @@ namespace SourceGit.Native
}
}
public void HideSelf()
{
// Do Nothing. Never used.
}
public void HideOtherApplications()
{
// Do Nothing. Never used.
}
public void ShowAllApplications()
{
// Do Nothing. Never used.
}
public string GetDataDir()
{
// AppImage supports portable mode

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using Avalonia;
@@ -13,6 +14,18 @@ namespace SourceGit.Native
[SupportedOSPlatform("macOS")]
internal class MacOS : OS.IBackend
{
[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_getClass")]
public static extern IntPtr objc_getClass(string name);
[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "sel_registerName")]
public static extern IntPtr sel_registerName(string name);
[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
public static extern IntPtr objc_msgSend(IntPtr receiver, IntPtr selector);
[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
public static extern IntPtr objc_msgSendWithArg(IntPtr receiver, IntPtr selector, IntPtr arg);
public void SetupApp(AppBuilder builder)
{
builder.With(new MacOSPlatformOptions()
@@ -44,6 +57,39 @@ namespace SourceGit.Native
window.ExtendClientAreaToDecorationsHint = true;
}
public void HideSelf()
{
IntPtr nsApplicationClass = objc_getClass("NSApplication");
IntPtr nsSharedApplicationSelector = sel_registerName("sharedApplication");
IntPtr nsApp = objc_msgSend(nsApplicationClass, nsSharedApplicationSelector);
IntPtr nsMethodSelector = sel_registerName("hide:");
IntPtr nsDelegateSelector = sel_registerName("delegate");
IntPtr nsDelegate = objc_msgSend(nsApp, nsDelegateSelector);
objc_msgSendWithArg(nsApp, nsMethodSelector, nsDelegate);
}
public void HideOtherApplications()
{
IntPtr nsApplicationClass = objc_getClass("NSApplication");
IntPtr nsSharedApplicationSelector = sel_registerName("sharedApplication");
IntPtr nsApp = objc_msgSend(nsApplicationClass, nsSharedApplicationSelector);
IntPtr nsMethodSelector = sel_registerName("hideOtherApplications:");
IntPtr nsDelegateSelector = sel_registerName("delegate");
IntPtr nsDelegate = objc_msgSend(nsApp, nsDelegateSelector);
objc_msgSendWithArg(nsApp, nsMethodSelector, nsDelegate);
}
public void ShowAllApplications()
{
IntPtr nsApplicationClass = objc_getClass("NSApplication");
IntPtr nsSharedApplicationSelector = sel_registerName("sharedApplication");
IntPtr nsApp = objc_msgSend(nsApplicationClass, nsSharedApplicationSelector);
IntPtr nsMethodSelector = sel_registerName("unhideAllApplications:");
IntPtr nsDelegateSelector = sel_registerName("delegate");
IntPtr nsDelegate = objc_msgSend(nsApp, nsDelegateSelector);
objc_msgSendWithArg(nsApp, nsMethodSelector, nsDelegate);
}
public string GetDataDir()
{
return Path.Combine(

View File

@@ -18,6 +18,10 @@ namespace SourceGit.Native
void SetupApp(AppBuilder builder);
void SetupWindow(Window window);
void HideSelf();
void HideOtherApplications();
void ShowAllApplications();
string GetDataDir();
string FindGitExecutable();
string FindTerminal(Models.ShellOrTerminal shell);
@@ -154,6 +158,21 @@ namespace SourceGit.Native
_backend.SetupWindow(window);
}
public static void HideSelf()
{
_backend.HideSelf();
}
public static void HideOtherApplications()
{
_backend.HideOtherApplications();
}
public static void ShowAllApplications()
{
_backend.ShowAllApplications();
}
public static void LogException(Exception ex)
{
if (ex == null)

View File

@@ -58,6 +58,21 @@ namespace SourceGit.Native
window.BorderThickness = new Thickness(1);
}
public void HideSelf()
{
// Do Nothing. Never used.
}
public void HideOtherApplications()
{
// Do Nothing. Never used.
}
public void ShowAllApplications()
{
// Do Nothing. Never used.
}
public string GetDataDir()
{
var execFile = Process.GetCurrentProcess().MainModule!.FileName;

View File

@@ -425,7 +425,6 @@ $1, $2, … Werte der Eingabe-Steuerelemente</x:String>
<x:String x:Key="Text.GitFlow.FinishHotfix" xml:space="preserve">FLOW Hotfix fertigstellen</x:String>
<x:String x:Key="Text.GitFlow.FinishRelease" xml:space="preserve">FLOW Release fertigstellen</x:String>
<x:String x:Key="Text.GitFlow.FinishTarget" xml:space="preserve">Ziel:</x:String>
<x:String x:Key="Text.GitFlow.FinishWithPush" xml:space="preserve">Push zu Remote(s) nach Abschluss</x:String>
<x:String x:Key="Text.GitFlow.FinishWithSquash" xml:space="preserve">Squash beim Merge</x:String>
<x:String x:Key="Text.GitFlow.Hotfix" xml:space="preserve">Hotfix:</x:String>
<x:String x:Key="Text.GitFlow.HotfixPrefix" xml:space="preserve">Hotfix-Präfix:</x:String>

View File

@@ -23,6 +23,7 @@
<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>
<x:String x:Key="Text.App.Hide" xml:space="preserve">Hide SourceGit</x:String>
<x:String x:Key="Text.App.HideOthers" xml:space="preserve">Hide Others</x:String>
<x:String x:Key="Text.App.ShowAll" xml:space="preserve">Show All</x:String>
<x:String x:Key="Text.Apply" xml:space="preserve">Patch</x:String>
<x:String x:Key="Text.Apply.3Way" xml:space="preserve">3-Way Merge</x:String>
@@ -130,6 +131,8 @@
<x:String x:Key="Text.Clone" xml:space="preserve">Clone Remote Repository</x:String>
<x:String x:Key="Text.Clone.AdditionalParam" xml:space="preserve">Extra Parameters:</x:String>
<x:String x:Key="Text.Clone.AdditionalParam.Placeholder" xml:space="preserve">Additional arguments to clone repository. Optional.</x:String>
<x:String x:Key="Text.Clone.Bookmark" xml:space="preserve">Bookmark:</x:String>
<x:String x:Key="Text.Clone.Group" xml:space="preserve">Group:</x:String>
<x:String x:Key="Text.Clone.LocalName" xml:space="preserve">Local Name:</x:String>
<x:String x:Key="Text.Clone.LocalName.Placeholder" xml:space="preserve">Repository name. Optional.</x:String>
<x:String x:Key="Text.Clone.ParentFolder" xml:space="preserve">Parent Folder:</x:String>
@@ -275,8 +278,11 @@
<x:String x:Key="Text.ConfigureCustomActionControls.Label" xml:space="preserve">Label:</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.Options" xml:space="preserve">Options:</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.Options.Tip" xml:space="preserve">Use '|' as delimiter for options</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.StringFormatter" xml:space="preserve">String Formatter:</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.StringFormatter.Tip" xml:space="preserve">Optional. Used to format output string. Ignored when input is empty. Please use `${VALUE}` to represent the input string.</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.StringValue.Tip" xml:space="preserve">The built-in variables ${REPO}, ${REMOTE}, ${BRANCH}, ${BRANCH_FRIENDLY_NAME}, ${SHA}, ${FILE}, and ${TAG} remain available here</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.Type" xml:space="preserve">Type:</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.UseFriendlyName" xml:space="preserve">Use Friendly Name:</x:String>
<x:String x:Key="Text.ConfigureWorkspace" xml:space="preserve">Workspaces</x:String>
<x:String x:Key="Text.ConfigureWorkspace.Color" xml:space="preserve">Color</x:String>
<x:String x:Key="Text.ConfigureWorkspace.Name" xml:space="preserve">Name</x:String>
@@ -370,6 +376,7 @@
<x:String x:Key="Text.Diff.Submodule" xml:space="preserve">SUBMODULE</x:String>
<x:String x:Key="Text.Diff.Submodule.Deleted" xml:space="preserve">DELETED</x:String>
<x:String x:Key="Text.Diff.Submodule.New" xml:space="preserve">NEW</x:String>
<x:String x:Key="Text.Diff.Submodule.UncommittedChanges" xml:space="preserve">+ {0} Uncommitted Changes</x:String>
<x:String x:Key="Text.Diff.SwapCommits" xml:space="preserve">Swap</x:String>
<x:String x:Key="Text.Diff.SyntaxHighlight" xml:space="preserve">Syntax Highlighting</x:String>
<x:String x:Key="Text.Diff.ToggleWordWrap" xml:space="preserve">Line Word Wrap</x:String>
@@ -433,7 +440,6 @@
<x:String x:Key="Text.GitFlow.FinishHotfix" xml:space="preserve">FLOW - Finish Hotfix</x:String>
<x:String x:Key="Text.GitFlow.FinishRelease" xml:space="preserve">FLOW - Finish Release</x:String>
<x:String x:Key="Text.GitFlow.FinishTarget" xml:space="preserve">Target:</x:String>
<x:String x:Key="Text.GitFlow.FinishWithPush" xml:space="preserve">Push to remote(s) after performing finish</x:String>
<x:String x:Key="Text.GitFlow.FinishWithSquash" xml:space="preserve">Squash during merge</x:String>
<x:String x:Key="Text.GitFlow.Hotfix" xml:space="preserve">Hotfix:</x:String>
<x:String x:Key="Text.GitFlow.HotfixPrefix" xml:space="preserve">Hotfix Prefix:</x:String>
@@ -499,6 +505,7 @@
<x:String x:Key="Text.Hotkeys.Global.GotoNextTab" xml:space="preserve">Go to next tab</x:String>
<x:String x:Key="Text.Hotkeys.Global.GotoPrevTab" xml:space="preserve">Go to previous tab</x:String>
<x:String x:Key="Text.Hotkeys.Global.NewTab" xml:space="preserve">Create new tab</x:String>
<x:String x:Key="Text.Hotkeys.Global.OpenLocalRepository" xml:space="preserve">Open local repository</x:String>
<x:String x:Key="Text.Hotkeys.Global.OpenPreferences" xml:space="preserve">Open Preferences dialog</x:String>
<x:String x:Key="Text.Hotkeys.Global.ShowWorkspaceDropdownMenu" xml:space="preserve">Show workspace dropdown menu</x:String>
<x:String x:Key="Text.Hotkeys.Global.SwitchTab" xml:space="preserve">Switch active tab</x:String>
@@ -592,6 +599,10 @@
<x:String x:Key="Text.OpenAppDataDir" xml:space="preserve">Open Data Storage Directory</x:String>
<x:String x:Key="Text.OpenFile" xml:space="preserve">Open File</x:String>
<x:String x:Key="Text.OpenInExternalMergeTool" xml:space="preserve">Open in External Merge Tool</x:String>
<x:String x:Key="Text.OpenLocalRepository" xml:space="preserve">Open Local Repository</x:String>
<x:String x:Key="Text.OpenLocalRepository.Bookmark" xml:space="preserve">Bookmark:</x:String>
<x:String x:Key="Text.OpenLocalRepository.Group" xml:space="preserve">Group:</x:String>
<x:String x:Key="Text.OpenLocalRepository.Path" xml:space="preserve">Folder:</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.Close" xml:space="preserve">Close Tab</x:String>
@@ -898,6 +909,8 @@
<x:String x:Key="Text.Submodule.Status.Unmerged" xml:space="preserve">unmerged</x:String>
<x:String x:Key="Text.Submodule.Update" xml:space="preserve">Update</x:String>
<x:String x:Key="Text.Submodule.URL" xml:space="preserve">URL</x:String>
<x:String x:Key="Text.SubmoduleRevisionCompare" xml:space="preserve">Submodule Change Details</x:String>
<x:String x:Key="Text.SubmoduleRevisionCompare.OpenDetails" xml:space="preserve">OPEN DETAILS</x:String>
<x:String x:Key="Text.Sure" xml:space="preserve">OK</x:String>
<x:String x:Key="Text.Tag.Tagger" xml:space="preserve">TAGGER</x:String>
<x:String x:Key="Text.Tag.Time" xml:space="preserve">TIME</x:String>

View File

@@ -27,6 +27,7 @@
<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>
<x:String x:Key="Text.App.Hide" xml:space="preserve">Ocultar SourceGit</x:String>
<x:String x:Key="Text.App.HideOthers" xml:space="preserve">Ocultar Otros</x:String>
<x:String x:Key="Text.App.ShowAll" xml:space="preserve">Mostrar Todo</x:String>
<x:String x:Key="Text.Apply" xml:space="preserve">Aplicar Parche</x:String>
<x:String x:Key="Text.Apply.3Way" xml:space="preserve">Merge a 3 vías (3-Way)</x:String>
@@ -134,6 +135,8 @@
<x:String x:Key="Text.Clone" xml:space="preserve">Clonar Repositorio Remoto</x:String>
<x:String x:Key="Text.Clone.AdditionalParam" xml:space="preserve">Parámetros Adicionales:</x:String>
<x:String x:Key="Text.Clone.AdditionalParam.Placeholder" xml:space="preserve">Argumentos adicionales para clonar el repositorio. Opcional.</x:String>
<x:String x:Key="Text.Clone.Bookmark" xml:space="preserve">Marcador:</x:String>
<x:String x:Key="Text.Clone.Group" xml:space="preserve">Grupo:</x:String>
<x:String x:Key="Text.Clone.LocalName" xml:space="preserve">Nombre Local:</x:String>
<x:String x:Key="Text.Clone.LocalName.Placeholder" xml:space="preserve">Nombre del repositorio. Opcional.</x:String>
<x:String x:Key="Text.Clone.ParentFolder" xml:space="preserve">Carpeta Padre:</x:String>
@@ -279,8 +282,11 @@
<x:String x:Key="Text.ConfigureCustomActionControls.Label" xml:space="preserve">Etiqueta:</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.Options" xml:space="preserve">Opciones:</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.Options.Tip" xml:space="preserve">Usar '|' como delimitador para las opciones</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.StringFormatter" xml:space="preserve">Formateador de Cadenas de texto:</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.StringFormatter.Tip" xml:space="preserve">Opcional. Utilizado para formatear la cadena de salida. Ignorado cuando la entrada está vacía. Por favor utiliza `${VALUE}` para representar la cadena de entrada.</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.StringValue.Tip" xml:space="preserve">La variables incorporadas ${REPO}, ${REMOTE}, ${BRANCH}, ${BRANCH_FRIENDLY_NAME}, ${SHA}, ${FILE}, y ${TAG} permanecen disponibles aquí</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.Type" xml:space="preserve">Tipo:</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.UseFriendlyName" xml:space="preserve">Utilizar Nombre Amigable:</x:String>
<x:String x:Key="Text.ConfigureWorkspace" xml:space="preserve">Espacios de Trabajo</x:String>
<x:String x:Key="Text.ConfigureWorkspace.Color" xml:space="preserve">Color</x:String>
<x:String x:Key="Text.ConfigureWorkspace.Name" xml:space="preserve">Nombre</x:String>
@@ -374,6 +380,7 @@
<x:String x:Key="Text.Diff.Submodule" xml:space="preserve">SUBMÓDULO</x:String>
<x:String x:Key="Text.Diff.Submodule.Deleted" xml:space="preserve">BORRADO</x:String>
<x:String x:Key="Text.Diff.Submodule.New" xml:space="preserve">NUEVO</x:String>
<x:String x:Key="Text.Diff.Submodule.UncommittedChanges" xml:space="preserve">+ {0} Cambios Sin confirmar</x:String>
<x:String x:Key="Text.Diff.SwapCommits" xml:space="preserve">Intercambiar</x:String>
<x:String x:Key="Text.Diff.SyntaxHighlight" xml:space="preserve">Resaltado de Sintaxis</x:String>
<x:String x:Key="Text.Diff.ToggleWordWrap" xml:space="preserve">Ajuste de Línea</x:String>
@@ -437,7 +444,6 @@
<x:String x:Key="Text.GitFlow.FinishHotfix" xml:space="preserve">FLOW - Finalizar Hotfix</x:String>
<x:String x:Key="Text.GitFlow.FinishRelease" xml:space="preserve">FLOW - Finalizar Release</x:String>
<x:String x:Key="Text.GitFlow.FinishTarget" xml:space="preserve">Destino:</x:String>
<x:String x:Key="Text.GitFlow.FinishWithPush" xml:space="preserve">Push al/los remoto(s) después de Finalizar</x:String>
<x:String x:Key="Text.GitFlow.FinishWithSquash" xml:space="preserve">Squash durante el merge</x:String>
<x:String x:Key="Text.GitFlow.Hotfix" xml:space="preserve">Hotfix:</x:String>
<x:String x:Key="Text.GitFlow.HotfixPrefix" xml:space="preserve">Prefijo de Hotfix:</x:String>
@@ -503,6 +509,7 @@
<x:String x:Key="Text.Hotkeys.Global.GotoNextTab" xml:space="preserve">Ir a la siguiente página</x:String>
<x:String x:Key="Text.Hotkeys.Global.GotoPrevTab" xml:space="preserve">Ir a la página anterior</x:String>
<x:String x:Key="Text.Hotkeys.Global.NewTab" xml:space="preserve">Crear nueva página</x:String>
<x:String x:Key="Text.Hotkeys.Global.OpenLocalRepository" xml:space="preserve">Abrir repositorio local</x:String>
<x:String x:Key="Text.Hotkeys.Global.OpenPreferences" xml:space="preserve">Abrir diálogo de preferencias</x:String>
<x:String x:Key="Text.Hotkeys.Global.ShowWorkspaceDropdownMenu" xml:space="preserve">Mostrar menú desplegable del espacio de trabajo</x:String>
<x:String x:Key="Text.Hotkeys.Global.SwitchTab" xml:space="preserve">Cambiar página activa</x:String>
@@ -596,6 +603,10 @@
<x:String x:Key="Text.OpenAppDataDir" xml:space="preserve">Abrir Directorio de Datos de la App</x:String>
<x:String x:Key="Text.OpenFile" xml:space="preserve">Abrir Archivo</x:String>
<x:String x:Key="Text.OpenInExternalMergeTool" xml:space="preserve">Abrir en Herramienta de Merge</x:String>
<x:String x:Key="Text.OpenLocalRepository" xml:space="preserve">Abrir Repositorio Local</x:String>
<x:String x:Key="Text.OpenLocalRepository.Bookmark" xml:space="preserve">Marcador:</x:String>
<x:String x:Key="Text.OpenLocalRepository.Group" xml:space="preserve">Grupo:</x:String>
<x:String x:Key="Text.OpenLocalRepository.Path" xml:space="preserve">Carpeta:</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.Close" xml:space="preserve">Cerrar Pestaña</x:String>
@@ -902,6 +913,8 @@
<x:String x:Key="Text.Submodule.Status.Unmerged" xml:space="preserve">unmerged</x:String>
<x:String x:Key="Text.Submodule.Update" xml:space="preserve">Actualizar</x:String>
<x:String x:Key="Text.Submodule.URL" xml:space="preserve">URL</x:String>
<x:String x:Key="Text.SubmoduleRevisionCompare" xml:space="preserve">Cambiar Detalles del Submódulo</x:String>
<x:String x:Key="Text.SubmoduleRevisionCompare.OpenDetails" xml:space="preserve">ABRIR DETALLES</x:String>
<x:String x:Key="Text.Sure" xml:space="preserve">OK</x:String>
<x:String x:Key="Text.Tag.Tagger" xml:space="preserve">ETIQUETADOR</x:String>
<x:String x:Key="Text.Tag.Time" xml:space="preserve">HORA</x:String>

View File

@@ -409,7 +409,6 @@
<x:String x:Key="Text.GitFlow.FinishHotfix" xml:space="preserve">FLOW - Terminer Hotfix</x:String>
<x:String x:Key="Text.GitFlow.FinishRelease" xml:space="preserve">FLOW - Terminer Release</x:String>
<x:String x:Key="Text.GitFlow.FinishTarget" xml:space="preserve">Cible:</x:String>
<x:String x:Key="Text.GitFlow.FinishWithPush" xml:space="preserve">Pousser vers le(s) dépôt(s) distant(s) après avoir terminé</x:String>
<x:String x:Key="Text.GitFlow.FinishWithSquash" xml:space="preserve">Squash lors de la fusion</x:String>
<x:String x:Key="Text.GitFlow.Hotfix" xml:space="preserve">Hotfix:</x:String>
<x:String x:Key="Text.GitFlow.HotfixPrefix" xml:space="preserve">Hotfix Prefix:</x:String>

View File

@@ -390,7 +390,6 @@
<x:String x:Key="Text.GitFlow.FinishHotfix" xml:space="preserve">FLOW - Selesaikan Hotfix</x:String>
<x:String x:Key="Text.GitFlow.FinishRelease" xml:space="preserve">FLOW - Selesaikan Release</x:String>
<x:String x:Key="Text.GitFlow.FinishTarget" xml:space="preserve">Target:</x:String>
<x:String x:Key="Text.GitFlow.FinishWithPush" xml:space="preserve">Push ke remote setelah selesai</x:String>
<x:String x:Key="Text.GitFlow.FinishWithSquash" xml:space="preserve">Squash saat merge</x:String>
<x:String x:Key="Text.GitFlow.Hotfix" xml:space="preserve">Hotfix:</x:String>
<x:String x:Key="Text.GitFlow.HotfixPrefix" xml:space="preserve">Prefix Hotfix:</x:String>

View File

@@ -424,7 +424,6 @@ ${pure_files:N} Come ${files:N}, ma senza cartelle</x:String>
<x:String x:Key="Text.GitFlow.FinishHotfix" xml:space="preserve">FLOW - Completa Hotfix</x:String>
<x:String x:Key="Text.GitFlow.FinishRelease" xml:space="preserve">FLOW - Completa Rilascio</x:String>
<x:String x:Key="Text.GitFlow.FinishTarget" xml:space="preserve">Target:</x:String>
<x:String x:Key="Text.GitFlow.FinishWithPush" xml:space="preserve">Invia al remote dopo aver finito</x:String>
<x:String x:Key="Text.GitFlow.FinishWithSquash" xml:space="preserve">Esegui squash durante il merge</x:String>
<x:String x:Key="Text.GitFlow.Hotfix" xml:space="preserve">Hotfix:</x:String>
<x:String x:Key="Text.GitFlow.HotfixPrefix" xml:space="preserve">Prefisso Hotfix:</x:String>

View File

@@ -425,7 +425,6 @@
<x:String x:Key="Text.GitFlow.FinishHotfix" xml:space="preserve">フロー - 緊急のバグ修正を完了</x:String>
<x:String x:Key="Text.GitFlow.FinishRelease" xml:space="preserve">フロー - リリース作業を完了</x:String>
<x:String x:Key="Text.GitFlow.FinishTarget" xml:space="preserve">対象:</x:String>
<x:String x:Key="Text.GitFlow.FinishWithPush" xml:space="preserve">コミットの完了後、リモートにプッシュ</x:String>
<x:String x:Key="Text.GitFlow.FinishWithSquash" xml:space="preserve">スカッシュしてマージ</x:String>
<x:String x:Key="Text.GitFlow.Hotfix" xml:space="preserve">緊急のバグ修正:</x:String>
<x:String x:Key="Text.GitFlow.HotfixPrefix" xml:space="preserve">緊急のバグ修正用のプレフィックス:</x:String>

View File

@@ -392,7 +392,6 @@
<x:String x:Key="Text.GitFlow.FinishHotfix" xml:space="preserve">FLOW - Hotfix 완료</x:String>
<x:String x:Key="Text.GitFlow.FinishRelease" xml:space="preserve">FLOW - Release 완료</x:String>
<x:String x:Key="Text.GitFlow.FinishTarget" xml:space="preserve">대상:</x:String>
<x:String x:Key="Text.GitFlow.FinishWithPush" xml:space="preserve">완료 후 원격(들)에 푸시</x:String>
<x:String x:Key="Text.GitFlow.FinishWithSquash" xml:space="preserve">병합 시 스쿼시</x:String>
<x:String x:Key="Text.GitFlow.Hotfix" xml:space="preserve">핫픽스:</x:String>
<x:String x:Key="Text.GitFlow.HotfixPrefix" xml:space="preserve">Hotfix 접두사:</x:String>

View File

@@ -437,7 +437,6 @@
<x:String x:Key="Text.GitFlow.FinishHotfix" xml:space="preserve">ПРОЦЕСС - Закончить исправление</x:String>
<x:String x:Key="Text.GitFlow.FinishRelease" xml:space="preserve">ПРОЦЕСС - Завершить выпуск</x:String>
<x:String x:Key="Text.GitFlow.FinishTarget" xml:space="preserve">Цель:</x:String>
<x:String x:Key="Text.GitFlow.FinishWithPush" xml:space="preserve">Выложить на удалённый(ые) после завершения</x:String>
<x:String x:Key="Text.GitFlow.FinishWithSquash" xml:space="preserve">Втиснуть при слиянии</x:String>
<x:String x:Key="Text.GitFlow.Hotfix" xml:space="preserve">Исправление:</x:String>
<x:String x:Key="Text.GitFlow.HotfixPrefix" xml:space="preserve">Префикс исправлений:</x:String>

View File

@@ -27,7 +27,8 @@
<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>
<x:String x:Key="Text.App.Hide" xml:space="preserve">隐藏 SourceGit</x:String>
<x:String x:Key="Text.App.ShowAll" xml:space="preserve">显示所有窗口</x:String>
<x:String x:Key="Text.App.HideOthers" xml:space="preserve">隐藏其他</x:String>
<x:String x:Key="Text.App.ShowAll" xml:space="preserve">显示全部</x:String>
<x:String x:Key="Text.Apply" xml:space="preserve">应用补丁(apply)</x:String>
<x:String x:Key="Text.Apply.3Way" xml:space="preserve">尝试三路合并</x:String>
<x:String x:Key="Text.Apply.File" xml:space="preserve">补丁文件 </x:String>
@@ -134,6 +135,8 @@
<x:String x:Key="Text.Clone" xml:space="preserve">克隆远程仓库</x:String>
<x:String x:Key="Text.Clone.AdditionalParam" xml:space="preserve">额外参数 </x:String>
<x:String x:Key="Text.Clone.AdditionalParam.Placeholder" xml:space="preserve">其他克隆参数,选填。</x:String>
<x:String x:Key="Text.Clone.Bookmark" xml:space="preserve">书签 </x:String>
<x:String x:Key="Text.Clone.Group" xml:space="preserve">自定义分组 </x:String>
<x:String x:Key="Text.Clone.LocalName" xml:space="preserve">本地仓库名 </x:String>
<x:String x:Key="Text.Clone.LocalName.Placeholder" xml:space="preserve">本地仓库目录的名字,选填。</x:String>
<x:String x:Key="Text.Clone.ParentFolder" xml:space="preserve">父级目录 </x:String>
@@ -279,8 +282,11 @@
<x:String x:Key="Text.ConfigureCustomActionControls.Label" xml:space="preserve">名称 </x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.Options" xml:space="preserve">选项列表 </x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.Options.Tip" xml:space="preserve">选项之间请使用英文 '|' 作为分隔符</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.StringFormatter" xml:space="preserve">输出内容格式化字串:</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.StringFormatter.Tip" xml:space="preserve">可选。用于格式化输出结果。当用户输入为空时忽略该操作。请使用`${VALUE}`代替用户输入。 </x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.StringValue.Tip" xml:space="preserve">内置变量 ${REPO}, ${REMOTE}, ${BRANCH}, ${BRANCH_FRIENDLY_NAME}, ${SHA}, ${FILE} 与 ${TAG} 在这里仍然可用</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.Type" xml:space="preserve">类型 </x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.UseFriendlyName" xml:space="preserve">输出结果带有远程名:</x:String>
<x:String x:Key="Text.ConfigureWorkspace" xml:space="preserve">工作区</x:String>
<x:String x:Key="Text.ConfigureWorkspace.Color" xml:space="preserve">颜色</x:String>
<x:String x:Key="Text.ConfigureWorkspace.Name" xml:space="preserve">名称</x:String>
@@ -374,6 +380,7 @@
<x:String x:Key="Text.Diff.Submodule" xml:space="preserve">子模块</x:String>
<x:String x:Key="Text.Diff.Submodule.Deleted" xml:space="preserve">删除</x:String>
<x:String x:Key="Text.Diff.Submodule.New" xml:space="preserve">新增</x:String>
<x:String x:Key="Text.Diff.Submodule.UncommittedChanges" xml:space="preserve">+ {0} 项未提交变更</x:String>
<x:String x:Key="Text.Diff.SwapCommits" xml:space="preserve">交换比对双方</x:String>
<x:String x:Key="Text.Diff.SyntaxHighlight" xml:space="preserve">语法高亮</x:String>
<x:String x:Key="Text.Diff.ToggleWordWrap" xml:space="preserve">自动换行</x:String>
@@ -437,7 +444,6 @@
<x:String x:Key="Text.GitFlow.FinishHotfix" xml:space="preserve">结束修复分支</x:String>
<x:String x:Key="Text.GitFlow.FinishRelease" xml:space="preserve">结束版本分支</x:String>
<x:String x:Key="Text.GitFlow.FinishTarget" xml:space="preserve">目标分支 </x:String>
<x:String x:Key="Text.GitFlow.FinishWithPush" xml:space="preserve">完成后自动推送</x:String>
<x:String x:Key="Text.GitFlow.FinishWithSquash" xml:space="preserve">压缩变更为单一提交后合并分支</x:String>
<x:String x:Key="Text.GitFlow.Hotfix" xml:space="preserve">修复分支 </x:String>
<x:String x:Key="Text.GitFlow.HotfixPrefix" xml:space="preserve">修复分支名前缀 </x:String>
@@ -503,6 +509,7 @@
<x:String x:Key="Text.Hotkeys.Global.GotoNextTab" xml:space="preserve">切换到下一个页面</x:String>
<x:String x:Key="Text.Hotkeys.Global.GotoPrevTab" xml:space="preserve">切换到上一个页面</x:String>
<x:String x:Key="Text.Hotkeys.Global.NewTab" xml:space="preserve">新建页面</x:String>
<x:String x:Key="Text.Hotkeys.Global.OpenLocalRepository" xml:space="preserve">打开本地仓库</x:String>
<x:String x:Key="Text.Hotkeys.Global.OpenPreferences" xml:space="preserve">打开偏好设置面板</x:String>
<x:String x:Key="Text.Hotkeys.Global.ShowWorkspaceDropdownMenu" xml:space="preserve">显示工作区下拉菜单</x:String>
<x:String x:Key="Text.Hotkeys.Global.SwitchTab" xml:space="preserve">切换显示页面</x:String>
@@ -596,6 +603,10 @@
<x:String x:Key="Text.OpenAppDataDir" xml:space="preserve">浏览应用数据目录</x:String>
<x:String x:Key="Text.OpenFile" xml:space="preserve">打开文件</x:String>
<x:String x:Key="Text.OpenInExternalMergeTool" xml:space="preserve">使用外部对比工具查看</x:String>
<x:String x:Key="Text.OpenLocalRepository" xml:space="preserve">打开本地仓库</x:String>
<x:String x:Key="Text.OpenLocalRepository.Bookmark" xml:space="preserve">书签 </x:String>
<x:String x:Key="Text.OpenLocalRepository.Group" xml:space="preserve">分组 </x:String>
<x:String x:Key="Text.OpenLocalRepository.Path" 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.Close" xml:space="preserve">关闭标签页</x:String>
@@ -902,6 +913,8 @@
<x:String x:Key="Text.Submodule.Status.Unmerged" xml:space="preserve">未解决冲突</x:String>
<x:String x:Key="Text.Submodule.Update" xml:space="preserve">更新</x:String>
<x:String x:Key="Text.Submodule.URL" xml:space="preserve">仓库</x:String>
<x:String x:Key="Text.SubmoduleRevisionCompare" xml:space="preserve">子模块变更详情</x:String>
<x:String x:Key="Text.SubmoduleRevisionCompare.OpenDetails" xml:space="preserve">查看变更详情</x:String>
<x:String x:Key="Text.Sure" xml:space="preserve">确 定</x:String>
<x:String x:Key="Text.Tag.Tagger" xml:space="preserve">创建者</x:String>
<x:String x:Key="Text.Tag.Time" xml:space="preserve">创建时间</x:String>

View File

@@ -27,7 +27,8 @@
<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>
<x:String x:Key="Text.App.Hide" xml:space="preserve">隱藏 SourceGit</x:String>
<x:String x:Key="Text.App.ShowAll" xml:space="preserve">顯示所有</x:String>
<x:String x:Key="Text.App.HideOthers" xml:space="preserve">隱藏其他</x:String>
<x:String x:Key="Text.App.ShowAll" xml:space="preserve">顯示全部</x:String>
<x:String x:Key="Text.Apply" xml:space="preserve">套用修補檔 (apply patch)</x:String>
<x:String x:Key="Text.Apply.3Way" xml:space="preserve">嘗試三向合併</x:String>
<x:String x:Key="Text.Apply.File" xml:space="preserve">修補檔:</x:String>
@@ -134,6 +135,8 @@
<x:String x:Key="Text.Clone" xml:space="preserve">複製 (clone) 遠端存放庫</x:String>
<x:String x:Key="Text.Clone.AdditionalParam" xml:space="preserve">額外參數:</x:String>
<x:String x:Key="Text.Clone.AdditionalParam.Placeholder" xml:space="preserve">其他複製參數,選填。</x:String>
<x:String x:Key="Text.Clone.Bookmark" xml:space="preserve">書籤:</x:String>
<x:String x:Key="Text.Clone.Group" xml:space="preserve">群組:</x:String>
<x:String x:Key="Text.Clone.LocalName" xml:space="preserve">本機存放庫名稱:</x:String>
<x:String x:Key="Text.Clone.LocalName.Placeholder" xml:space="preserve">本機存放庫目錄的名稱,選填。</x:String>
<x:String x:Key="Text.Clone.ParentFolder" xml:space="preserve">上層目錄:</x:String>
@@ -279,8 +282,11 @@
<x:String x:Key="Text.ConfigureCustomActionControls.Label" xml:space="preserve">名稱:</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.Options" xml:space="preserve">選項列表:</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.Options.Tip" xml:space="preserve">請使用英文「|」符號分隔選項</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.StringFormatter" xml:space="preserve">字串格式化器:</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.StringFormatter.Tip" xml:space="preserve">可選。用於格式化輸出字串。當輸入為空時將被忽略。請使用 `${VALUE}` 來表示輸入字串。</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.StringValue.Tip" xml:space="preserve">內建變數 ${REPO}、${REMOTE}、${BRANCH}、${BRANCH_FRIENDLY_NAME}、${SHA}、${FILE} 及 ${TAG} 在此處仍可使用</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.Type" xml:space="preserve">類型:</x:String>
<x:String x:Key="Text.ConfigureCustomActionControls.UseFriendlyName" xml:space="preserve">輸出包含遠端的名稱:</x:String>
<x:String x:Key="Text.ConfigureWorkspace" xml:space="preserve">工作區</x:String>
<x:String x:Key="Text.ConfigureWorkspace.Color" xml:space="preserve">顏色</x:String>
<x:String x:Key="Text.ConfigureWorkspace.Name" xml:space="preserve">名稱</x:String>
@@ -374,6 +380,7 @@
<x:String x:Key="Text.Diff.Submodule" xml:space="preserve">子模組</x:String>
<x:String x:Key="Text.Diff.Submodule.Deleted" xml:space="preserve">已刪除</x:String>
<x:String x:Key="Text.Diff.Submodule.New" xml:space="preserve">新增</x:String>
<x:String x:Key="Text.Diff.Submodule.UncommittedChanges" xml:space="preserve">+ {0} 項未提交變更</x:String>
<x:String x:Key="Text.Diff.SwapCommits" xml:space="preserve">交換比對雙方</x:String>
<x:String x:Key="Text.Diff.SyntaxHighlight" xml:space="preserve">語法上色</x:String>
<x:String x:Key="Text.Diff.ToggleWordWrap" xml:space="preserve">自動換行</x:String>
@@ -437,7 +444,6 @@
<x:String x:Key="Text.GitFlow.FinishHotfix" xml:space="preserve">完成修復分支</x:String>
<x:String x:Key="Text.GitFlow.FinishRelease" xml:space="preserve">完成發行分支</x:String>
<x:String x:Key="Text.GitFlow.FinishTarget" xml:space="preserve">目標分支:</x:String>
<x:String x:Key="Text.GitFlow.FinishWithPush" xml:space="preserve">完成後自動推送</x:String>
<x:String x:Key="Text.GitFlow.FinishWithSquash" xml:space="preserve">壓縮為單一提交後合併</x:String>
<x:String x:Key="Text.GitFlow.Hotfix" xml:space="preserve">修復分支:</x:String>
<x:String x:Key="Text.GitFlow.HotfixPrefix" xml:space="preserve">修復分支前置詞:</x:String>
@@ -503,6 +509,7 @@
<x:String x:Key="Text.Hotkeys.Global.GotoNextTab" xml:space="preserve">切換到下一個頁面</x:String>
<x:String x:Key="Text.Hotkeys.Global.GotoPrevTab" xml:space="preserve">切換到上一個頁面</x:String>
<x:String x:Key="Text.Hotkeys.Global.NewTab" xml:space="preserve">新增頁面</x:String>
<x:String x:Key="Text.Hotkeys.Global.OpenLocalRepository" xml:space="preserve">開啟本機存放庫</x:String>
<x:String x:Key="Text.Hotkeys.Global.OpenPreferences" xml:space="preserve">開啟偏好設定面板</x:String>
<x:String x:Key="Text.Hotkeys.Global.ShowWorkspaceDropdownMenu" xml:space="preserve">顯示工作區的下拉式選單</x:String>
<x:String x:Key="Text.Hotkeys.Global.SwitchTab" xml:space="preserve">切換目前頁面</x:String>
@@ -596,6 +603,10 @@
<x:String x:Key="Text.OpenAppDataDir" xml:space="preserve">瀏覽程式資料目錄</x:String>
<x:String x:Key="Text.OpenFile" xml:space="preserve">開啟檔案</x:String>
<x:String x:Key="Text.OpenInExternalMergeTool" xml:space="preserve">使用外部比對工具檢視</x:String>
<x:String x:Key="Text.OpenLocalRepository" xml:space="preserve">開啟本機存放庫</x:String>
<x:String x:Key="Text.OpenLocalRepository.Bookmark" xml:space="preserve">書籤:</x:String>
<x:String x:Key="Text.OpenLocalRepository.Group" xml:space="preserve">群組:</x:String>
<x:String x:Key="Text.OpenLocalRepository.Path" 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.Close" xml:space="preserve">關閉分頁</x:String>
@@ -902,6 +913,8 @@
<x:String x:Key="Text.Submodule.Status.Unmerged" xml:space="preserve">未解決的衝突</x:String>
<x:String x:Key="Text.Submodule.Update" xml:space="preserve">更新</x:String>
<x:String x:Key="Text.Submodule.URL" xml:space="preserve">存放庫</x:String>
<x:String x:Key="Text.SubmoduleRevisionCompare" xml:space="preserve">子模組變更詳細資訊</x:String>
<x:String x:Key="Text.SubmoduleRevisionCompare.OpenDetails" xml:space="preserve">開啟變更詳細資訊</x:String>
<x:String x:Key="Text.Sure" xml:space="preserve">確 定</x:String>
<x:String x:Key="Text.Tag.Tagger" xml:space="preserve">建立者</x:String>
<x:String x:Key="Text.Tag.Time" xml:space="preserve">建立時間</x:String>

View File

@@ -65,14 +65,12 @@ namespace SourceGit.ViewModels
Filter = string.Empty;
}
public void Launch()
public Blame Launch()
{
_repoFiles.Clear();
_visibleFiles.Clear();
Close();
if (!string.IsNullOrEmpty(_selectedFile))
App.ShowWindow(new Blame(_repo, _selectedFile, _head));
return !string.IsNullOrEmpty(_selectedFile) ? new Blame(_repo, _selectedFile, _head) : null;
}
private void UpdateVisible()

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Threading.Tasks;
@@ -45,6 +46,28 @@ namespace SourceGit.ViewModels
set => SetProperty(ref _local, value);
}
public List<RepositoryNode> Groups
{
get;
}
public RepositoryNode SelectedGroup
{
get => _selectedGroup;
set => SetProperty(ref _selectedGroup, value);
}
public List<int> Bookmarks
{
get;
}
public int Bookmark
{
get => _bookmark;
set => SetProperty(ref _bookmark, value);
}
public string ExtraArgs
{
get => _extraArgs;
@@ -61,6 +84,15 @@ namespace SourceGit.ViewModels
{
_pageId = pageId;
Groups = new List<RepositoryNode>();
Groups.Add(new RepositoryNode { Name = "No Group (Uncategorized)", Id = string.Empty });
SelectedGroup = Groups[0];
CollectGroups(Groups, Preferences.Instance.RepositoryNodes);
Bookmarks = new List<int>();
for (var i = 0; i < Models.Bookmarks.Brushes.Length; i++)
Bookmarks.Add(i);
var activeWorkspace = Preferences.Instance.GetActiveWorkspace();
_parentFolder = activeWorkspace?.DefaultCloneDir;
if (string.IsNullOrEmpty(ParentFolder))
@@ -134,7 +166,9 @@ namespace SourceGit.ViewModels
log.Complete();
var node = Preferences.Instance.FindOrAddNodeByRepositoryPath(path, null, true);
var parent = _selectedGroup is { Id: not "" } ? _selectedGroup : null;
var node = Preferences.Instance.FindOrAddNodeByRepositoryPath(path, parent, true);
node.Bookmark = _bookmark;
await node.UpdateStatusAsync(false, null);
var launcher = App.GetLauncher();
@@ -153,6 +187,18 @@ namespace SourceGit.ViewModels
return true;
}
private void CollectGroups(List<RepositoryNode> outs, List<RepositoryNode> collections)
{
foreach (var node in collections)
{
if (!node.IsRepository)
{
outs.Add(node);
CollectGroups(outs, node.SubNodes);
}
}
}
private string _pageId = string.Empty;
private string _remote = string.Empty;
private bool _useSSH = false;
@@ -160,5 +206,7 @@ namespace SourceGit.ViewModels
private string _parentFolder = string.Empty;
private string _local = string.Empty;
private string _extraArgs = string.Empty;
private RepositoryNode _selectedGroup = null;
private int _bookmark = 0;
}
}

View File

@@ -44,13 +44,11 @@ namespace SourceGit.ViewModels
Filter = string.Empty;
}
public void Launch()
public Compare Launch()
{
_refs.Clear();
Close();
if (_compareTo != null)
App.ShowWindow(new Compare(_repo, _basedOn, _compareTo));
return _compareTo != null ? new Compare(_repo, _basedOn, _compareTo) : null;
}
private void UpdateRefs()

View File

@@ -73,10 +73,9 @@ namespace SourceGit.ViewModels
await _wc.UseMineAsync([_change]);
}
public async Task MergeAsync()
public MergeConflictEditor CreateOpenMergeEditorRequest()
{
if (CanMerge)
await App.ShowDialog(new MergeConflictEditor(_repo, _head, _change.Path));
return CanMerge ? new MergeConflictEditor(_repo, _head, _change.Path) : null;
}
public async Task MergeExternalAsync()

View File

@@ -190,7 +190,10 @@ namespace SourceGit.ViewModels
}
if (isSubmodule)
{
submoduleDiff.FullPath = submoduleRoot;
rs = submoduleDiff;
}
}
if (!isSubmodule)
@@ -288,6 +291,16 @@ namespace SourceGit.ViewModels
private async Task<Models.RevisionSubmodule> QuerySubmoduleRevisionAsync(string repo, string sha)
{
if (!File.Exists(Path.Combine(repo, ".git")))
return new Models.RevisionSubmodule() { Commit = new Models.Commit() { SHA = sha } };
var uncommittedChangesCount = 0;
if (sha.EndsWith("-dirty", StringComparison.Ordinal))
{
sha = sha.Substring(0, sha.Length - 6);
uncommittedChangesCount = await new Commands.CountLocalChanges(repo, true).GetResultAsync().ConfigureAwait(false);
}
var commit = await new Commands.QuerySingleCommit(repo, sha).GetResultAsync().ConfigureAwait(false);
if (commit == null)
return new Models.RevisionSubmodule() { Commit = new Models.Commit() { SHA = sha } };
@@ -296,7 +309,8 @@ namespace SourceGit.ViewModels
return new Models.RevisionSubmodule()
{
Commit = commit,
FullMessage = new Models.CommitFullMessage { Message = body }
FullMessage = new Models.CommitFullMessage { Message = body },
UncommittedChanges = uncommittedChangesCount
};
}

View File

@@ -18,15 +18,22 @@ namespace SourceGit.ViewModels
public string Label { get; set; }
public string Placeholder { get; set; }
public string Text { get; set; }
public string Formatter { get; set; }
public CustomActionControlTextBox(string label, string placeholder, string defaultValue)
public CustomActionControlTextBox(string label, string placeholder, string defaultValue, string formatter)
{
Label = label + ":";
Placeholder = placeholder;
Text = defaultValue;
Formatter = formatter;
}
public string GetValue() => Text;
public string GetValue()
{
if (string.IsNullOrEmpty(Text))
return string.Empty;
return string.IsNullOrEmpty(Formatter) ? Text : Formatter.Replace("${VALUE}", Text);
}
}
public class CustomActionControlPathSelector : ObservableObject, ICustomActionControlParameter
@@ -77,12 +84,7 @@ namespace SourceGit.ViewModels
public string Label { get; set; }
public string Description { get; set; }
public List<string> Options { get; set; } = [];
public string Value
{
get => _value;
set => SetProperty(ref _value, value);
}
public string Value { get; set; }
public CustomActionControlComboBox(string label, string description, string options)
{
@@ -93,13 +95,45 @@ namespace SourceGit.ViewModels
if (parts.Length > 0)
{
Options.AddRange(parts);
_value = parts[0];
Value = parts[0];
}
}
public string GetValue() => _value;
public string GetValue() => Value;
}
private string _value = string.Empty;
public class CustomActionControlBranchSelector : ObservableObject, ICustomActionControlParameter
{
public string Label { get; set; }
public string Description { get; set; }
public List<Models.Branch> Branches { get; set; } = [];
public Models.Branch SelectedBranch { get; set; }
public CustomActionControlBranchSelector(string label, string description, Repository repo, bool isLocal, bool useFriendlyName)
{
Label = label;
Description = description;
_useFriendlyName = useFriendlyName;
foreach (var b in repo.Branches)
{
if (b.IsLocal == isLocal && !b.IsDetachedHead)
Branches.Add(b);
}
if (Branches.Count > 0)
SelectedBranch = Branches[0];
}
public string GetValue()
{
if (SelectedBranch == null)
return string.Empty;
return _useFriendlyName ? SelectedBranch.FriendlyName : SelectedBranch.Name;
}
private bool _useFriendlyName = false;
}
public class ExecuteCustomAction : Popup
@@ -124,7 +158,31 @@ namespace SourceGit.ViewModels
_repo = repo;
CustomAction = action;
Target = scopeTarget ?? new Models.Null();
PrepareControlParameters();
foreach (var ctl in CustomAction.Controls)
{
switch (ctl.Type)
{
case Models.CustomActionControlType.TextBox:
ControlParameters.Add(new CustomActionControlTextBox(ctl.Label, ctl.Description, PrepareStringByTarget(ctl.StringValue), ctl.StringFormatter));
break;
case Models.CustomActionControlType.PathSelector:
ControlParameters.Add(new CustomActionControlPathSelector(ctl.Label, ctl.Description, ctl.BoolValue, PrepareStringByTarget(ctl.StringValue)));
break;
case Models.CustomActionControlType.CheckBox:
ControlParameters.Add(new CustomActionControlCheckBox(ctl.Label, ctl.Description, ctl.StringValue, ctl.BoolValue));
break;
case Models.CustomActionControlType.ComboBox:
ControlParameters.Add(new CustomActionControlComboBox(ctl.Label, ctl.Description, PrepareStringByTarget(ctl.StringValue)));
break;
case Models.CustomActionControlType.LocalBranchSelector:
ControlParameters.Add(new CustomActionControlBranchSelector(ctl.Label, ctl.Description, _repo, true, false));
break;
case Models.CustomActionControlType.RemoteBranchSelector:
ControlParameters.Add(new CustomActionControlBranchSelector(ctl.Label, ctl.Description, _repo, false, ctl.BoolValue));
break;
}
}
}
public override async Task<bool> Sure()
@@ -153,31 +211,10 @@ namespace SourceGit.ViewModels
return true;
}
private void PrepareControlParameters()
{
foreach (var ctl in CustomAction.Controls)
{
switch (ctl.Type)
{
case Models.CustomActionControlType.TextBox:
ControlParameters.Add(new CustomActionControlTextBox(ctl.Label, ctl.Description, PrepareStringByTarget(ctl.StringValue)));
break;
case Models.CustomActionControlType.PathSelector:
ControlParameters.Add(new CustomActionControlPathSelector(ctl.Label, ctl.Description, ctl.BoolValue, PrepareStringByTarget(ctl.StringValue)));
break;
case Models.CustomActionControlType.CheckBox:
ControlParameters.Add(new CustomActionControlCheckBox(ctl.Label, ctl.Description, ctl.StringValue, ctl.BoolValue));
break;
case Models.CustomActionControlType.ComboBox:
ControlParameters.Add(new CustomActionControlComboBox(ctl.Label, ctl.Description, PrepareStringByTarget(ctl.StringValue)));
break;
}
}
}
private string PrepareStringByTarget(string org)
{
org = org.Replace("${REPO}", GetWorkdir());
var repoPath = OperatingSystem.IsWindows() ? _repo.FullPath.Replace("/", "\\") : _repo.FullPath;
org = org.Replace("${REPO}", repoPath);
return Target switch
{
@@ -186,15 +223,10 @@ namespace SourceGit.ViewModels
Models.Tag t => org.Replace("${TAG}", t.Name),
Models.Remote r => org.Replace("${REMOTE}", r.Name),
Models.CustomActionTargetFile f => org.Replace("${FILE}", f.File).Replace("${SHA}", f.Revision?.SHA ?? string.Empty),
_ => org
_ => org.Replace("${BRANCH}", _repo.CurrentBranch?.Name ?? "HEAD")
};
}
private string GetWorkdir()
{
return OperatingSystem.IsWindows() ? _repo.FullPath.Replace("/", "\\") : _repo.FullPath;
}
private void Run(string args)
{
var start = new ProcessStartInfo();

View File

@@ -60,14 +60,12 @@ namespace SourceGit.ViewModels
Filter = string.Empty;
}
public void Launch()
public FileHistories Launch()
{
_repoFiles.Clear();
_visibleFiles.Clear();
Close();
if (!string.IsNullOrEmpty(_selectedFile))
App.ShowWindow(new FileHistories(_repo, _selectedFile));
return !string.IsNullOrEmpty(_selectedFile) ? new FileHistories(_repo, _selectedFile) : null;
}
private void UpdateVisible()

View File

@@ -21,12 +21,6 @@ namespace SourceGit.ViewModels
set;
} = false;
public bool AutoPush
{
get;
set;
} = false;
public bool KeepBranch
{
get;
@@ -50,7 +44,7 @@ namespace SourceGit.ViewModels
var prefix = _repo.GitFlow.GetPrefix(Type);
var name = Branch.Name.StartsWith(prefix) ? Branch.Name.Substring(prefix.Length) : Branch.Name;
var succ = await Commands.GitFlow.FinishAsync(_repo.FullPath, Type, name, Squash, AutoPush, KeepBranch, log);
var succ = await Commands.GitFlow.FinishAsync(_repo.FullPath, Type, name, Squash, KeepBranch, log);
log.Complete();
return succ;

View File

@@ -418,22 +418,6 @@ namespace SourceGit.ViewModels
_repo.ShowPopup(new DropHead(_repo, head, parent));
}
public async Task InteractiveRebaseAsync(Models.Commit commit, Models.InteractiveRebaseAction act)
{
var prefill = new InteractiveRebasePrefill(commit.SHA, act);
var start = act switch
{
Models.InteractiveRebaseAction.Squash or Models.InteractiveRebaseAction.Fixup => $"{commit.SHA}~~",
_ => $"{commit.SHA}~",
};
var on = await new Commands.QuerySingleCommit(_repo.FullPath, start).GetResultAsync();
if (on == null)
_repo.SendNotification($"Can not squash current commit into parent!", true);
else
await App.ShowDialog(new InteractiveRebase(_repo, on, prefill));
}
public async Task<string> GetCommitFullMessageAsync(Models.Commit commit)
{
return await new Commands.QueryCommitFullMessage(_repo.FullPath, commit.SHA)

View File

@@ -13,6 +13,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
namespace SourceGit.ViewModels
{
public record InteractiveRebasePrefill(string SHA, Models.InteractiveRebaseAction Action);
public record InteractiveRebaseReorderItem(string Key, InteractiveRebaseItem Item);
public class InteractiveRebaseItem : ObservableObject
{
@@ -172,10 +173,57 @@ namespace SourceGit.ViewModels
.ConfigureAwait(false);
var list = new List<InteractiveRebaseItem>();
var needReorder = new List<InteractiveRebaseReorderItem>();
for (var i = 0; i < commits.Count; i++)
{
var c = commits[i];
list.Add(new InteractiveRebaseItem(commits.Count - i, c.Commit, c.Message));
var item = new InteractiveRebaseItem(commits.Count - i, c.Commit, c.Message);
var subject = c.Commit.Subject;
if (item.OriginalOrder > 1)
{
if (subject.StartsWith("fixup! ", StringComparison.Ordinal))
{
item.Action = Models.InteractiveRebaseAction.Fixup;
needReorder.Add(new(subject.Substring(7), item));
continue;
}
if (subject.StartsWith("squash! ", StringComparison.Ordinal))
{
item.Action = Models.InteractiveRebaseAction.Squash;
needReorder.Add(new(subject.Substring(8), item));
continue;
}
}
var reordered = new List<InteractiveRebaseReorderItem>();
foreach (var o in needReorder)
{
if (subject.StartsWith(o.Key, StringComparison.Ordinal))
{
list.Add(o.Item);
reordered.Add(o);
}
}
foreach (var k in reordered)
needReorder.Remove(k);
list.Add(item);
}
foreach (var v in needReorder)
{
for (var i = 0; i < list.Count; i++)
{
if (v.Item.OriginalOrder > list[i].OriginalOrder)
{
v.Item.Action = Models.InteractiveRebaseAction.Pick; // For safety, reset to pick if the target commit is not found
list.Insert(i, v.Item);
break;
}
}
}
var selected = list.Count > 0 ? list[0] : null;
@@ -363,7 +411,18 @@ namespace SourceGit.ViewModels
item.IsMessageUserEdited = false;
if (item.Action == Models.InteractiveRebaseAction.Squash)
pendingMessages.Add(item.OriginalFullMessage);
{
if (item.OriginalFullMessage.StartsWith("squash! ", StringComparison.Ordinal))
{
var firstLineEnd = item.OriginalFullMessage.IndexOf('\n');
if (firstLineEnd > 0)
pendingMessages.Add(item.OriginalFullMessage.Substring(firstLineEnd + 1));
}
else
{
pendingMessages.Add(item.OriginalFullMessage);
}
}
hasPending = true;
continue;

View File

@@ -117,7 +117,7 @@ namespace SourceGit.ViewModels
return false;
}
public void Quit()
public void CloseAll()
{
_ignoreIndexChange = true;

View File

@@ -0,0 +1,115 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Threading.Tasks;
namespace SourceGit.ViewModels
{
public class OpenLocalRepository : Popup
{
[Required(ErrorMessage = "Repository folder is required")]
[CustomValidation(typeof(OpenLocalRepository), nameof(ValidateRepoPath))]
public string RepoPath
{
get => _repoPath;
set => SetProperty(ref _repoPath, value, true);
}
public List<RepositoryNode> Groups
{
get;
}
public RepositoryNode Group
{
get => _group;
set => SetProperty(ref _group, value);
}
public List<int> Bookmarks
{
get;
}
public int Bookmark
{
get => _bookmark;
set => SetProperty(ref _bookmark, value);
}
public OpenLocalRepository(string pageId, RepositoryNode group)
{
_pageId = pageId;
Groups = new List<RepositoryNode>();
Groups.Add(new RepositoryNode { Name = "No Group (Uncategorized)", Id = string.Empty });
Group = group ?? Groups[0];
CollectGroups(Groups, Preferences.Instance.RepositoryNodes);
Bookmarks = new List<int>();
for (var i = 0; i < Models.Bookmarks.Brushes.Length; i++)
Bookmarks.Add(i);
}
public static ValidationResult ValidateRepoPath(string folder, ValidationContext _)
{
if (!Directory.Exists(folder))
return new ValidationResult("Given path can NOT be found");
return ValidationResult.Success;
}
public override async Task<bool> Sure()
{
var isBare = await new Commands.IsBareRepository(_repoPath).GetResultAsync();
var parent = _group is { Id: not "" } ? _group : null;
var repoRoot = _repoPath;
if (!isBare)
{
var test = await new Commands.QueryRepositoryRootPath(_repoPath).GetResultAsync();
if (test.IsSuccess && !string.IsNullOrWhiteSpace(test.StdOut))
{
repoRoot = test.StdOut.Trim();
}
else
{
var launcher = App.GetLauncher();
foreach (var page in launcher.Pages)
{
if (page.Node.Id.Equals(_pageId, StringComparison.Ordinal))
{
page.Popup = new Init(page.Node.Id, _repoPath, parent, test.StdErr);
break;
}
}
return false;
}
}
var node = Preferences.Instance.FindOrAddNodeByRepositoryPath(repoRoot, parent, true);
node.Bookmark = _bookmark;
await node.UpdateStatusAsync(false, null);
Welcome.Instance.Refresh();
node.Open();
return true;
}
private void CollectGroups(List<RepositoryNode> outs, List<RepositoryNode> collections)
{
foreach (var node in collections)
{
if (!node.IsRepository)
{
outs.Add(node);
CollectGroups(outs, node.SubNodes);
}
}
}
private string _pageId = string.Empty;
private string _repoPath = string.Empty;
private RepositoryNode _group = null;
private int _bookmark = 0;
}
}

View File

@@ -629,19 +629,22 @@ namespace SourceGit.ViewModels
RemoveInvalidRepositoriesRecursive(RepositoryNodes);
}
public async Task UpdateAvailableAIModelsAsync()
public void UpdateAvailableAIModels()
{
foreach (var service in OpenAIServices)
Task.Run(() =>
{
try
foreach (var service in OpenAIServices)
{
await service.FetchAvailableModelsAsync();
try
{
service.FetchAvailableModels();
}
catch
{
// Ignore errors.
}
}
catch
{
// Ignore errors.
}
}
});
}
public void Save()

View File

@@ -58,7 +58,7 @@ namespace SourceGit.ViewModels
set
{
if (SetProperty(ref _selectedRemoteBranch, value, true))
IsSetTrackOptionVisible = value != null && _selectedLocalBranch.Upstream != value.FullName;
IsSetTrackOptionVisible = value != null && (value.Head == null || _selectedLocalBranch.Upstream != value.FullName);
}
}

View File

@@ -1887,7 +1887,7 @@ namespace SourceGit.ViewModels
try
{
if (Preferences.Instance.EnableAutoFetch || !CanCreatePopup())
if (!Preferences.Instance.EnableAutoFetch || !CanCreatePopup())
{
_lastFetchTime = DateTime.Now;
return;

View File

@@ -73,7 +73,6 @@ namespace SourceGit.ViewModels
_cmds.Add(new("Push", "push", "Push", async () => await repo.PushAsync(false)));
_cmds.Add(new("Stash.Title", "stash", "Stashes.Add", async () => await repo.StashAllAsync(false)));
_cmds.Add(new("Apply.Title", "apply", "Diff", () => repo.ApplyPatch()));
_cmds.Add(new("Configure", "configure", "Settings", async () => await App.ShowDialog(new RepositoryConfigure(repo))));
_cmds.Sort((l, r) => l.Label.CompareTo(r.Label));
_visibleCmds = _cmds;

View File

@@ -0,0 +1,177 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
namespace SourceGit.ViewModels
{
public class SubmoduleRevisionCompare : ObservableObject
{
public bool IsLoading
{
get => _isLoading;
private set => SetProperty(ref _isLoading, value);
}
public Models.Commit Base
{
get => _base;
private set => SetProperty(ref _base, value);
}
public Models.Commit To
{
get => _to;
private set => SetProperty(ref _to, value);
}
public int TotalChanges
{
get => _totalChanges;
private set => SetProperty(ref _totalChanges, value);
}
public List<Models.Change> VisibleChanges
{
get => _visibleChanges;
private set => SetProperty(ref _visibleChanges, value);
}
public List<Models.Change> SelectedChanges
{
get => _selectedChanges;
set
{
if (SetProperty(ref _selectedChanges, value))
{
if (value is { Count: 1 })
DiffContext = new DiffContext(_repo, new Models.DiffOption(_base.SHA, _to.SHA, value[0]), _diffContext);
else
DiffContext = null;
}
}
}
public string SearchFilter
{
get => _searchFilter;
set
{
if (SetProperty(ref _searchFilter, value))
RefreshVisible();
}
}
public DiffContext DiffContext
{
get => _diffContext;
private set => SetProperty(ref _diffContext, value);
}
public SubmoduleRevisionCompare(Models.SubmoduleDiff diff)
{
_repo = diff.FullPath;
_base = diff.Old.Commit;
_to = diff.New.Commit;
Refresh();
}
public void Swap()
{
(Base, To) = (To, Base);
Refresh();
}
public void ClearSearchFilter()
{
SearchFilter = string.Empty;
}
public string GetAbsPath(string path)
{
return Native.OS.GetAbsPath(_repo, path);
}
public void OpenInExternalDiffTool(Models.Change change)
{
new Commands.DiffTool(_repo, new Models.DiffOption(_base.SHA, _to.SHA, change)).Open();
}
public async Task<bool> SaveChangesAsPatchAsync(List<Models.Change> changes, string saveTo)
{
return await Commands.SaveChangesAsPatch.ProcessRevisionCompareChangesAsync(_repo, changes, _base.SHA, _to.SHA, saveTo);
}
private void Refresh()
{
IsLoading = true;
VisibleChanges = [];
SelectedChanges = [];
Task.Run(async () =>
{
_changes = await new Commands.CompareRevisions(_repo, _base.SHA, _to.SHA)
.ReadAsync()
.ConfigureAwait(false);
var visible = _changes;
if (!string.IsNullOrWhiteSpace(_searchFilter))
{
visible = new List<Models.Change>();
foreach (var c in _changes)
{
if (c.Path.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase))
visible.Add(c);
}
}
Dispatcher.UIThread.Post(() =>
{
TotalChanges = _changes.Count;
VisibleChanges = visible;
IsLoading = false;
if (VisibleChanges.Count > 0)
SelectedChanges = [VisibleChanges[0]];
else
SelectedChanges = [];
});
});
}
private void RefreshVisible()
{
if (_changes == null)
return;
if (string.IsNullOrEmpty(_searchFilter))
{
VisibleChanges = _changes;
}
else
{
var visible = new List<Models.Change>();
foreach (var c in _changes)
{
if (c.Path.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase))
visible.Add(c);
}
VisibleChanges = visible;
}
}
private string _repo;
private bool _isLoading = true;
private Models.Commit _base = null;
private Models.Commit _to = null;
private int _totalChanges = 0;
private List<Models.Change> _changes = null;
private List<Models.Change> _visibleChanges = null;
private List<Models.Change> _selectedChanges = null;
private string _searchFilter = string.Empty;
private DiffContext _diffContext = null;
}
}

View File

@@ -128,19 +128,6 @@ namespace SourceGit.ViewModels
return rs.StdOut.Trim();
}
public void InitRepository(string path, RepositoryNode parent, string reason)
{
if (!Preferences.Instance.IsGitConfigured())
{
Models.Notification.Send(null, App.Text("NotConfigured"), true);
return;
}
var activePage = App.GetLauncher().ActivePage;
if (activePage != null && activePage.CanCreatePopup())
activePage.Popup = new Init(activePage.Node.Id, path, parent, reason);
}
public async Task AddRepositoryAsync(string path, RepositoryNode parent, bool moveNode, bool open)
{
var node = Preferences.Instance.FindOrAddNodeByRepositoryPath(path, parent, moveNode);
@@ -163,6 +150,19 @@ namespace SourceGit.ViewModels
activePage.Popup = new Clone(activePage.Node.Id);
}
public void OpenLocalRepository()
{
if (!Preferences.Instance.IsGitConfigured())
{
Models.Notification.Send(null, App.Text("NotConfigured"), true);
return;
}
var activePage = App.GetLauncher().ActivePage;
if (activePage != null && activePage.CanCreatePopup())
activePage.Popup = new OpenLocalRepository(activePage.Node.Id, null);
}
public void OpenTerminal()
{
if (!Preferences.Instance.IsGitConfigured())

View File

@@ -4,16 +4,16 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:v="using:SourceGit.Views"
xmlns:vm="using:SourceGit.ViewModels"
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="120"
mc:Ignorable="d" d:DesignWidth="520" d:DesignHeight="400"
x:Class="SourceGit.Views.AIAssistant"
x:DataType="vm:AIAssistant"
x:Name="ThisControl"
Icon="/App.ico"
Title="{DynamicResource Text.AIAssistant}"
Width="520" SizeToContent="Height"
CanResize="False"
Width="520" Height="400"
CanResize="True"
WindowStartupLocation="CenterOwner">
<Grid RowDefinitions="Auto,Auto,Auto">
<Grid RowDefinitions="Auto,*,Auto">
<!-- TitleBar -->
<Grid Grid.Row="0" Height="28" IsVisible="{Binding !#ThisControl.UseSystemWindowFrame}">
<Border Background="{DynamicResource Brush.TitleBar}"
@@ -39,7 +39,6 @@
<!-- AI response -->
<v:AIResponseView Grid.Row="1"
Margin="8"
Height="320"
BorderThickness="1"
BorderBrush="{DynamicResource Brush.Border2}"
Background="{DynamicResource Brush.Contents}"

View File

@@ -19,7 +19,7 @@ namespace SourceGit.Views
if (e.Key == Key.Enter)
{
vm.Launch();
this.ShowWindow(vm.Launch());
e.Handled = true;
}
else if (e.Key == Key.Up)
@@ -55,7 +55,7 @@ namespace SourceGit.Views
{
if (DataContext is ViewModels.BlameCommandPalette vm)
{
vm.Launch();
this.ShowWindow(vm.Launch());
e.Handled = true;
}
}

View File

@@ -537,7 +537,7 @@ namespace SourceGit.Views
compare.Icon = this.CreateMenuIcon("Icons.Compare");
compare.Click += (_, ev) =>
{
App.ShowWindow(new ViewModels.Compare(repo, branches[0], branches[1]));
this.ShowWindow(new ViewModels.Compare(repo, branches[0], branches[1]));
ev.Handled = true;
};
menu.Items.Add(compare);
@@ -819,7 +819,7 @@ namespace SourceGit.Views
interactiveRebase.Click += async (_, e) =>
{
var commit = await new Commands.QuerySingleCommit(repo.FullPath, branch.Head).GetResultAsync();
await App.ShowDialog(new ViewModels.InteractiveRebase(repo, commit));
await this.ShowDialogAsync(new ViewModels.InteractiveRebase(repo, commit));
e.Handled = true;
};
@@ -853,7 +853,7 @@ namespace SourceGit.Views
compareWithCurrent.Icon = this.CreateMenuIcon("Icons.Compare");
compareWithCurrent.Click += (_, _) =>
{
App.ShowWindow(new ViewModels.Compare(repo, branch, current));
this.ShowWindow(new ViewModels.Compare(repo, branch, current));
};
var compareWith = new MenuItem();
@@ -1154,7 +1154,7 @@ namespace SourceGit.Views
interactiveRebase.Click += async (_, e) =>
{
var commit = await new Commands.QuerySingleCommit(repo.FullPath, branch.Head).GetResultAsync();
await App.ShowDialog(new ViewModels.InteractiveRebase(repo, commit));
await this.ShowDialogAsync(new ViewModels.InteractiveRebase(repo, commit));
e.Handled = true;
};
@@ -1163,7 +1163,7 @@ namespace SourceGit.Views
compareWithHead.Icon = this.CreateMenuIcon("Icons.Compare");
compareWithHead.Click += (_, _) =>
{
App.ShowWindow(new ViewModels.Compare(repo, branch, current));
this.ShowWindow(new ViewModels.Compare(repo, branch, current));
};
var compareWith = new MenuItem();

View File

@@ -4,6 +4,7 @@
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.Clone"
x:DataType="vm:Clone">
@@ -19,7 +20,7 @@
Text="{DynamicResource Text.Clone}"/>
</StackPanel>
<Grid Margin="8,16,0,0" RowDefinitions="32,Auto,32,32,32,32" ColumnDefinitions="Auto,*">
<Grid Margin="8,16,0,0" RowDefinitions="32,Auto,32,32,32,32,32,32" ColumnDefinitions="Auto,*">
<TextBlock Grid.Row="0" Grid.Column="0"
HorizontalAlignment="Right"
Margin="0,0,8,0"
@@ -102,7 +103,44 @@
Watermark="{DynamicResource Text.Clone.AdditionalParam.Placeholder}"
Text="{Binding ExtraArgs, Mode=TwoWay}"/>
<CheckBox Grid.Row="5" Grid.Column="1"
<TextBlock Grid.Row="5" Grid.Column="0"
HorizontalAlignment="Right"
Margin="0,0,8,0"
Text="{DynamicResource Text.Clone.Group}"/>
<ComboBox Grid.Row="5" Grid.Column="1"
Height="28" Padding="8,0"
VerticalAlignment="Center" HorizontalAlignment="Stretch"
ItemsSource="{Binding Groups}"
SelectedItem="{Binding SelectedGroup, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate DataType="vm:RepositoryNode">
<TextBlock Text="{Binding Name, Mode=OneWay}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBlock Grid.Row="6" Grid.Column="0"
HorizontalAlignment="Right"
Margin="0,0,8,0"
Text="{DynamicResource Text.Clone.Bookmark}"/>
<ComboBox Grid.Row="6" Grid.Column="1"
Height="28" Padding="8,0"
VerticalAlignment="Center"
ItemsSource="{Binding Bookmarks}"
SelectedItem="{Binding Bookmark, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid Height="20">
<Path Width="12" Height="12"
Fill="{Binding Converter={x:Static c:IntConverters.ToBookmarkBrush}}"
HorizontalAlignment="Center" VerticalAlignment="Center"
Data="{StaticResource Icons.Bookmark}"/>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<CheckBox Grid.Row="7" Grid.Column="1"
Content="{DynamicResource Text.Clone.RecurseSubmodules}"
IsChecked="{Binding InitAndUpdateSubmodules, Mode=TwoWay}"
ToolTip.Tip="--recurse-submodules"/>

View File

@@ -62,7 +62,10 @@
<TextBlock Text="{Binding SHA}" Margin="12,0,4,0" VerticalAlignment="Center"/>
<Button Classes="icon_button" Width="24" Cursor="Hand" Click="OnCopyCommitSHA" ToolTip.Tip="{DynamicResource Text.Copy}">
<Path Width="12" Height="12" Data="{StaticResource Icons.Copy}"/>
<Grid>
<Path Width="12" Height="12" Data="{StaticResource Icons.Copy}" IsVisible="{Binding #ThisControl.IsSHACopied, Mode=OneWay, Converter={x:Static BoolConverters.Not}}"/>
<Path Width="14" Height="14" Margin="0,2,0,0" Data="{StaticResource Icons.Check}" Fill="Green" IsVisible="{Binding #ThisControl.IsSHACopied, Mode=OneWay}"/>
</Grid>
</Button>
<Button Classes="icon_button" Width="24" Cursor="Hand" Click="OnOpenContainsIn" IsVisible="{Binding #ThisControl.SupportsContainsIn}" ToolTip.Tip="{DynamicResource Text.CommitDetail.Info.ContainsIn}">

View File

@@ -5,6 +5,7 @@ using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Threading;
namespace SourceGit.Views
{
@@ -55,16 +56,51 @@ namespace SourceGit.Views
set => SetValue(ChildrenProperty, value);
}
public static readonly StyledProperty<bool> IsSHACopiedProperty =
AvaloniaProperty.Register<CommitBaseInfo, bool>(nameof(IsSHACopied));
public bool IsSHACopied
{
get => GetValue(IsSHACopiedProperty);
set => SetValue(IsSHACopiedProperty, value);
}
public CommitBaseInfo()
{
InitializeComponent();
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == ContentProperty)
{
_iconResetTimer?.Dispose();
SetCurrentValue(IsSHACopiedProperty, false);
}
}
protected override void OnUnloaded(RoutedEventArgs e)
{
base.OnUnloaded(e);
_iconResetTimer?.Dispose();
}
private async void OnCopyCommitSHA(object sender, RoutedEventArgs e)
{
if (sender is Button { DataContext: Models.Commit commit })
await this.CopyTextAsync(commit.SHA);
_iconResetTimer = DispatcherTimer.RunOnce(() =>
{
if (IsSHACopied)
IsSHACopied = false;
_iconResetTimer = null;
}, TimeSpan.FromSeconds(2));
IsSHACopied = true;
e.Handled = true;
}
@@ -190,5 +226,7 @@ namespace SourceGit.Views
await this.CopyTextAsync(detail.FullMessage.Message);
e.Handled = true;
}
private IDisposable _iconResetTimer;
}
}

View File

@@ -38,7 +38,7 @@ namespace SourceGit.Views
history.Icon = this.CreateMenuIcon("Icons.Histories");
history.Click += (_, ev) =>
{
App.ShowWindow(new ViewModels.DirHistories(repo, node.FullPath, commit.SHA));
this.ShowWindow(new ViewModels.DirHistories(repo, node.FullPath, commit.SHA));
ev.Handled = true;
};
@@ -273,7 +273,7 @@ namespace SourceGit.Views
history.Icon = this.CreateMenuIcon("Icons.Histories");
history.Click += (_, ev) =>
{
App.ShowWindow(new ViewModels.FileHistories(repo.FullPath, change.Path, commit.SHA));
this.ShowWindow(new ViewModels.FileHistories(repo.FullPath, change.Path, commit.SHA));
ev.Handled = true;
};
@@ -283,7 +283,7 @@ namespace SourceGit.Views
blame.IsEnabled = change.Index != Models.ChangeState.Deleted;
blame.Click += (_, ev) =>
{
App.ShowWindow(new ViewModels.Blame(repo.FullPath, change.Path, commit));
this.ShowWindow(new ViewModels.Blame(repo.FullPath, change.Path, commit));
ev.Handled = true;
};

View File

@@ -565,7 +565,7 @@ namespace SourceGit.Views
e.Handled = true;
}
private async void OnOpenOpenAIHelper(object sender, RoutedEventArgs e)
private void OnOpenOpenAIHelper(object sender, RoutedEventArgs e)
{
if (DataContext is ViewModels.WorkingCopy vm && sender is Button button && ShowAdvancedOptions)
{
@@ -588,7 +588,7 @@ namespace SourceGit.Views
if (services.Count == 1)
{
await App.ShowDialog(new ViewModels.AIAssistant(repo, services[0], vm.Staged));
DoOpenAIAssistant(repo, services[0], vm.Staged);
e.Handled = true;
return;
}
@@ -599,9 +599,9 @@ namespace SourceGit.Views
var dup = service;
var item = new MenuItem();
item.Header = service.Name;
item.Click += async (_, ev) =>
item.Click += (_, ev) =>
{
await App.ShowDialog(new ViewModels.AIAssistant(repo, dup, vm.Staged));
DoOpenAIAssistant(repo, dup, vm.Staged);
ev.Handled = true;
};
@@ -637,5 +637,16 @@ namespace SourceGit.Views
e.Handled = true;
}
private void DoOpenAIAssistant(ViewModels.Repository repo, AI.Service service, List<Models.Change> changes)
{
var owner = TopLevel.GetTopLevel(this) as Window;
if (owner == null)
return;
var assistant = new ViewModels.AIAssistant(repo, service, changes);
var view = new AIAssistant() { DataContext = assistant };
view.Show(owner);
}
}
}

View File

@@ -105,13 +105,9 @@ namespace SourceGit.Views
_refreshTimer = DispatcherTimer.Run(() =>
{
Dispatcher.UIThread.Invoke(() =>
{
var text = GetDisplayText();
if (!text.Equals(Text, StringComparison.Ordinal))
Text = text;
});
var text = GetDisplayText();
if (!text.Equals(Text, StringComparison.Ordinal))
Text = text;
return true;
}, TimeSpan.FromSeconds(10));
}

View File

@@ -19,7 +19,7 @@ namespace SourceGit.Views
if (e.Key == Key.Enter)
{
vm.Launch();
this.ShowWindow(vm.Launch());
e.Handled = true;
}
else if (e.Key == Key.Up)
@@ -55,7 +55,7 @@ namespace SourceGit.Views
{
if (DataContext is ViewModels.CompareCommandPalette vm)
{
vm.Launch();
this.ShowWindow(vm.Launch());
e.Handled = true;
}
}

View File

@@ -129,9 +129,11 @@
<TextBlock Margin="0,12,0,0" Text="{DynamicResource Text.ConfigureCustomActionControls.Type}"/>
<ComboBox Margin="0,4,0,0" Height="28" HorizontalAlignment="Stretch" SelectedIndex="{Binding Type, Mode=TwoWay}">
<ComboBoxItem Content="TextBox"/>
<ComboBoxItem Content="PathSelector"/>
<ComboBoxItem Content="Path Selector"/>
<ComboBoxItem Content="CheckBox"/>
<ComboBoxItem Content="ComboBox"/>
<ComboBoxItem Content="Local Branch Selector"/>
<ComboBoxItem Content="Remote Branch Selector"/>
</ComboBox>
<!-- Description -->
@@ -160,7 +162,14 @@
<TextBox Margin="0,4,0,0"
CornerRadius="3"
Height="28"
Text="{Binding StringValue, Mode=TwoWay}"/>
Text="{Binding StringValue, Mode=TwoWay}">
<TextBox.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding Path="Type" Converter="{x:Static ObjectConverters.NotEqual}" ConverterParameter="{x:Static m:CustomActionControlType.LocalBranchSelector}"/>
<Binding Path="Type" Converter="{x:Static ObjectConverters.NotEqual}" ConverterParameter="{x:Static m:CustomActionControlType.RemoteBranchSelector}"/>
</MultiBinding>
</TextBox.IsVisible>
</TextBox>
<TextBlock Margin="0,2,0,0"
Classes="small"
TextWrapping="Wrap"
@@ -187,12 +196,29 @@
</TextBlock.IsVisible>
</TextBlock>
<!-- String Formatter -->
<TextBlock Margin="0,12,0,0"
Text="{DynamicResource Text.ConfigureCustomActionControls.StringFormatter}"
IsVisible="{Binding Type, Converter={x:Static ObjectConverters.Equal}, ConverterParameter={x:Static m:CustomActionControlType.TextBox}}"/>
<TextBox Margin="0,4,0,0"
CornerRadius="3"
Height="28"
Text="{Binding StringFormatter, Mode=TwoWay}"
IsVisible="{Binding Type, Converter={x:Static ObjectConverters.Equal}, ConverterParameter={x:Static m:CustomActionControlType.TextBox}}"/>
<TextBlock Margin="0,2,0,0"
Classes="small"
TextWrapping="Wrap"
Text="{DynamicResource Text.ConfigureCustomActionControls.StringFormatter.Tip}"
Foreground="{DynamicResource Brush.FG2}"
IsVisible="{Binding Type, Converter={x:Static ObjectConverters.Equal}, ConverterParameter={x:Static m:CustomActionControlType.TextBox}}"/>
<!-- BoolValue is needed by CheckBox/PathSelector -->
<Grid Margin="0,8,0,0" ColumnDefinitions="Auto,*">
<Grid.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.Or}">
<Binding Path="Type" Converter="{x:Static ObjectConverters.Equal}" ConverterParameter="{x:Static m:CustomActionControlType.CheckBox}"/>
<Binding Path="Type" Converter="{x:Static ObjectConverters.Equal}" ConverterParameter="{x:Static m:CustomActionControlType.PathSelector}"/>
<Binding Path="Type" Converter="{x:Static ObjectConverters.Equal}" ConverterParameter="{x:Static m:CustomActionControlType.RemoteBranchSelector}"/>
</MultiBinding>
</Grid.IsVisible>
<TextBlock Grid.Column="0"
@@ -201,6 +227,9 @@
<TextBlock Grid.Column="0"
Text="{DynamicResource Text.ConfigureCustomActionControls.IsFolder}"
IsVisible="{Binding Type, Converter={x:Static ObjectConverters.Equal}, ConverterParameter={x:Static m:CustomActionControlType.PathSelector}}"/>
<TextBlock Grid.Column="0"
Text="{DynamicResource Text.ConfigureCustomActionControls.UseFriendlyName}"
IsVisible="{Binding Type, Converter={x:Static ObjectConverters.Equal}, ConverterParameter={x:Static m:CustomActionControlType.RemoteBranchSelector}}"/>
<CheckBox Grid.Column="1"
Margin="8,0,0,0"
Height="28"

View File

@@ -40,7 +40,10 @@ namespace SourceGit.Views
private async void OnMerge(object _, RoutedEventArgs e)
{
if (DataContext is ViewModels.Conflict vm)
await vm.MergeAsync();
{
var request = vm.CreateOpenMergeEditorRequest();
await this.ShowDialogAsync(request);
}
e.Handled = true;
}

View File

@@ -1,5 +1,7 @@
using System;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Shapes;
using Avalonia.Media;
@@ -44,5 +46,63 @@ namespace SourceGit.Views
return null;
}
public static void ShowWindow(this Control control, object data)
{
if (data == null)
return;
if (data is not ChromelessWindow window)
{
window = CreateFromViewModels(data) as ChromelessWindow;
if (window == null)
return;
window.DataContext = data;
}
do
{
var owner = TopLevel.GetTopLevel(control) as Window;
if (owner != null)
{
// Get the screen where current window locates.
var screen = owner.Screens.ScreenFromWindow(owner) ?? owner.Screens.Primary;
if (screen == null)
break;
// Calculate the startup position (Center Screen Mode) of target window
var rect = new PixelRect(PixelSize.FromSize(window.ClientSize, owner.DesktopScaling));
var centeredRect = screen.WorkingArea.CenterRect(rect);
if (owner.Screens.ScreenFromPoint(centeredRect.Position) == null)
break;
// Use the startup position
window.WindowStartupLocation = WindowStartupLocation.Manual;
window.Position = centeredRect.Position;
}
} while (false);
window.Show();
}
public static Task ShowDialogAsync(this Control control, object data)
{
var owner = TopLevel.GetTopLevel(control) as Window;
if (owner == null)
return null;
if (data is ChromelessWindow window)
return window.ShowDialog(owner);
window = CreateFromViewModels(data) as ChromelessWindow;
if (window != null)
{
window.DataContext = data;
return window.ShowDialog(owner);
}
return null;
}
}
}

View File

@@ -321,11 +321,26 @@
<ContentControl Content="{Binding New}">
<ContentControl.DataTemplates>
<DataTemplate DataType="m:RevisionSubmodule">
<v:CommitBaseInfo Margin="0,4,8,6" Content="{Binding Commit}" FullMessage="{Binding FullMessage}"/>
<StackPanel Orientation="Vertical">
<TextBlock Margin="0,8,0,4"
HorizontalAlignment="Center"
Foreground="OrangeRed"
FontFamily="{DynamicResource Fonts.Monospace}"
Text="{Binding UncommittedChanges, Converter={x:Static c:StringConverters.FormatByResourceKey}, ConverterParameter='Diff.Submodule.UncommittedChanges'}"
IsVisible="{Binding UncommittedChanges, Converter={x:Static c:IntConverters.IsGreaterThanZero}}"/>
<v:CommitBaseInfo Margin="0,4,8,6" Content="{Binding Commit}" FullMessage="{Binding FullMessage}"/>
</StackPanel>
</DataTemplate>
</ContentControl.DataTemplates>
</ContentControl>
</Border>
<Button Classes="flat"
Margin="0,8"
HorizontalAlignment="Center"
Content="{DynamicResource Text.SubmoduleRevisionCompare.OpenDetails}"
Click="OnOpenSubmoduleRevisionCompare"
IsEnabled="{Binding CanOpenDetails, Mode=OneWay}"/>
</StackPanel>
</ScrollViewer>
</Grid>

View File

@@ -42,5 +42,14 @@ namespace SourceGit.Views
this.FindDescendantOfType<ThemedTextDiffPresenter>()?.GotoChange(ViewModels.BlockNavigationDirection.Last);
e.Handled = true;
}
private void OnOpenSubmoduleRevisionCompare(object sender, RoutedEventArgs e)
{
if (sender is Button { DataContext: Models.SubmoduleDiff diff } && diff.CanOpenDetails)
{
var vm = new ViewModels.SubmoduleRevisionCompare(diff);
this.ShowWindow(vm);
}
}
}
}

View File

@@ -4,6 +4,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:m="using:SourceGit.Models"
xmlns:vm="using:SourceGit.ViewModels"
xmlns:v="using:SourceGit.Views"
xmlns:c="using:SourceGit.Converters"
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450"
x:Class="SourceGit.Views.ExecuteCustomAction"
@@ -140,6 +141,28 @@
IsVisible="{Binding Description, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
</Grid>
</DataTemplate>
<DataTemplate DataType="vm:CustomActionControlBranchSelector">
<Grid RowDefinitions="32,Auto" ColumnDefinitions="150,*">
<TextBlock Grid.Row="0" Grid.Column="0"
Text="{Binding Label}"
HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,8,0"/>
<v:BranchSelector Grid.Row="0" Grid.Column="1"
Height="28" Padding="8,0"
VerticalAlignment="Center" HorizontalAlignment="Stretch"
Branches="{Binding Branches, Mode=OneWay}"
SelectedBranch="{Binding SelectedBranch, Mode=TwoWay}"/>
<TextBlock Grid.Row="1" Grid.Column="1"
Classes="small"
Margin="0,2"
TextWrapping="Wrap"
Text="{Binding Description}"
Foreground="{DynamicResource Brush.FG2}"
IsVisible="{Binding Description, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
</Grid>
</DataTemplate>
</ItemsControl.DataTemplates>
</ItemsControl>
</StackPanel>

View File

@@ -19,7 +19,7 @@ namespace SourceGit.Views
if (e.Key == Key.Enter)
{
vm.Launch();
this.ShowWindow(vm.Launch());
e.Handled = true;
}
else if (e.Key == Key.Up)
@@ -55,7 +55,7 @@ namespace SourceGit.Views
{
if (DataContext is ViewModels.FileHistoryCommandPalette vm)
{
vm.Launch();
this.ShowWindow(vm.Launch());
e.Handled = true;
}
}

View File

@@ -27,7 +27,7 @@
IsVisible="{Binding Type, Converter={x:Static ObjectConverters.Equal}, ConverterParameter={x:Static m:GitFlowBranchType.Hotfix}}"/>
</StackPanel>
<Grid Margin="0,16,0,0" RowDefinitions="32,32,32,32" ColumnDefinitions="150,*">
<Grid Margin="0,16,0,0" RowDefinitions="32,32,32" ColumnDefinitions="150,*">
<TextBlock Grid.Row="0" Grid.Column="0"
HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,8,0"
@@ -43,11 +43,6 @@
ToolTip.Tip="--squash"/>
<CheckBox Grid.Row="2" Grid.Column="1"
Content="{DynamicResource Text.GitFlow.FinishWithPush}"
IsChecked="{Binding AutoPush, Mode=TwoWay}"
ToolTip.Tip="--push"/>
<CheckBox Grid.Row="3" Grid.Column="1"
Content="{DynamicResource Text.GitFlow.KeepBranchAfterFinish}"
IsChecked="{Binding KeepBranch, Mode=TwoWay}"
ToolTip.Tip="-k"/>

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Collections;
@@ -832,7 +833,7 @@ namespace SourceGit.Views
manually.Icon = this.CreateMenuIcon("Icons.InteractiveRebase");
manually.Click += async (_, e) =>
{
await App.ShowDialog(new ViewModels.InteractiveRebase(repo, commit));
await this.ShowDialogAsync(new ViewModels.InteractiveRebase(repo, commit));
e.Handled = true;
};
@@ -841,7 +842,7 @@ namespace SourceGit.Views
reword.Icon = this.CreateMenuIcon("Icons.Rename");
reword.Click += async (_, e) =>
{
await vm.InteractiveRebaseAsync(commit, Models.InteractiveRebaseAction.Reword);
await InteractiveRebaseWithPrefillActionAsync(repo, commit, Models.InteractiveRebaseAction.Reword);
e.Handled = true;
};
@@ -850,7 +851,7 @@ namespace SourceGit.Views
edit.Icon = this.CreateMenuIcon("Icons.Edit");
edit.Click += async (_, e) =>
{
await vm.InteractiveRebaseAsync(commit, Models.InteractiveRebaseAction.Edit);
await InteractiveRebaseWithPrefillActionAsync(repo, commit, Models.InteractiveRebaseAction.Edit);
e.Handled = true;
};
@@ -859,7 +860,7 @@ namespace SourceGit.Views
squash.Icon = this.CreateMenuIcon("Icons.SquashIntoParent");
squash.Click += async (_, e) =>
{
await vm.InteractiveRebaseAsync(commit, Models.InteractiveRebaseAction.Squash);
await InteractiveRebaseWithPrefillActionAsync(repo, commit, Models.InteractiveRebaseAction.Squash);
e.Handled = true;
};
@@ -868,7 +869,7 @@ namespace SourceGit.Views
fixup.Icon = this.CreateMenuIcon("Icons.Fix");
fixup.Click += async (_, e) =>
{
await vm.InteractiveRebaseAsync(commit, Models.InteractiveRebaseAction.Fixup);
await InteractiveRebaseWithPrefillActionAsync(repo, commit, Models.InteractiveRebaseAction.Fixup);
e.Handled = true;
};
@@ -877,7 +878,7 @@ namespace SourceGit.Views
drop.Icon = this.CreateMenuIcon("Icons.Clear");
drop.Click += async (_, e) =>
{
await vm.InteractiveRebaseAsync(commit, Models.InteractiveRebaseAction.Drop);
await InteractiveRebaseWithPrefillActionAsync(repo, commit, Models.InteractiveRebaseAction.Drop);
e.Handled = true;
};
@@ -902,7 +903,7 @@ namespace SourceGit.Views
interactiveRebase.Icon = this.CreateMenuIcon("Icons.InteractiveRebase");
interactiveRebase.Click += async (_, e) =>
{
await App.ShowDialog(new ViewModels.InteractiveRebase(repo, commit));
await this.ShowDialogAsync(new ViewModels.InteractiveRebase(repo, commit));
e.Handled = true;
};
@@ -1417,6 +1418,22 @@ namespace SourceGit.Views
menu.Items.Add(submenu);
}
private async Task InteractiveRebaseWithPrefillActionAsync(ViewModels.Repository repo, Models.Commit target, Models.InteractiveRebaseAction action)
{
var prefill = new ViewModels.InteractiveRebasePrefill(target.SHA, action);
var start = action switch
{
Models.InteractiveRebaseAction.Squash or Models.InteractiveRebaseAction.Fixup => $"{target.SHA}~~",
_ => $"{target.SHA}~",
};
var on = await new Commands.QuerySingleCommit(repo.FullPath, start).GetResultAsync();
if (on == null)
repo.SendNotification($"Commit '{start}' is not a valid revision for `git rebase -i`!", true);
else
await this.ShowDialogAsync(new ViewModels.InteractiveRebase(repo, on, prefill));
}
private double _lastGraphStartY = 0;
private double _lastGraphClipWidth = 0;
private double _lastGraphRowHeight = 0;

View File

@@ -45,7 +45,7 @@
FontSize="{Binding Source={x:Static vm:Preferences.Instance}, Path=DefaultFontSize, Converter={x:Static c:DoubleConverters.Increase}}"
Margin="0,0,0,8"/>
<Grid RowDefinitions="20,20,20,20,20,20,20,20,20,20" ColumnDefinitions="150,*">
<Grid RowDefinitions="20,20,20,20,20,20,20,20,20,20,20" ColumnDefinitions="150,*">
<TextBlock Grid.Row="0" Grid.Column="0" Classes="bold" Text="{OnPlatform Ctrl+\,, macOS=⌘+\,}"/>
<TextBlock Grid.Row="0" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Global.OpenPreferences}"/>
@@ -64,17 +64,20 @@
<TextBlock Grid.Row="5" Grid.Column="0" Classes="bold" Text="{OnPlatform Ctrl+N, macOS=⌘+N}"/>
<TextBlock Grid.Row="5" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Global.Clone}" />
<TextBlock Grid.Row="6" Grid.Column="0" Classes="bold" Text="{OnPlatform Ctrl+Alt+P, macOS=⌘+⌥+P}"/>
<TextBlock Grid.Row="6" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Global.ShowWorkspaceDropdownMenu}" />
<TextBlock Grid.Row="6" Grid.Column="0" Classes="bold" Text="{OnPlatform Ctrl+Shift+O, macOS=⌘+⇧+O}"/>
<TextBlock Grid.Row="6" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Global.OpenLocalRepository}" />
<TextBlock Grid.Row="7" Grid.Column="0" Classes="bold" Text="{OnPlatform Ctrl+P, macOS=⌘+P}"/>
<TextBlock Grid.Row="7" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Global.SwitchTab}" />
<TextBlock Grid.Row="7" Grid.Column="0" Classes="bold" Text="{OnPlatform Ctrl+Alt+P, macOS=⌘+⌥+P}"/>
<TextBlock Grid.Row="7" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Global.ShowWorkspaceDropdownMenu}" />
<TextBlock Grid.Row="8" Grid.Column="0" Classes="bold" Text="{OnPlatform 'Ctrl+-/=', macOS='⌘+-/='}"/>
<TextBlock Grid.Row="8" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Global.Zoom}" />
<TextBlock Grid.Row="8" Grid.Column="0" Classes="bold" Text="{OnPlatform Ctrl+P, macOS=⌘+P}"/>
<TextBlock Grid.Row="8" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Global.SwitchTab}" />
<TextBlock Grid.Row="9" Grid.Column="0" Classes="bold" Text="{OnPlatform Ctrl+Q, macOS=⌘+Q}"/>
<TextBlock Grid.Row="9" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Quit}" />
<TextBlock Grid.Row="9" Grid.Column="0" Classes="bold" Text="{OnPlatform 'Ctrl+-/=', macOS='⌘+-/='}"/>
<TextBlock Grid.Row="9" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Global.Zoom}" />
<TextBlock Grid.Row="10" Grid.Column="0" Classes="bold" Text="{OnPlatform Ctrl+Q, macOS=⌘+Q}"/>
<TextBlock Grid.Row="10" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Quit}" />
</Grid>
<TextBlock Text="{DynamicResource Text.Hotkeys.Repo}"

View File

@@ -106,7 +106,7 @@ namespace SourceGit.Views
Activate();
}
protected override async void OnOpened(EventArgs e)
protected override void OnOpened(EventArgs e)
{
base.OnOpened(e);
@@ -114,8 +114,6 @@ namespace SourceGit.Views
var state = preferences.Layout.LauncherWindowState;
if (state == WindowState.Maximized || state == WindowState.FullScreen)
WindowState = WindowState.Maximized;
await preferences.UpdateAvailableAIModelsAsync();
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
@@ -174,14 +172,14 @@ namespace SourceGit.Views
{
if (e is { KeyModifiers: KeyModifiers.Control, Key: Key.OemComma })
{
await App.ShowDialog(new Preferences());
await this.ShowDialogAsync(new Preferences());
e.Handled = true;
return;
}
if (e is { KeyModifiers: KeyModifiers.None, Key: Key.F1 })
{
await App.ShowDialog(new Hotkeys());
await this.ShowDialogAsync(new Hotkeys());
e.Handled = true;
return;
}
@@ -233,6 +231,16 @@ namespace SourceGit.Views
return;
}
if (e.Key == Key.O && e.KeyModifiers.HasFlag(KeyModifiers.Shift))
{
if (vm.ActivePage.Data is not ViewModels.Welcome)
vm.AddNewTab();
ViewModels.Welcome.Instance.OpenLocalRepository();
e.Handled = true;
return;
}
if (e.Key == Key.T)
{
vm.AddNewTab();
@@ -330,10 +338,17 @@ namespace SourceGit.Views
base.OnClosing(e);
if (!Design.IsDesignMode && DataContext is ViewModels.Launcher launcher)
{
launcher.CloseAll();
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
if (!Design.IsDesignMode)
ViewModels.Preferences.Instance.Save();
launcher.Quit();
}
App.Quit(0);
}
private void OnPositionChanged(object sender, PixelPointEventArgs e)
@@ -396,7 +411,7 @@ namespace SourceGit.Views
configure.Header = App.Text("Workspace.Configure");
configure.Click += async (_, ev) =>
{
await App.ShowDialog(new ViewModels.ConfigureWorkspace());
await this.ShowDialogAsync(new ViewModels.ConfigureWorkspace());
ev.Handled = true;
};
menu.Items.Add(configure);

View File

@@ -351,8 +351,6 @@ namespace SourceGit.Views
Models.TextMateHelper.SetThemeByApp(_textMate);
else if (change.Property == SelectedChunkProperty)
TextArea.TextView.InvalidateVisual();
else if (change.Property == MaxLineNumberProperty)
TextArea.LeftMargins[0].InvalidateMeasure();
}
private void UpdateContent()
@@ -439,6 +437,9 @@ namespace SourceGit.Views
return;
}
foreach (var margin in TextArea.LeftMargins)
margin.InvalidateMeasure();
var lines = Lines;
var start = int.MaxValue;
var count = 0;

View File

@@ -0,0 +1,77 @@
<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"
xmlns:c="using:SourceGit.Converters"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SourceGit.Views.OpenLocalRepository"
x:DataType="vm:OpenLocalRepository">
<StackPanel Orientation="Vertical" Margin="8,0,0,0">
<StackPanel Orientation="Horizontal">
<Path Width="16" Height="16"
Margin="0,2,0,0"
Data="{StaticResource Icons.Folder.Open}"/>
<TextBlock FontSize="18"
Margin="8,0,0,0"
Classes="bold"
Text="{DynamicResource Text.OpenLocalRepository}"/>
</StackPanel>
<Grid Margin="8,16,0,0" RowDefinitions="32,32,32" ColumnDefinitions="Auto,*">
<TextBlock Grid.Row="0" Grid.Column="0"
HorizontalAlignment="Right"
Margin="0,0,8,0"
Text="{DynamicResource Text.OpenLocalRepository.Path}"/>
<TextBox Grid.Row="0" Grid.Column="1"
Height="28"
CornerRadius="3"
Text="{Binding RepoPath, Mode=TwoWay}">
<TextBox.InnerRightContent>
<Button Classes="icon_button" Width="28" Height="28" Margin="4,0,0,0" Click="OnSelectRepositoryFolder">
<Path Data="{StaticResource Icons.Folder.Open}" Fill="{DynamicResource Brush.FG1}"/>
</Button>
</TextBox.InnerRightContent>
</TextBox>
<TextBlock Grid.Row="1" Grid.Column="0"
HorizontalAlignment="Right"
Margin="0,0,8,0"
Text="{DynamicResource Text.OpenLocalRepository.Group}"/>
<ComboBox Grid.Row="1" Grid.Column="1"
Height="28" Padding="8,0"
VerticalAlignment="Center" HorizontalAlignment="Stretch"
ItemsSource="{Binding Groups}"
SelectedItem="{Binding Group, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate DataType="vm:RepositoryNode">
<TextBlock Text="{Binding Name, Mode=OneWay}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBlock Grid.Row="2" Grid.Column="0"
HorizontalAlignment="Right"
Margin="0,0,8,0"
Text="{DynamicResource Text.OpenLocalRepository.Bookmark}"/>
<ComboBox Grid.Row="2" Grid.Column="1"
Height="28" Padding="8,0"
VerticalAlignment="Center"
ItemsSource="{Binding Bookmarks}"
SelectedItem="{Binding Bookmark, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid Height="20">
<Path Width="12" Height="12"
Fill="{Binding Converter={x:Static c:IntConverters.ToBookmarkBrush}}"
HorizontalAlignment="Center" VerticalAlignment="Center"
Data="{StaticResource Icons.Bookmark}"/>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
</StackPanel>
</UserControl>

View File

@@ -0,0 +1,56 @@
using System;
using System.IO;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Platform.Storage;
namespace SourceGit.Views
{
public partial class OpenLocalRepository : UserControl
{
public OpenLocalRepository()
{
InitializeComponent();
}
private async void OnSelectRepositoryFolder(object _1, RoutedEventArgs e)
{
if (DataContext is not ViewModels.OpenLocalRepository vm)
return;
var topLevel = TopLevel.GetTopLevel(this);
if (topLevel == null)
return;
var preference = ViewModels.Preferences.Instance;
var workspace = preference.GetActiveWorkspace();
var initDir = workspace.DefaultCloneDir;
if (string.IsNullOrEmpty(initDir) || !Directory.Exists(initDir))
initDir = preference.GitDefaultCloneDir;
var options = new FolderPickerOpenOptions() { AllowMultiple = false };
if (Directory.Exists(initDir))
{
var folder = await topLevel.StorageProvider.TryGetFolderFromPathAsync(initDir);
options.SuggestedStartLocation = folder;
}
try
{
var selected = await topLevel.StorageProvider.OpenFolderPickerAsync(options);
if (selected.Count == 1)
{
var folder = selected[0];
vm.RepoPath = folder is { Path: { IsAbsoluteUri: true } path } ? path.LocalPath : folder?.Path.ToString();
}
}
catch (Exception exception)
{
Models.Notification.Send(null, $"Failed to open repository: {exception.Message}", true);
}
e.Handled = true;
}
}
}

View File

@@ -206,7 +206,7 @@ namespace SourceGit.Views
}
var preferences = ViewModels.Preferences.Instance;
await preferences.UpdateAvailableAIModelsAsync();
preferences.UpdateAvailableAIModels();
preferences.Save();
}
@@ -386,7 +386,7 @@ namespace SourceGit.Views
if (sender is CheckBox box)
{
ViewModels.Preferences.Instance.UseSystemWindowFrame = box.IsChecked == true;
await App.ShowDialog(new ConfirmRestart());
await this.ShowDialogAsync(new ConfirmRestart());
}
e.Handled = true;
@@ -486,12 +486,7 @@ namespace SourceGit.Views
if (sender is not Button { DataContext: Models.CustomAction act })
return;
var dialog = new ConfigureCustomActionControls()
{
DataContext = new ViewModels.ConfigureCustomActionControls(act.Controls)
};
await dialog.ShowDialog(this);
await this.ShowDialogAsync(new ViewModels.ConfigureCustomActionControls(act.Controls));
e.Handled = true;
}

View File

@@ -56,12 +56,7 @@ namespace SourceGit.Views
if (sender is not Button { DataContext: Models.CustomAction act })
return;
var dialog = new ConfigureCustomActionControls()
{
DataContext = new ViewModels.ConfigureCustomActionControls(act.Controls)
};
await dialog.ShowDialog(this);
await this.ShowDialogAsync(new ViewModels.ConfigureCustomActionControls(act.Controls));
e.Handled = true;
}

View File

@@ -138,7 +138,7 @@ namespace SourceGit.Views
{
if (DataContext is ViewModels.Repository repo)
{
await App.ShowDialog(new ViewModels.Statistics(repo.FullPath));
await this.ShowDialogAsync(new ViewModels.Statistics(repo.FullPath));
e.Handled = true;
}
}
@@ -147,7 +147,7 @@ namespace SourceGit.Views
{
if (DataContext is ViewModels.Repository repo)
{
await App.ShowDialog(new ViewModels.RepositoryConfigure(repo));
await this.ShowDialogAsync(new ViewModels.RepositoryConfigure(repo));
e.Handled = true;
}
}
@@ -387,7 +387,7 @@ namespace SourceGit.Views
{
locks.Click += async (_, e) =>
{
await App.ShowDialog(new ViewModels.LFSLocks(repo, repo.Remotes[0].Name));
await this.ShowDialogAsync(new ViewModels.LFSLocks(repo, repo.Remotes[0].Name));
e.Handled = true;
};
}
@@ -400,7 +400,7 @@ namespace SourceGit.Views
lockRemote.Header = remoteName;
lockRemote.Click += async (_, e) =>
{
await App.ShowDialog(new ViewModels.LFSLocks(repo, remoteName));
await this.ShowDialogAsync(new ViewModels.LFSLocks(repo, remoteName));
e.Handled = true;
};
locks.Items.Add(lockRemote);
@@ -494,7 +494,7 @@ namespace SourceGit.Views
{
if (DataContext is ViewModels.Repository repo)
{
await App.ShowDialog(new ViewModels.ViewLogs(repo));
await this.ShowDialogAsync(new ViewModels.ViewLogs(repo));
e.Handled = true;
}
}

View File

@@ -472,7 +472,7 @@ namespace SourceGit.Views
history.Icon = this.CreateMenuIcon("Icons.Histories");
history.Click += (_, ev) =>
{
App.ShowWindow(new ViewModels.DirHistories(repo, path, commit.SHA));
this.ShowWindow(new ViewModels.DirHistories(repo, path, commit.SHA));
ev.Handled = true;
};
@@ -601,7 +601,7 @@ namespace SourceGit.Views
history.Icon = this.CreateMenuIcon("Icons.Histories");
history.Click += (_, ev) =>
{
App.ShowWindow(new ViewModels.FileHistories(repo.FullPath, file.Path, commit.SHA));
this.ShowWindow(new ViewModels.FileHistories(repo.FullPath, file.Path, commit.SHA));
ev.Handled = true;
};
@@ -611,7 +611,7 @@ namespace SourceGit.Views
blame.IsEnabled = file.Type == Models.ObjectType.Blob;
blame.Click += (_, ev) =>
{
App.ShowWindow(new ViewModels.Blame(repo.FullPath, file.Path, commit));
this.ShowWindow(new ViewModels.Blame(repo.FullPath, file.Path, commit));
ev.Handled = true;
};

View File

@@ -0,0 +1,193 @@
<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: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.SubmoduleRevisionCompare"
x:DataType="vm:SubmoduleRevisionCompare"
x:Name="ThisControl"
Icon="/App.ico"
Title="{DynamicResource Text.SubmoduleRevisionCompare}"
MinWidth="1280" MinHeight="720">
<Grid RowDefinitions="Auto,64,*">
<!-- TitleBar -->
<Grid Grid.Row="0" Height="28" IsVisible="{Binding !#ThisControl.UseSystemWindowFrame}">
<!-- Bottom border -->
<Border Background="{DynamicResource Brush.TitleBar}"
BorderThickness="0,0,0,1" BorderBrush="{DynamicResource Brush.Border2}"
DoubleTapped="MaximizeOrRestoreWindow"
PointerPressed="BeginMoveWindow"/>
<Path Width="12" Height="12"
Margin="10,0,0,0"
HorizontalAlignment="Left"
Data="{StaticResource Icons.Compare}"
IsVisible="{OnPlatform True, macOS=False}"/>
<TextBlock Classes="bold"
Text="{DynamicResource Text.SubmoduleRevisionCompare}"
HorizontalAlignment="Center" VerticalAlignment="Center"
IsHitTestVisible="False"/>
<!-- Caption Buttons (Windows/Linux) -->
<v:CaptionButtons HorizontalAlignment="Right" IsVisible="{OnPlatform True, macOS=False}"/>
</Grid>
<!-- Compare Targets -->
<Border Grid.Row="1">
<Grid Margin="8" ColumnDefinitions="*,48,*">
<Border Grid.Column="0" BorderBrush="{DynamicResource Brush.Diff.DeletedHighlight}" BorderThickness="1" Background="{DynamicResource Brush.Contents}" CornerRadius="4" Padding="4">
<Grid RowDefinitions="Auto,*">
<Grid Grid.Row="0" ColumnDefinitions="Auto,*,Auto,Auto">
<v:Avatar Grid.Column="0"
Width="16" Height="16"
VerticalAlignment="Center"
IsHitTestVisible="False"
User="{Binding Base.Author}"/>
<TextBlock Grid.Column="1" Text="{Binding Base.Author.Name}" Margin="8,0,0,0"/>
<TextBlock Grid.Column="2"
Text="{Binding Base.SHA, Converter={x:Static c:StringConverters.ToShortSHA}}"
Foreground="DarkOrange" Margin="8,0,0,0"
TextDecorations="Underline"
Cursor="Hand"/>
<v:DateTimePresenter Grid.Column="3"
Margin="8,0,0,0"
Timestamp="{Binding Base.CommitterTime}"
Foreground="{DynamicResource Brush.FG2}"/>
</Grid>
<TextBlock Grid.Row="1" Text="{Binding Base.Subject}" VerticalAlignment="Bottom"/>
</Grid>
</Border>
<!-- Swap Button -->
<Button Grid.Column="1" Classes="icon_button" Command="{Binding Swap}" HorizontalAlignment="Center" ToolTip.Tip="{DynamicResource Text.Diff.SwapCommits}">
<Path Width="16" Height="16" Data="{DynamicResource Icons.Compare}"/>
</Button>
<Border Grid.Column="2" BorderBrush="{DynamicResource Brush.Diff.AddedHighlight}" BorderThickness="1" Background="{DynamicResource Brush.Contents}" CornerRadius="4" Padding="4">
<Grid RowDefinitions="Auto,*">
<Grid Grid.Row="0" ColumnDefinitions="Auto,*,Auto,Auto">
<v:Avatar Grid.Column="0"
Width="16" Height="16"
VerticalAlignment="Center"
IsHitTestVisible="False"
User="{Binding To.Author}"/>
<TextBlock Grid.Column="1" Text="{Binding To.Author.Name}" Margin="8,0,0,0"/>
<TextBlock Grid.Column="2"
Text="{Binding To.SHA, Converter={x:Static c:StringConverters.ToShortSHA}}"
Foreground="DarkOrange"
Margin="8,0,0,0"
TextDecorations="Underline"
Cursor="Hand"/>
<v:DateTimePresenter Grid.Column="3"
Margin="8,0,0,0"
Timestamp="{Binding To.CommitterTime}"
Foreground="{DynamicResource Brush.FG2}"/>
</Grid>
<TextBlock Grid.Row="1" Text="{Binding To.Subject}" VerticalAlignment="Bottom"/>
</Grid>
</Border>
</Grid>
</Border>
<!-- Changes -->
<Border Grid.Row="2">
<Grid Margin="8,0,8,8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="256" MinWidth="200" MaxWidth="480"/>
<ColumnDefinition Width="4"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" RowDefinitions="26,*,26">
<!-- Search & Display Mode -->
<Grid Grid.Row="0" ColumnDefinitions="*,18">
<TextBox Grid.Column="0"
x:Name="ChangeSearchBox"
Height="26"
BorderThickness="1" BorderBrush="{DynamicResource Brush.Border2}"
Background="Transparent"
CornerRadius="4"
Watermark="{DynamicResource Text.CommitDetail.Changes.Search}"
Text="{Binding SearchFilter, Mode=TwoWay}">
<TextBox.InnerLeftContent>
<Path Width="14" Height="14" Margin="4,0,0,0" Fill="{DynamicResource Brush.FG2}" Data="{StaticResource Icons.Search}"/>
</TextBox.InnerLeftContent>
<TextBox.InnerRightContent>
<Button Classes="icon_button"
IsVisible="{Binding SearchFilter, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
Command="{Binding ClearSearchFilter}">
<Path Width="14" Height="14" Fill="{DynamicResource Brush.FG2}" Data="{StaticResource Icons.Clear}"/>
</Button>
</TextBox.InnerRightContent>
</TextBox>
<v:ChangeViewModeSwitcher Grid.Column="1"
Width="14" Height="14"
HorizontalAlignment="Right"
ViewMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=CommitChangeViewMode, Mode=TwoWay}"/>
</Grid>
<!-- Changes -->
<Border Grid.Row="1" Margin="0,4,0,0" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1" Background="{DynamicResource Brush.Contents}">
<v:ChangeCollectionView ViewMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=CommitChangeViewMode}"
EnableCompactFolders="{Binding Source={x:Static vm:Preferences.Instance}, Path=EnableCompactFoldersInChangesTree}"
Changes="{Binding VisibleChanges}"
SelectedChanges="{Binding SelectedChanges, Mode=TwoWay}"
ContextRequested="OnChangeContextRequested"
KeyDown="OnChangeCollectionViewKeyDown"/>
</Border>
<!-- Loading Status Icon -->
<v:LoadingIcon Grid.Row="1"
Width="48" Height="48"
HorizontalAlignment="Center" VerticalAlignment="Center"
IsVisible="{Binding IsLoading}"/>
<!-- Summary -->
<Border Grid.Row="2" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1,0,1,1" Background="Transparent">
<TextBlock Margin="4,0,0,0"
Foreground="{DynamicResource Brush.FG2}"
HorizontalAlignment="Left" VerticalAlignment="Center">
<Run Text="{Binding TotalChanges, Mode=OneWay}" FontWeight="Bold"/>
<Run Text="{DynamicResource Text.CommitDetail.Changes.Count}"/>
</TextBlock>
</Border>
</Grid>
<GridSplitter Grid.Column="1"
MinWidth="1"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Background="Transparent"
Focusable="False"/>
<Grid Grid.Column="2">
<Border BorderThickness="1" BorderBrush="{DynamicResource Brush.Border2}">
<StackPanel Orientation="Vertical" VerticalAlignment="Center">
<Path Width="64" Height="64" Data="{StaticResource Icons.Diff}" Fill="{DynamicResource Brush.FG2}"/>
<TextBlock Margin="0,16,0,0"
Text="{DynamicResource Text.Diff.Welcome}"
FontSize="18" FontWeight="Bold"
Foreground="{DynamicResource Brush.FG2}"
HorizontalAlignment="Center"/>
</StackPanel>
</Border>
<ContentControl Content="{Binding DiffContext}">
<ContentControl.DataTemplates>
<DataTemplate DataType="vm:DiffContext">
<v:DiffView/>
</DataTemplate>
</ContentControl.DataTemplates>
</ContentControl>
</Grid>
</Grid>
</Border>
</Grid>
</v:ChromelessWindow>

View File

@@ -0,0 +1,190 @@
using System;
using System.IO;
using System.Text;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Platform.Storage;
namespace SourceGit.Views
{
public partial class SubmoduleRevisionCompare : ChromelessWindow
{
public SubmoduleRevisionCompare()
{
InitializeComponent();
}
private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e)
{
if (DataContext is ViewModels.SubmoduleRevisionCompare { SelectedChanges: { Count: > 0 } selected } vm &&
sender is ChangeCollectionView view)
{
var menu = new ContextMenu();
var patch = new MenuItem();
patch.Header = App.Text("FileCM.SaveAsPatch");
patch.Icon = this.CreateMenuIcon("Icons.Save");
patch.Click += async (_, e) =>
{
var storageProvider = this.StorageProvider;
if (storageProvider == null)
return;
var options = new FilePickerSaveOptions();
options.Title = App.Text("FileCM.SaveAsPatch");
options.DefaultExtension = ".patch";
options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }];
try
{
var storageFile = await storageProvider.SaveFilePickerAsync(options);
if (storageFile != null)
{
var saveTo = storageFile.Path.LocalPath;
var succ = await vm.SaveChangesAsPatchAsync(selected, saveTo);
if (succ)
await new Alert().ShowAsync(this, "Save patch successfully.", false);
}
}
catch (Exception exception)
{
await new Alert().ShowAsync(this, $"Failed to save as patch: {exception.Message}", true);
}
e.Handled = true;
};
if (selected.Count == 1)
{
var change = selected[0];
var openWithMerger = new MenuItem();
openWithMerger.Header = App.Text("OpenInExternalMergeTool");
openWithMerger.Icon = this.CreateMenuIcon("Icons.OpenWith");
openWithMerger.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+D" : "Ctrl+Shift+D";
openWithMerger.Click += (_, ev) =>
{
vm.OpenInExternalDiffTool(change);
ev.Handled = true;
};
menu.Items.Add(openWithMerger);
if (change.Index != Models.ChangeState.Deleted)
{
var full = vm.GetAbsPath(change.Path);
var explore = new MenuItem();
explore.Header = App.Text("RevealFile");
explore.Icon = this.CreateMenuIcon("Icons.Explore");
explore.IsEnabled = File.Exists(full);
explore.Click += (_, ev) =>
{
Native.OS.OpenInFileManager(full);
ev.Handled = true;
};
menu.Items.Add(explore);
}
menu.Items.Add(new MenuItem() { Header = "-" });
menu.Items.Add(patch);
var copyPath = new MenuItem();
copyPath.Header = App.Text("CopyPath");
copyPath.Icon = this.CreateMenuIcon("Icons.Copy");
copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C";
copyPath.Click += async (_, ev) =>
{
await this.CopyTextAsync(change.Path);
ev.Handled = true;
};
var copyFullPath = new MenuItem();
copyFullPath.Header = App.Text("CopyFullPath");
copyFullPath.Icon = this.CreateMenuIcon("Icons.Copy");
copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C";
copyFullPath.Click += async (_, ev) =>
{
await this.CopyTextAsync(vm.GetAbsPath(change.Path));
ev.Handled = true;
};
menu.Items.Add(new MenuItem() { Header = "-" });
menu.Items.Add(copyPath);
menu.Items.Add(copyFullPath);
}
else
{
menu.Items.Add(patch);
var copyPath = new MenuItem();
copyPath.Header = App.Text("CopyPath");
copyPath.Icon = this.CreateMenuIcon("Icons.Copy");
copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C";
copyPath.Click += async (_, ev) =>
{
var builder = new StringBuilder();
foreach (var c in selected)
builder.AppendLine(c.Path);
await this.CopyTextAsync(builder.ToString());
ev.Handled = true;
};
var copyFullPath = new MenuItem();
copyFullPath.Header = App.Text("CopyFullPath");
copyFullPath.Icon = this.CreateMenuIcon("Icons.Copy");
copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C";
copyFullPath.Click += async (_, ev) =>
{
var builder = new StringBuilder();
foreach (var c in selected)
builder.AppendLine(vm.GetAbsPath(c.Path));
await this.CopyTextAsync(builder.ToString());
ev.Handled = true;
};
menu.Items.Add(new MenuItem() { Header = "-" });
menu.Items.Add(copyPath);
menu.Items.Add(copyFullPath);
}
menu.Open(view);
}
e.Handled = true;
}
private async void OnChangeCollectionViewKeyDown(object sender, KeyEventArgs e)
{
if (DataContext is not ViewModels.SubmoduleRevisionCompare vm)
return;
if (sender is not ChangeCollectionView { SelectedChanges: { Count: > 0 } selectedChanges })
return;
var cmdKey = OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control;
if (e.Key == Key.C && e.KeyModifiers.HasFlag(cmdKey))
{
var builder = new StringBuilder();
var copyAbsPath = e.KeyModifiers.HasFlag(KeyModifiers.Shift);
if (selectedChanges.Count == 1)
{
builder.Append(copyAbsPath ? vm.GetAbsPath(selectedChanges[0].Path) : selectedChanges[0].Path);
}
else
{
foreach (var c in selectedChanges)
builder.AppendLine(copyAbsPath ? vm.GetAbsPath(c.Path) : c.Path);
}
await this.CopyTextAsync(builder.ToString());
e.Handled = true;
}
else if (e.Key == Key.F && e.KeyModifiers == cmdKey)
{
ChangeSearchBox.Focus();
e.Handled = true;
}
}
}
}

View File

@@ -257,7 +257,7 @@ namespace SourceGit.Views
histories.Icon = this.CreateMenuIcon("Icons.Histories");
histories.Click += (_, ev) =>
{
App.ShowWindow(new ViewModels.FileHistories(repo.FullPath, submodule.Path));
this.ShowWindow(new ViewModels.FileHistories(repo.FullPath, submodule.Path));
ev.Handled = true;
};

View File

@@ -271,7 +271,7 @@ namespace SourceGit.Views
compareWithHead.Icon = this.CreateMenuIcon("Icons.Compare");
compareWithHead.Click += (_, _) =>
{
App.ShowWindow(new ViewModels.Compare(repo, tag, repo.CurrentBranch));
this.ShowWindow(new ViewModels.Compare(repo, tag, repo.CurrentBranch));
};
var compareWith = new MenuItem();
@@ -387,7 +387,7 @@ namespace SourceGit.Views
if (based.CreatorDate > to.CreatorDate)
(based, to) = (to, based);
App.ShowWindow(new ViewModels.Compare(repo, based, to));
this.ShowWindow(new ViewModels.Compare(repo, based, to));
ev.Handled = true;
};
menu.Items.Add(compare);

View File

@@ -98,6 +98,23 @@ namespace SourceGit.Views
}
}
protected override Size MeasureOverride(Size availableSize)
{
if (DataContext is not ViewModels.TextDiffContext ctx)
return new Size(0, 0);
var typeface = new Typeface(TextArea.FontFamily);
var test = new FormattedText(
$"{ctx.Data.MaxLineNumber}",
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
typeface,
TextArea.FontSize,
Brushes.White);
return new Size(test.Width, 0);
}
private readonly bool _usePresenter;
private readonly bool _isOld;
}
@@ -578,29 +595,6 @@ namespace SourceGit.Views
protected override void OnDataContextChanged(EventArgs e)
{
base.OnDataContextChanged(e);
if (DataContext is ViewModels.TextDiffContext ctx)
{
var typeface = new Typeface(FontFamily);
var test = new FormattedText(
$"{ctx.Data.MaxLineNumber}",
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
typeface,
FontSize,
Brushes.White);
var width = test.WidthIncludingTrailingWhitespace;
foreach (var margin in TextArea.LeftMargins)
{
if (margin is LineNumberMargin lineNumberMargin)
margin.Width = width;
}
var dock = TextArea.FindDescendantOfType<DockPanel>();
dock?.InvalidateArrange();
}
AutoScrollToFirstChange();
}
@@ -721,6 +715,9 @@ namespace SourceGit.Views
if (DataContext is not ViewModels.TextDiffContext ctx)
return;
foreach (var margin in TextArea.LeftMargins)
margin.InvalidateMeasure();
if (ctx.IsSideBySide() && !IsOld)
return;
@@ -1495,17 +1492,17 @@ namespace SourceGit.Views
var tmpFile = Path.GetTempFileName();
if (change.WorkTree == Models.ChangeState.Untracked)
{
diff.GenerateNewPatchFromSelection(change, null, selection, false, tmpFile);
diff.GenerateNewPatchFromSelection(change.Path, null, selection, false, tmpFile);
}
else if (chunk.Combined)
{
var treeGuid = await new Commands.QueryStagedFileBlobGuid(repo.FullPath, change.Path).GetResultAsync();
diff.GeneratePatchFromSelection(change, treeGuid, selection, false, tmpFile);
diff.GeneratePatchFromSelection(change.Path, treeGuid, selection, false, tmpFile);
}
else
{
var treeGuid = await new Commands.QueryStagedFileBlobGuid(repo.FullPath, change.Path).GetResultAsync();
diff.GeneratePatchFromSelectionSingleSide(change, treeGuid, selection, false, chunk.IsOldSide, tmpFile);
diff.GeneratePatchFromSelectionSingleSide(change.Path, treeGuid, selection, false, chunk.IsOldSide, tmpFile);
}
await new Commands.Apply(repo.FullPath, tmpFile, true, "nowarn", "--cache --index").ExecAsync();
@@ -1533,11 +1530,11 @@ namespace SourceGit.Views
var treeGuid = await new Commands.QueryStagedFileBlobGuid(repo.FullPath, change.Path).GetResultAsync();
var tmpFile = Path.GetTempFileName();
if (change.Index == Models.ChangeState.Added)
diff.GenerateNewPatchFromSelection(change, treeGuid, selection, true, tmpFile);
diff.GenerateNewPatchFromSelection(change.Path, treeGuid, selection, true, tmpFile);
else if (chunk.Combined)
diff.GeneratePatchFromSelection(change, treeGuid, selection, true, tmpFile);
diff.GeneratePatchFromSelection(change.Path, treeGuid, selection, true, tmpFile);
else
diff.GeneratePatchFromSelectionSingleSide(change, treeGuid, selection, true, chunk.IsOldSide, tmpFile);
diff.GeneratePatchFromSelectionSingleSide(change.Path, treeGuid, selection, true, chunk.IsOldSide, tmpFile);
await new Commands.Apply(repo.FullPath, tmpFile, true, "nowarn", "--cache --index --reverse").ExecAsync();
File.Delete(tmpFile);
@@ -1564,17 +1561,17 @@ namespace SourceGit.Views
var tmpFile = Path.GetTempFileName();
if (change.WorkTree == Models.ChangeState.Untracked)
{
diff.GenerateNewPatchFromSelection(change, null, selection, true, tmpFile);
diff.GenerateNewPatchFromSelection(change.Path, null, selection, true, tmpFile);
}
else if (chunk.Combined)
{
var treeGuid = await new Commands.QueryStagedFileBlobGuid(repo.FullPath, change.Path).GetResultAsync();
diff.GeneratePatchFromSelection(change, treeGuid, selection, true, tmpFile);
diff.GeneratePatchFromSelection(change.Path, treeGuid, selection, true, tmpFile);
}
else
{
var treeGuid = await new Commands.QueryStagedFileBlobGuid(repo.FullPath, change.Path).GetResultAsync();
diff.GeneratePatchFromSelectionSingleSide(change, treeGuid, selection, true, chunk.IsOldSide, tmpFile);
diff.GeneratePatchFromSelectionSingleSide(change.Path, treeGuid, selection, true, chunk.IsOldSide, tmpFile);
}
await new Commands.Apply(repo.FullPath, tmpFile, true, "nowarn", "--reverse").ExecAsync();

View File

@@ -19,7 +19,14 @@
<Path Width="16" Height="16" Data="{StaticResource Icons.Clone}" Margin="0,4,0,0"/>
</Button>
<Button Classes="icon_button" Width="32" Click="OpenLocalRepository" ToolTip.Tip="{DynamicResource Text.Welcome.OpenOrInit}">
<Button Classes="icon_button" Width="32" Command="{Binding OpenLocalRepository}">
<ToolTip.Tip>
<TextBlock>
<Run Text="{DynamicResource Text.Welcome.OpenOrInit}"/>
<Run Text=" "/>
<Run Text="{OnPlatform Ctrl+Shift+O, macOS=⌘+⇧+O}" FontSize="11" Foreground="{DynamicResource MenuFlyoutItemKeyboardAcceleratorTextForeground}"/>
</TextBlock>
</ToolTip.Tip>
<Path Width="14" Height="14" Data="{StaticResource Icons.Folder.Open}" Margin="0,2,0,0"/>
</Button>

View File

@@ -1,9 +1,4 @@
using System;
using System.IO;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Platform.Storage;
namespace SourceGit.Views
{
@@ -13,56 +8,5 @@ namespace SourceGit.Views
{
InitializeComponent();
}
private async void OpenLocalRepository(object _1, RoutedEventArgs e)
{
var activePage = App.GetLauncher().ActivePage;
if (activePage == null || !activePage.CanCreatePopup())
return;
var topLevel = TopLevel.GetTopLevel(this);
if (topLevel == null)
return;
var preference = ViewModels.Preferences.Instance;
var workspace = preference.GetActiveWorkspace();
var initDir = workspace.DefaultCloneDir;
if (string.IsNullOrEmpty(initDir) || !Directory.Exists(initDir))
initDir = preference.GitDefaultCloneDir;
var options = new FolderPickerOpenOptions() { AllowMultiple = false };
if (Directory.Exists(initDir))
{
var folder = await topLevel.StorageProvider.TryGetFolderFromPathAsync(initDir);
options.SuggestedStartLocation = folder;
}
try
{
var selected = await topLevel.StorageProvider.OpenFolderPickerAsync(options);
if (selected.Count == 1)
{
var folder = selected[0];
var folderPath = folder is { Path: { IsAbsoluteUri: true } path } ? path.LocalPath : folder?.Path.ToString();
var repoPath = await ViewModels.Welcome.Instance.GetRepositoryRootAsync(folderPath);
if (!string.IsNullOrEmpty(repoPath))
{
await ViewModels.Welcome.Instance.AddRepositoryAsync(repoPath, null, false, true);
ViewModels.Welcome.Instance.Refresh();
}
else if (Directory.Exists(folderPath))
{
var test = await new Commands.QueryRepositoryRootPath(folderPath).GetResultAsync();
ViewModels.Welcome.Instance.InitRepository(folderPath, null, test.StdErr);
}
}
}
catch (Exception exception)
{
Models.Notification.Send(null, $"Failed to open repository: {exception.Message}", true);
}
e.Handled = true;
}
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using Avalonia.Controls;
@@ -33,7 +34,7 @@ namespace SourceGit.Views
{
var repoView = this.FindAncestorOfType<Repository>();
if (repoView is { DataContext: ViewModels.Repository repo })
await App.ShowDialog(new ViewModels.AssumeUnchangedManager(repo));
await this.ShowDialogAsync(new ViewModels.AssumeUnchangedManager(repo));
e.Handled = true;
}
@@ -359,7 +360,7 @@ namespace SourceGit.Views
mergeBuiltin.Click += async (_, e) =>
{
var head = await new Commands.QuerySingleCommit(repo.FullPath, "HEAD").GetResultAsync();
await App.ShowDialog(new ViewModels.MergeConflictEditor(repo, head, change.Path));
await this.ShowDialogAsync(new ViewModels.MergeConflictEditor(repo, head, change.Path));
e.Handled = true;
};
@@ -540,7 +541,7 @@ namespace SourceGit.Views
hasExtra = true;
}
if (repo.IsLFSEnabled())
if (File.Exists(path) && repo.IsLFSEnabled())
{
var lfs = new MenuItem();
lfs.Header = App.Text("GitLFS");
@@ -647,7 +648,7 @@ namespace SourceGit.Views
history.Icon = this.CreateMenuIcon("Icons.Histories");
history.Click += (_, e) =>
{
App.ShowWindow(new ViewModels.DirHistories(repo, selectedSingleFolder));
this.ShowWindow(new ViewModels.DirHistories(repo, selectedSingleFolder));
e.Handled = true;
};
@@ -661,7 +662,7 @@ namespace SourceGit.Views
history.Icon = this.CreateMenuIcon("Icons.Histories");
history.Click += (_, e) =>
{
App.ShowWindow(new ViewModels.FileHistories(repo.FullPath, change.Path));
this.ShowWindow(new ViewModels.FileHistories(repo.FullPath, change.Path));
e.Handled = true;
};
@@ -671,7 +672,7 @@ namespace SourceGit.Views
blame.Click += async (_, ev) =>
{
var commit = await new Commands.QuerySingleCommit(repo.FullPath, "HEAD").GetResultAsync();
App.ShowWindow(new ViewModels.Blame(repo.FullPath, change.Path, commit));
this.ShowWindow(new ViewModels.Blame(repo.FullPath, change.Path, commit));
ev.Handled = true;
};
@@ -871,7 +872,7 @@ namespace SourceGit.Views
history.Icon = this.CreateMenuIcon("Icons.Histories");
history.Click += (_, e) =>
{
App.ShowWindow(new ViewModels.DirHistories(repo, selectedSingleFolder));
this.ShowWindow(new ViewModels.DirHistories(repo, selectedSingleFolder));
e.Handled = true;
};
@@ -927,9 +928,9 @@ namespace SourceGit.Views
if (services.Count == 1)
{
ai.Click += async (_, e) =>
ai.Click += (_, e) =>
{
await App.ShowDialog(new ViewModels.AIAssistant(repo, services[0], selectedStaged));
DoOpenAIAssistant(repo, services[0], selectedStaged);
e.Handled = true;
};
}
@@ -941,9 +942,9 @@ namespace SourceGit.Views
var item = new MenuItem();
item.Header = service.Name;
item.Click += async (_, e) =>
item.Click += (_, e) =>
{
await App.ShowDialog(new ViewModels.AIAssistant(repo, dup, selectedStaged));
DoOpenAIAssistant(repo, dup, selectedStaged);
e.Handled = true;
};
@@ -1037,7 +1038,7 @@ namespace SourceGit.Views
menu.Items.Add(patch);
menu.Items.Add(new MenuItem() { Header = "-" });
if (repo.IsLFSEnabled())
if (File.Exists(path) && repo.IsLFSEnabled())
{
var lfs = new MenuItem();
lfs.Header = App.Text("GitLFS");
@@ -1118,7 +1119,7 @@ namespace SourceGit.Views
history.Icon = this.CreateMenuIcon("Icons.Histories");
history.Click += (_, e) =>
{
App.ShowWindow(new ViewModels.DirHistories(repo, selectedSingleFolder));
this.ShowWindow(new ViewModels.DirHistories(repo, selectedSingleFolder));
e.Handled = true;
};
@@ -1132,7 +1133,7 @@ namespace SourceGit.Views
history.Icon = this.CreateMenuIcon("Icons.Histories");
history.Click += (_, e) =>
{
App.ShowWindow(new ViewModels.FileHistories(repo.FullPath, change.Path));
this.ShowWindow(new ViewModels.FileHistories(repo.FullPath, change.Path));
e.Handled = true;
};
@@ -1142,7 +1143,7 @@ namespace SourceGit.Views
blame.Click += async (_, e) =>
{
var commit = await new Commands.QuerySingleCommit(repo.FullPath, "HEAD").GetResultAsync();
App.ShowWindow(new ViewModels.Blame(repo.FullPath, change.Path, commit));
this.ShowWindow(new ViewModels.Blame(repo.FullPath, change.Path, commit));
e.Handled = true;
};
@@ -1262,7 +1263,7 @@ namespace SourceGit.Views
history.Icon = this.CreateMenuIcon("Icons.Histories");
history.Click += (_, e) =>
{
App.ShowWindow(new ViewModels.DirHistories(repo, selectedSingleFolder));
this.ShowWindow(new ViewModels.DirHistories(repo, selectedSingleFolder));
e.Handled = true;
};
@@ -1369,5 +1370,16 @@ namespace SourceGit.Views
menu.Items.Add(custom);
menu.Items.Add(new MenuItem() { Header = "-" });
}
private void DoOpenAIAssistant(ViewModels.Repository repo, AI.Service serivce, List<Models.Change> changes)
{
var owner = TopLevel.GetTopLevel(this) as Window;
if (owner == null)
return;
var assistant = new ViewModels.AIAssistant(repo, serivce, changes);
var view = new AIAssistant() { DataContext = assistant };
view.Show(owner);
}
}
}