refactor: terminal/shell integration (#471)

This commit is contained in:
leo 2024-09-14 12:09:50 +08:00
parent 817f8919fd
commit fb0120d338
No known key found for this signature in database
27 changed files with 445 additions and 427 deletions

View file

@ -1,10 +0,0 @@
namespace SourceGit.Models
{
public enum Shell
{
Default = 0,
PowerShell,
CommandPrompt,
DefaultShellOfWindowsTerminal,
}
}

View file

@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
namespace SourceGit.Models
{
public class ShellOrTerminal
{
public string Type { get; set; }
public string Name { get; set; }
public string Exec { get; set; }
public Bitmap Icon
{
get
{
var icon = AssetLoader.Open(new Uri($"avares://SourceGit/Resources/Images/ShellIcons/{Type}.png", UriKind.RelativeOrAbsolute));
return new Bitmap(icon);
}
}
public static readonly List<ShellOrTerminal> Supported;
static ShellOrTerminal()
{
if (OperatingSystem.IsWindows())
{
Supported = new List<ShellOrTerminal>()
{
new ShellOrTerminal("git-bash", "Git Bash", "bash.exe"),
new ShellOrTerminal("pwsh", "PowerShell", "pwsh.exe|powershell.exe"),
new ShellOrTerminal("cmd", "Command Prompt", "cmd.exe"),
new ShellOrTerminal("wt", "Windows Terminal", "wt.exe")
};
}
else if (OperatingSystem.IsMacOS())
{
Supported = new List<ShellOrTerminal>()
{
new ShellOrTerminal("mac-terminal", "Terminal", ""),
new ShellOrTerminal("iterm2", "iTerm", ""),
};
}
else
{
Supported = new List<ShellOrTerminal>()
{
new ShellOrTerminal("gnome-terminal", "Gnome Terminal", "gnome-terminal"),
new ShellOrTerminal("konsole", "Konsole", "konsole"),
new ShellOrTerminal("xfce4-terminal", "Xfce4 Terminal", "xfce4-terminal"),
new ShellOrTerminal("lxterminal", "LXTerminal", "lxterminal"),
new ShellOrTerminal("deepin-terminal", "Deepin Terminal", "deepin-terminal"),
new ShellOrTerminal("mate-terminal", "MATE Terminal", "mate-terminal"),
new ShellOrTerminal("foot", "Foot", "foot"),
};
}
}
public ShellOrTerminal(string type, string name, string exec)
{
Type = type;
Name = name;
Exec = exec;
}
}
}

View file

@ -11,29 +11,6 @@ namespace SourceGit.Native
[SupportedOSPlatform("linux")]
internal class Linux : OS.IBackend
{
class Terminal
{
public string FilePath { get; set; }
public string OpenArgFormat { get; set; }
public Terminal(string exec, string fmt)
{
FilePath = exec;
OpenArgFormat = fmt;
}
public void Open(string dir)
{
Process.Start(FilePath, string.Format(OpenArgFormat, dir));
}
}
public Linux()
{
_xdgOpenPath = FindExecutable("xdg-open");
_terminal = FindTerminal();
}
public void SetupApp(AppBuilder builder)
{
builder.With(new X11PlatformOptions()
@ -47,6 +24,20 @@ namespace SourceGit.Native
return FindExecutable("git");
}
public string FindTerminal(Models.ShellOrTerminal shell)
{
var pathVariable = Environment.GetEnvironmentVariable("PATH") ?? string.Empty;
var pathes = pathVariable.Split(Path.PathSeparator, StringSplitOptions.RemoveEmptyEntries);
foreach (var path in pathes)
{
var test = Path.Combine(path, shell.Exec);
if (File.Exists(test))
return test;
}
return string.Empty;
}
public List<Models.ExternalTool> FindExternalTools()
{
var finder = new Models.ExternalToolsFinder();
@ -61,50 +52,40 @@ namespace SourceGit.Native
public void OpenBrowser(string url)
{
if (string.IsNullOrEmpty(_xdgOpenPath))
App.RaiseException("", $"Can NOT find `xdg-open` command!!!");
else
Process.Start(_xdgOpenPath, $"\"{url}\"");
Process.Start("xdg-open", $"\"{url}\"");
}
public void OpenInFileManager(string path, bool select)
{
if (string.IsNullOrEmpty(_xdgOpenPath))
{
App.RaiseException("", $"Can NOT find `xdg-open` command!!!");
return;
}
if (Directory.Exists(path))
{
Process.Start(_xdgOpenPath, $"\"{path}\"");
Process.Start("xdg-open", $"\"{path}\"");
}
else
{
var dir = Path.GetDirectoryName(path);
if (Directory.Exists(dir))
Process.Start(_xdgOpenPath, $"\"{dir}\"");
Process.Start("xdg-open", $"\"{dir}\"");
}
}
public void OpenTerminal(string workdir)
{
var dir = string.IsNullOrEmpty(workdir) ? "~" : workdir;
if (_terminal == null)
App.RaiseException(dir, $"Only supports gnome-terminal/konsole/xfce4-terminal/lxterminal/deepin-terminal/mate-terminal/foot!");
else
_terminal.Open(dir);
if (string.IsNullOrEmpty(OS.ShellOrTerminal) || !File.Exists(OS.ShellOrTerminal))
{
App.RaiseException(workdir, $"Can not found terminal! Please confirm that the correct shell/terminal has been configured.");
return;
}
var startInfo = new ProcessStartInfo();
startInfo.WorkingDirectory = string.IsNullOrEmpty(workdir) ? "~" : workdir;
startInfo.FileName = OS.ShellOrTerminal;
Process.Start(startInfo);
}
public void OpenWithDefaultEditor(string file)
{
if (string.IsNullOrEmpty(_xdgOpenPath))
{
App.RaiseException("", $"Can NOT find `xdg-open` command!!!");
return;
}
var proc = Process.Start(_xdgOpenPath, $"\"{file}\"");
var proc = Process.Start("xdg-open", $"\"{file}\"");
if (proc != null)
{
proc.WaitForExit();
@ -130,51 +111,10 @@ namespace SourceGit.Native
return string.Empty;
}
private Terminal FindTerminal()
{
var pathVariable = Environment.GetEnvironmentVariable("PATH") ?? string.Empty;
var pathes = pathVariable.Split(Path.PathSeparator, StringSplitOptions.RemoveEmptyEntries);
foreach (var path in pathes)
{
var test = Path.Combine(path, "gnome-terminal");
if (File.Exists(test))
return new Terminal(test, "--working-directory=\"{0}\"");
test = Path.Combine(path, "konsole");
if (File.Exists(test))
return new Terminal(test, "--workdir \"{0}\"");
test = Path.Combine(path, "xfce4-terminal");
if (File.Exists(test))
return new Terminal(test, "--working-directory=\"{0}\"");
test = Path.Combine(path, "lxterminal");
if (File.Exists(test))
return new Terminal(test, "--working-directory=\"{0}\"");
test = Path.Combine(path, "deepin-terminal");
if (File.Exists(test))
return new Terminal(test, "--work-directory \"{0}\"");
test = Path.Combine(path, "mate-terminal");
if (File.Exists(test))
return new Terminal(test, "--working-directory=\"{0}\"");
test = Path.Combine(path, "foot");
if (File.Exists(test))
return new Terminal(test, "--working-directory=\"{0}\"");
}
return null;
}
private string FindJetBrainsFleet()
{
var path = $"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}/JetBrains/Toolbox/apps/fleet/bin/Fleet";
return File.Exists(path) ? path : FindExecutable("fleet");
}
private string _xdgOpenPath = null;
private Terminal _terminal = null;
}
}

View file

@ -24,6 +24,19 @@ namespace SourceGit.Native
return File.Exists("/usr/bin/git") ? "/usr/bin/git" : string.Empty;
}
public string FindTerminal(Models.ShellOrTerminal shell)
{
switch (shell.Type)
{
case "mac-terminal":
return "Terminal";
case "iterm2":
return "iTerm";
}
return "InvalidTerminal";
}
public List<Models.ExternalTool> FindExternalTools()
{
var finder = new Models.ExternalToolsFinder();
@ -53,12 +66,7 @@ namespace SourceGit.Native
{
var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
var dir = string.IsNullOrEmpty(workdir) ? home : workdir;
var terminal = "Terminal";
if (Directory.Exists("/Applications/iTerm.app"))
terminal = "iTerm";
Process.Start("open", $"-a {terminal} \"{dir}\"");
Process.Start("open", $"-a {OS.ShellOrTerminal} \"{dir}\"");
}
public void OpenWithDefaultEditor(string file)

View file

@ -13,6 +13,7 @@ namespace SourceGit.Native
void SetupApp(AppBuilder builder);
string FindGitExecutable();
string FindTerminal(Models.ShellOrTerminal shell);
List<Models.ExternalTool> FindExternalTools();
void OpenTerminal(string workdir);
@ -23,6 +24,7 @@ namespace SourceGit.Native
public static string DataDir { get; private set; } = string.Empty;
public static string GitExecutable { get; set; } = string.Empty;
public static string ShellOrTerminal { get; set; } = string.Empty;
public static List<Models.ExternalTool> ExternalTools { get; set; } = [];
static OS()
@ -45,29 +47,6 @@ namespace SourceGit.Native
}
}
public static Models.Shell GetShell()
{
if (OperatingSystem.IsWindows())
return (_backend as Windows)!.Shell;
return Models.Shell.Default;
}
public static bool SetShell(Models.Shell shell)
{
if (OperatingSystem.IsWindows())
{
var windows = (_backend as Windows)!;
if (windows.Shell != shell)
{
windows.Shell = shell;
return true;
}
}
return false;
}
public static void SetupApp(AppBuilder builder)
{
_backend.SetupApp(builder);
@ -95,6 +74,14 @@ namespace SourceGit.Native
return _backend.FindGitExecutable();
}
public static void SetShellOrTerminal(Models.ShellOrTerminal shellOrTerminal)
{
if (shellOrTerminal == null)
ShellOrTerminal = string.Empty;
else
ShellOrTerminal = _backend.FindTerminal(shellOrTerminal);
}
public static void OpenInFileManager(string path, bool select = false)
{
_backend.OpenInFileManager(path, select);
@ -107,7 +94,10 @@ namespace SourceGit.Native
public static void OpenTerminal(string workdir)
{
_backend.OpenTerminal(workdir);
if (string.IsNullOrEmpty(ShellOrTerminal))
App.RaiseException(workdir, $"Can not found terminal! Please confirm that the correct shell/terminal has been configured.");
else
_backend.OpenTerminal(workdir);
}
public static void OpenWithDefaultEditor(string file)

View file

@ -53,12 +53,6 @@ namespace SourceGit.Native
[DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = false)]
private static extern int SHOpenFolderAndSelectItems(IntPtr pidlFolder, int cild, IntPtr apidl, int dwFlags);
public Models.Shell Shell
{
get;
set;
} = Models.Shell.Default;
public void SetupApp(AppBuilder builder)
{
// Fix drop shadow issue on Windows 10
@ -98,6 +92,51 @@ namespace SourceGit.Native
return null;
}
public string FindTerminal(Models.ShellOrTerminal shell)
{
switch (shell.Type)
{
case "git-bash":
if (string.IsNullOrEmpty(OS.GitExecutable))
break;
var binDir = Path.GetDirectoryName(OS.GitExecutable)!;
var bash = Path.Combine(binDir, "bash.exe");
if (!File.Exists(bash))
break;
return bash;
case "pwsh":
var localMachine = Microsoft.Win32.RegistryKey.OpenBaseKey(
Microsoft.Win32.RegistryHive.LocalMachine,
Microsoft.Win32.RegistryView.Registry64);
var pwsh = localMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\pwsh.exe");
if (pwsh != null)
{
var path = pwsh.GetValue(null) as string;
if (File.Exists(path))
return path;
}
var pwshFinder = new StringBuilder("powershell.exe", 512);
if (PathFindOnPath(pwshFinder, null))
return pwshFinder.ToString();
break;
case "cmd":
return "C:\\Windows\\System32\\cmd.exe";
case "wt":
var wtFinder = new StringBuilder("wt.exe", 512);
if (PathFindOnPath(wtFinder, null))
return wtFinder.ToString();
break;
}
return string.Empty;
}
public List<Models.ExternalTool> FindExternalTools()
{
var finder = new Models.ExternalToolsFinder();
@ -119,56 +158,15 @@ namespace SourceGit.Native
public void OpenTerminal(string workdir)
{
if (string.IsNullOrEmpty(workdir) || !Path.Exists(workdir))
if (string.IsNullOrEmpty(OS.ShellOrTerminal) || !File.Exists(OS.ShellOrTerminal))
{
workdir = ".";
App.RaiseException(workdir, $"Can not found terminal! Please confirm that the correct shell/terminal has been configured.");
return;
}
var startInfo = new ProcessStartInfo();
startInfo.WorkingDirectory = workdir;
switch (Shell)
{
case Models.Shell.Default:
if (string.IsNullOrEmpty(OS.GitExecutable))
{
App.RaiseException(workdir, $"Can NOT found bash.exe");
return;
}
var binDir = Path.GetDirectoryName(OS.GitExecutable)!;
var bash = Path.Combine(binDir, "bash.exe");
if (!File.Exists(bash))
{
App.RaiseException(workdir, $"Can NOT found bash.exe under '{binDir}'");
return;
}
startInfo.FileName = bash;
break;
case Models.Shell.PowerShell:
startInfo.FileName = ChoosePowerShell();
startInfo.Arguments = startInfo.FileName.EndsWith("pwsh.exe") ? $"-WorkingDirectory \"{workdir}\" -Nologo" : "-Nologo";
break;
case Models.Shell.CommandPrompt:
startInfo.FileName = "cmd";
break;
case Models.Shell.DefaultShellOfWindowsTerminal:
var wt = FindWindowsTerminalApp();
if (!File.Exists(wt))
{
App.RaiseException(workdir, $"Can NOT found wt.exe on your system!");
return;
}
startInfo.FileName = wt;
startInfo.Arguments = $"-d \"{workdir}\"";
break;
default:
App.RaiseException(workdir, $"Bad shell configuration!");
return;
}
startInfo.FileName = OS.ShellOrTerminal;
Process.Start(startInfo);
}
@ -218,52 +216,6 @@ namespace SourceGit.Native
DwmExtendFrameIntoClientArea(platformHandle.Handle, ref margins);
}
// There are two versions of PowerShell : pwsh.exe (preferred) and powershell.exe (system default)
private string ChoosePowerShell()
{
if (!string.IsNullOrEmpty(_powershellPath))
return _powershellPath;
var localMachine = Microsoft.Win32.RegistryKey.OpenBaseKey(
Microsoft.Win32.RegistryHive.LocalMachine,
Microsoft.Win32.RegistryView.Registry64);
var pwsh = localMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\pwsh.exe");
if (pwsh != null)
{
var path = pwsh.GetValue(null) as string;
if (File.Exists(path))
{
_powershellPath = path;
return _powershellPath;
}
}
var finder = new StringBuilder("powershell.exe", 512);
if (PathFindOnPath(finder, null))
{
_powershellPath = finder.ToString();
return _powershellPath;
}
return string.Empty;
}
private string FindWindowsTerminalApp()
{
if (!string.IsNullOrEmpty(_wtPath))
return _wtPath;
var finder = new StringBuilder("wt.exe", 512);
if (PathFindOnPath(finder, null))
{
_wtPath = finder.ToString();
return _wtPath;
}
return string.Empty;
}
#region EXTERNAL_EDITOR_FINDER
private string FindVSCode()
{
@ -385,8 +337,5 @@ namespace SourceGit.Native
ILFree(pidl);
}
}
private string _powershellPath = string.Empty;
private string _wtPath = string.Empty;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -394,6 +394,10 @@
<x:String x:Key="Text.Preference.Appearance.ThemeOverrides" xml:space="preserve">Design-Anpassungen</x:String>
<x:String x:Key="Text.Preference.Appearance.UseFixedTabWidth" xml:space="preserve">Fixe Tab-Breite in Titelleiste</x:String>
<x:String x:Key="Text.Preference.Appearance.UseNativeWindowFrame" xml:space="preserve">Verwende nativen Fensterrahmen</x:String>
<x:String x:Key="Text.Preference.DiffMerge" xml:space="preserve">DIFF/MERGE TOOL</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Path" xml:space="preserve">Installationspfad</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Path.Placeholder" xml:space="preserve">Installationspfad zum Diff/Merge Tool</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Type" xml:space="preserve">Tool</x:String>
<x:String x:Key="Text.Preference.General" xml:space="preserve">ALLGEMEIN</x:String>
<x:String x:Key="Text.Preference.General.Check4UpdatesOnStartup" xml:space="preserve">Beim Starten nach Updates suchen</x:String>
<x:String x:Key="Text.Preference.General.Locale" xml:space="preserve">Sprache</x:String>
@ -408,7 +412,6 @@
<x:String x:Key="Text.Preference.Git.Email" xml:space="preserve">Benutzer Email</x:String>
<x:String x:Key="Text.Preference.Git.Email.Placeholder" xml:space="preserve">Globale Git Benutzer Email</x:String>
<x:String x:Key="Text.Preference.Git.Path" xml:space="preserve">Installationspfad</x:String>
<x:String x:Key="Text.Preference.Git.Shell" xml:space="preserve">Shell</x:String>
<x:String x:Key="Text.Preference.Git.User" xml:space="preserve">Benutzername</x:String>
<x:String x:Key="Text.Preference.Git.User.Placeholder" xml:space="preserve">Globaler Git Benutzername</x:String>
<x:String x:Key="Text.Preference.Git.Version" xml:space="preserve">Git Version</x:String>
@ -421,10 +424,6 @@
<x:String x:Key="Text.Preference.GPG.Path.Placeholder" xml:space="preserve">Installationspfad zum GPG Programm</x:String>
<x:String x:Key="Text.Preference.GPG.UserKey" xml:space="preserve">Benutzer Signierungsschlüssel</x:String>
<x:String x:Key="Text.Preference.GPG.UserKey.Placeholder" xml:space="preserve">GPG Benutzer Signierungsschlüssel</x:String>
<x:String x:Key="Text.Preference.DiffMerge" xml:space="preserve">DIFF/MERGE TOOL</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Path" xml:space="preserve">Installationspfad</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Path.Placeholder" xml:space="preserve">Installationspfad zum Diff/Merge Tool</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Type" xml:space="preserve">Tool</x:String>
<x:String x:Key="Text.PruneRemote" xml:space="preserve">Remote löschen</x:String>
<x:String x:Key="Text.PruneRemote.Target" xml:space="preserve">Ziel:</x:String>
<x:String x:Key="Text.PruneWorktrees" xml:space="preserve">Worktrees löschen</x:String>

View file

@ -399,6 +399,10 @@
<x:String x:Key="Text.Preference.Appearance.ThemeOverrides" xml:space="preserve">Theme Overrides</x:String>
<x:String x:Key="Text.Preference.Appearance.UseFixedTabWidth" xml:space="preserve">Use fixed tab width in titlebar</x:String>
<x:String x:Key="Text.Preference.Appearance.UseNativeWindowFrame" xml:space="preserve">Use native window frame</x:String>
<x:String x:Key="Text.Preference.DiffMerge" xml:space="preserve">DIFF/MERGE TOOL</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Path" xml:space="preserve">Install Path</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Path.Placeholder" xml:space="preserve">Input path for diff/merge tool</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Type" xml:space="preserve">Tool</x:String>
<x:String x:Key="Text.Preference.General" xml:space="preserve">GENERAL</x:String>
<x:String x:Key="Text.Preference.General.Check4UpdatesOnStartup" xml:space="preserve">Check for updates on startup</x:String>
<x:String x:Key="Text.Preference.General.Locale" xml:space="preserve">Language</x:String>
@ -413,7 +417,6 @@
<x:String x:Key="Text.Preference.Git.Email" xml:space="preserve">User Email</x:String>
<x:String x:Key="Text.Preference.Git.Email.Placeholder" xml:space="preserve">Global git user email</x:String>
<x:String x:Key="Text.Preference.Git.Path" xml:space="preserve">Install Path</x:String>
<x:String x:Key="Text.Preference.Git.Shell" xml:space="preserve">Shell</x:String>
<x:String x:Key="Text.Preference.Git.User" xml:space="preserve">User Name</x:String>
<x:String x:Key="Text.Preference.Git.User.Placeholder" xml:space="preserve">Global git user name</x:String>
<x:String x:Key="Text.Preference.Git.Version" xml:space="preserve">Git version</x:String>
@ -426,10 +429,10 @@
<x:String x:Key="Text.Preference.GPG.Path.Placeholder" xml:space="preserve">Input path for installed gpg program</x:String>
<x:String x:Key="Text.Preference.GPG.UserKey" xml:space="preserve">User Signing Key</x:String>
<x:String x:Key="Text.Preference.GPG.UserKey.Placeholder" xml:space="preserve">User's gpg signing key</x:String>
<x:String x:Key="Text.Preference.DiffMerge" xml:space="preserve">DIFF/MERGE TOOL</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Path" xml:space="preserve">Install Path</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Path.Placeholder" xml:space="preserve">Input path for diff/merge tool</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Type" xml:space="preserve">Tool</x:String>
<x:String x:Key="Text.Preference.Integration" xml:space="preserve">INTEGRATION</x:String>
<x:String x:Key="Text.Preference.Shell" xml:space="preserve">SHELL/TERMINAL</x:String>
<x:String x:Key="Text.Preference.Shell.Type" xml:space="preserve">Shell/Terminal</x:String>
<x:String x:Key="Text.Preference.Shell.Path" xml:space="preserve">Path</x:String>
<x:String x:Key="Text.PruneRemote" xml:space="preserve">Prune Remote</x:String>
<x:String x:Key="Text.PruneRemote.Target" xml:space="preserve">Target:</x:String>
<x:String x:Key="Text.PruneWorktrees" xml:space="preserve">Prune Worktrees</x:String>

View file

@ -387,6 +387,10 @@
<x:String x:Key="Text.Preference.Appearance.ThemeOverrides" xml:space="preserve">Dérogations de thème</x:String>
<x:String x:Key="Text.Preference.Appearance.UseFixedTabWidth" xml:space="preserve">Utiliser des onglets de taille fixe dans la barre de titre</x:String>
<x:String x:Key="Text.Preference.Appearance.UseNativeWindowFrame" xml:space="preserve">Utiliser un cadre de fenêtre natif</x:String>
<x:String x:Key="Text.Preference.DiffMerge" xml:space="preserve">OUTIL DIFF/MERGE</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Path" xml:space="preserve">Chemin d'installation</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Path.Placeholder" xml:space="preserve">Saisir le chemin d'installation de l'outil diff/merge</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Type" xml:space="preserve">Outil</x:String>
<x:String x:Key="Text.Preference.General" xml:space="preserve">GÉNÉRAL</x:String>
<x:String x:Key="Text.Preference.General.Check4UpdatesOnStartup" xml:space="preserve">Vérifier les mises à jour au démarrage</x:String>
<x:String x:Key="Text.Preference.General.Locale" xml:space="preserve">Language</x:String>
@ -401,7 +405,6 @@
<x:String x:Key="Text.Preference.Git.Email" xml:space="preserve">E-mail utilsateur</x:String>
<x:String x:Key="Text.Preference.Git.Email.Placeholder" xml:space="preserve">E-mail utilsateur global</x:String>
<x:String x:Key="Text.Preference.Git.Path" xml:space="preserve">Chemin d'installation</x:String>
<x:String x:Key="Text.Preference.Git.Shell" xml:space="preserve">Shell</x:String>
<x:String x:Key="Text.Preference.Git.User" xml:space="preserve">Nom d'utilisateur</x:String>
<x:String x:Key="Text.Preference.Git.User.Placeholder" xml:space="preserve">Nom d'utilisateur global</x:String>
<x:String x:Key="Text.Preference.Git.Version" xml:space="preserve">Version de Git</x:String>
@ -414,10 +417,6 @@
<x:String x:Key="Text.Preference.GPG.Path.Placeholder" xml:space="preserve">Saisir le chemin d'installation vers le programme GPG</x:String>
<x:String x:Key="Text.Preference.GPG.UserKey" xml:space="preserve">Clé de signature de l'utilisateur</x:String>
<x:String x:Key="Text.Preference.GPG.UserKey.Placeholder" xml:space="preserve">Clé de signature GPG de l'utilisateur</x:String>
<x:String x:Key="Text.Preference.DiffMerge" xml:space="preserve">OUTIL DIFF/MERGE</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Path" xml:space="preserve">Chemin d'installation</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Path.Placeholder" xml:space="preserve">Saisir le chemin d'installation de l'outil diff/merge</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Type" xml:space="preserve">Outil</x:String>
<x:String x:Key="Text.PruneRemote" xml:space="preserve">Élaguer une branche distant</x:String> <!-- If it is indeed about a branch -->
<x:String x:Key="Text.PruneRemote.Target" xml:space="preserve">Cible :</x:String>
<x:String x:Key="Text.PruneWorktrees" xml:space="preserve">Élaguer les Worktrees</x:String>

View file

@ -381,6 +381,10 @@
<x:String x:Key="Text.Preference.Appearance.Theme" xml:space="preserve">Tema</x:String>
<x:String x:Key="Text.Preference.Appearance.ThemeOverrides" xml:space="preserve">Sobrescrever Tema</x:String>
<x:String x:Key="Text.Preference.Appearance.UseFixedTabWidth" xml:space="preserve">Usar largura fixa da aba na barra de título</x:String>
<x:String x:Key="Text.Preference.DiffMerge" xml:space="preserve">FERRAMENTA DE DIF/MERGE</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Path" xml:space="preserve">Caminho de Instalação</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Path.Placeholder" xml:space="preserve">Insira o caminho para a ferramenta de dif/merge</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Type" xml:space="preserve">Ferramenta</x:String>
<x:String x:Key="Text.Preference.General" xml:space="preserve">GERAL</x:String>
<x:String x:Key="Text.Preference.General.Check4UpdatesOnStartup" xml:space="preserve">Verificar atualizações na inicialização</x:String>
<x:String x:Key="Text.Preference.General.Locale" xml:space="preserve">Idioma</x:String>
@ -395,7 +399,6 @@
<x:String x:Key="Text.Preference.Git.Email" xml:space="preserve">E-mail do Usuário</x:String>
<x:String x:Key="Text.Preference.Git.Email.Placeholder" xml:space="preserve">E-mail global do usuário git</x:String>
<x:String x:Key="Text.Preference.Git.Path" xml:space="preserve">Caminho de Instalação</x:String>
<x:String x:Key="Text.Preference.Git.Shell" xml:space="preserve">Shell</x:String>
<x:String x:Key="Text.Preference.Git.User" xml:space="preserve">Nome do Usuário</x:String>
<x:String x:Key="Text.Preference.Git.User.Placeholder" xml:space="preserve">Nome global do usuário git</x:String>
<x:String x:Key="Text.Preference.Git.Version" xml:space="preserve">Versão do Git</x:String>
@ -408,10 +411,6 @@
<x:String x:Key="Text.Preference.GPG.Path.Placeholder" xml:space="preserve">Insira o caminho do programa gpg instalado</x:String>
<x:String x:Key="Text.Preference.GPG.UserKey" xml:space="preserve">Chave de Assinatura do Usuário</x:String>
<x:String x:Key="Text.Preference.GPG.UserKey.Placeholder" xml:space="preserve">Chave de assinatura gpg do usuário</x:String>
<x:String x:Key="Text.Preference.DiffMerge" xml:space="preserve">FERRAMENTA DE DIF/MERGE</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Path" xml:space="preserve">Caminho de Instalação</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Path.Placeholder" xml:space="preserve">Insira o caminho para a ferramenta de dif/merge</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Type" xml:space="preserve">Ferramenta</x:String>
<x:String x:Key="Text.PruneRemote" xml:space="preserve">Prunar Remoto</x:String>
<x:String x:Key="Text.PruneRemote.Target" xml:space="preserve">Alvo:</x:String>
<x:String x:Key="Text.PruneWorktrees" xml:space="preserve">Podar Worktrees</x:String>

View file

@ -398,6 +398,10 @@
<x:String x:Key="Text.Preference.Appearance.ThemeOverrides" xml:space="preserve">主题自定义</x:String>
<x:String x:Key="Text.Preference.Appearance.UseFixedTabWidth" xml:space="preserve">主标签使用固定宽度</x:String>
<x:String x:Key="Text.Preference.Appearance.UseNativeWindowFrame" xml:space="preserve">使用系统默认窗体样式</x:String>
<x:String x:Key="Text.Preference.DiffMerge" xml:space="preserve">对比/合并工具</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Path" xml:space="preserve">安装路径</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Path.Placeholder" xml:space="preserve">填写工具可执行文件所在位置</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Type" xml:space="preserve">工具</x:String>
<x:String x:Key="Text.Preference.General" xml:space="preserve">通用配置</x:String>
<x:String x:Key="Text.Preference.General.Check4UpdatesOnStartup" xml:space="preserve">启动时检测软件更新</x:String>
<x:String x:Key="Text.Preference.General.Locale" xml:space="preserve">显示语言</x:String>
@ -412,7 +416,6 @@
<x:String x:Key="Text.Preference.Git.Email" xml:space="preserve">邮箱</x:String>
<x:String x:Key="Text.Preference.Git.Email.Placeholder" xml:space="preserve">默认GIT用户邮箱</x:String>
<x:String x:Key="Text.Preference.Git.Path" xml:space="preserve">安装路径</x:String>
<x:String x:Key="Text.Preference.Git.Shell" xml:space="preserve">终端Shell</x:String>
<x:String x:Key="Text.Preference.Git.User" xml:space="preserve">用户名</x:String>
<x:String x:Key="Text.Preference.Git.User.Placeholder" xml:space="preserve">默认GIT用户名</x:String>
<x:String x:Key="Text.Preference.Git.Version" xml:space="preserve">Git 版本</x:String>
@ -425,10 +428,10 @@
<x:String x:Key="Text.Preference.GPG.Path.Placeholder" xml:space="preserve">签名程序所在路径</x:String>
<x:String x:Key="Text.Preference.GPG.UserKey" xml:space="preserve">用户签名KEY</x:String>
<x:String x:Key="Text.Preference.GPG.UserKey.Placeholder" xml:space="preserve">输入签名提交所使用的KEY</x:String>
<x:String x:Key="Text.Preference.DiffMerge" xml:space="preserve">对比/合并工具</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Path" xml:space="preserve">安装路径</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Path.Placeholder" xml:space="preserve">填写工具可执行文件所在位置</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Type" xml:space="preserve">工具</x:String>
<x:String x:Key="Text.Preference.Integration" xml:space="preserve">第三方工具集成</x:String>
<x:String x:Key="Text.Preference.Shell" xml:space="preserve">终端/SHELL</x:String>
<x:String x:Key="Text.Preference.Shell.Type" xml:space="preserve">终端/SHELL</x:String>
<x:String x:Key="Text.Preference.Shell.Path" xml:space="preserve">安装路径</x:String>
<x:String x:Key="Text.PruneRemote" xml:space="preserve">清理远程已删除分支</x:String>
<x:String x:Key="Text.PruneRemote.Target" xml:space="preserve">目标 </x:String>
<x:String x:Key="Text.PruneWorktrees" xml:space="preserve">清理工作树</x:String>

View file

@ -398,6 +398,10 @@
<x:String x:Key="Text.Preference.Appearance.ThemeOverrides" xml:space="preserve">自訂主題</x:String>
<x:String x:Key="Text.Preference.Appearance.UseFixedTabWidth" xml:space="preserve">使用固定寬度的分頁標籤</x:String>
<x:String x:Key="Text.Preference.Appearance.UseNativeWindowFrame" xml:space="preserve">使用系統原生預設視窗樣式</x:String>
<x:String x:Key="Text.Preference.DiffMerge" xml:space="preserve">對比/合併工具</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Path" xml:space="preserve">安裝路徑</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Path.Placeholder" xml:space="preserve">填寫可執行檔案所在路徑</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Type" xml:space="preserve">工具</x:String>
<x:String x:Key="Text.Preference.General" xml:space="preserve">一般設定</x:String>
<x:String x:Key="Text.Preference.General.Check4UpdatesOnStartup" xml:space="preserve">啟動時檢查軟體更新</x:String>
<x:String x:Key="Text.Preference.General.Locale" xml:space="preserve">顯示語言</x:String>
@ -412,7 +416,6 @@
<x:String x:Key="Text.Preference.Git.Email" xml:space="preserve">電子郵件</x:String>
<x:String x:Key="Text.Preference.Git.Email.Placeholder" xml:space="preserve">預設 Git 使用者電子郵件</x:String>
<x:String x:Key="Text.Preference.Git.Path" xml:space="preserve">安裝路徑</x:String>
<x:String x:Key="Text.Preference.Git.Shell" xml:space="preserve">終端 Shell</x:String>
<x:String x:Key="Text.Preference.Git.User" xml:space="preserve">使用者名稱</x:String>
<x:String x:Key="Text.Preference.Git.User.Placeholder" xml:space="preserve">預設 Git 使用者名稱</x:String>
<x:String x:Key="Text.Preference.Git.Version" xml:space="preserve">Git 版本</x:String>
@ -425,10 +428,10 @@
<x:String x:Key="Text.Preference.GPG.Path.Placeholder" xml:space="preserve">填寫 gpg.exe 所在路徑</x:String>
<x:String x:Key="Text.Preference.GPG.UserKey" xml:space="preserve">使用者簽章金鑰</x:String>
<x:String x:Key="Text.Preference.GPG.UserKey.Placeholder" xml:space="preserve">填寫簽章提交所使用的金鑰</x:String>
<x:String x:Key="Text.Preference.DiffMerge" xml:space="preserve">對比/合併工具</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Path" xml:space="preserve">安裝路徑</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Path.Placeholder" xml:space="preserve">填寫可執行檔案所在路徑</x:String>
<x:String x:Key="Text.Preference.DiffMerge.Type" xml:space="preserve">工具</x:String>
<x:String x:Key="Text.Preference.Integration" xml:space="preserve">第三方工具集成</x:String>
<x:String x:Key="Text.Preference.Shell" xml:space="preserve">終端/SHELL</x:String>
<x:String x:Key="Text.Preference.Shell.Type" xml:space="preserve">終端/SHELL</x:String>
<x:String x:Key="Text.Preference.Shell.Path" xml:space="preserve">安裝路徑</x:String>
<x:String x:Key="Text.PruneRemote" xml:space="preserve">清理遠端已刪除分支</x:String>
<x:String x:Key="Text.PruneRemote.Target" xml:space="preserve">目標:</x:String>
<x:String x:Key="Text.PruneWorktrees" xml:space="preserve">清理工作區</x:String>

View file

@ -229,16 +229,6 @@ namespace SourceGit.ViewModels
}
}
public Models.Shell GitShell
{
get => Native.OS.GetShell();
set
{
if (Native.OS.SetShell(value))
OnPropertyChanged();
}
}
public string GitDefaultCloneDir
{
get => _gitDefaultCloneDir;
@ -274,6 +264,59 @@ namespace SourceGit.ViewModels
}
}
public int ShellOrTerminal
{
get => _shellOrTerminal;
set
{
if (SetProperty(ref _shellOrTerminal, value))
{
if (value >= 0 && value < Models.ShellOrTerminal.Supported.Count)
Native.OS.SetShellOrTerminal(Models.ShellOrTerminal.Supported[value]);
else
Native.OS.SetShellOrTerminal(null);
OnPropertyChanged(nameof(ShellOrTerminalPath));
}
}
}
public string ShellOrTerminalPath
{
get => Native.OS.ShellOrTerminal;
set
{
if (value != Native.OS.ShellOrTerminal)
{
Native.OS.ShellOrTerminal = value;
OnPropertyChanged();
}
}
}
public int ExternalMergeToolType
{
get => _externalMergeToolType;
set
{
var changed = SetProperty(ref _externalMergeToolType, value);
if (changed && !OperatingSystem.IsWindows() && value > 0 && value < Models.ExternalMerger.Supported.Count)
{
var tool = Models.ExternalMerger.Supported[value];
if (File.Exists(tool.Exec))
ExternalMergeToolPath = tool.Exec;
else
ExternalMergeToolPath = string.Empty;
}
}
}
public string ExternalMergeToolPath
{
get => _externalMergeToolPath;
set => SetProperty(ref _externalMergeToolPath, value);
}
public string OpenAIServer
{
get => Models.OpenAI.Server;
@ -313,29 +356,6 @@ namespace SourceGit.ViewModels
}
}
public int ExternalMergeToolType
{
get => _externalMergeToolType;
set
{
var changed = SetProperty(ref _externalMergeToolType, value);
if (changed && !OperatingSystem.IsWindows() && value > 0 && value < Models.ExternalMerger.Supported.Count)
{
var tool = Models.ExternalMerger.Supported[value];
if (File.Exists(tool.Exec))
ExternalMergeToolPath = tool.Exec;
else
ExternalMergeToolPath = string.Empty;
}
}
}
public string ExternalMergeToolPath
{
get => _externalMergeToolPath;
set => SetProperty(ref _externalMergeToolPath, value);
}
public List<RepositoryNode> RepositoryNodes
{
get;
@ -560,6 +580,7 @@ namespace SourceGit.ViewModels
private string _gitDefaultCloneDir = string.Empty;
private int _shellOrTerminal = -1;
private int _externalMergeToolType = 0;
private string _externalMergeToolPath = string.Empty;
}

View file

@ -194,7 +194,7 @@
<TextBlock Classes="tab_header" Text="{DynamicResource Text.Preference.Git}"/>
</TabItem.Header>
<Grid Margin="8" RowDefinitions="32,32,Auto,32,32,32,32,32,Auto" ColumnDefinitions="Auto,*">
<Grid Margin="8" RowDefinitions="32,32,32,32,32,32,32,Auto" ColumnDefinitions="Auto,*">
<TextBlock Grid.Row="0" Grid.Column="0"
Text="{DynamicResource Text.Preference.Git.Path}"
HorizontalAlignment="Right"
@ -226,50 +226,11 @@
</Border>
</StackPanel>
<Border Grid.Row="2" Grid.Column="0"
Height="32"
IsVisible="{OnPlatform False, Windows=True}">
<TextBlock Text="{DynamicResource Text.Preference.Git.Shell}"
HorizontalAlignment="Right"
Margin="0,0,16,0"/>
</Border>
<ComboBox Grid.Row="2" Grid.Column="1"
MinHeight="28"
Padding="8,0"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
RenderOptions.BitmapInterpolationMode="HighQuality"
FontSize="{Binding DefaultFontSize, Mode=OneWay}"
SelectedIndex="{Binding GitShell, Mode=TwoWay}"
IsVisible="{OnPlatform False, Windows=True}">
<ComboBox.Items>
<Grid ColumnDefinitions="Auto,*">
<Image Grid.Column="0" Width="16" Height="16" Source="/Resources/Images/ShellIcons/git-bash.png" RenderOptions.BitmapInterpolationMode="HighQuality"/>
<TextBlock Grid.Column="1" Text="Git Bash" Margin="6,0,0,0"/>
</Grid>
<Grid ColumnDefinitions="Auto,*">
<Image Grid.Column="0" Width="16" Height="16" Source="/Resources/Images/ShellIcons/pwsh.png" RenderOptions.BitmapInterpolationMode="HighQuality"/>
<TextBlock Grid.Column="1" Text="PowerShell" Margin="6,0,0,0"/>
</Grid>
<Grid ColumnDefinitions="Auto,*">
<Image Grid.Column="0" Width="16" Height="16" Source="/Resources/Images/ShellIcons/cmd.png" RenderOptions.BitmapInterpolationMode="HighQuality"/>
<TextBlock Grid.Column="1" Text="Command Prompt" Margin="6,0,0,0"/>
</Grid>
<Grid ColumnDefinitions="Auto,*">
<Image Grid.Column="0" Width="16" Height="16" Source="/Resources/Images/ShellIcons/wt.png" RenderOptions.BitmapInterpolationMode="HighQuality"/>
<TextBlock Grid.Column="1" Text="Default Shell in Windows Terminal" Margin="6,0,0,0"/>
</Grid>
</ComboBox.Items>
</ComboBox>
<TextBlock Grid.Row="3" Grid.Column="0"
<TextBlock Grid.Row="2" Grid.Column="0"
Text="{DynamicResource Text.Preference.Git.DefaultCloneDir}"
HorizontalAlignment="Right"
Margin="0,0,16,0"/>
<TextBox Grid.Row="3" Grid.Column="1"
<TextBox Grid.Row="2" Grid.Column="1"
Height="28"
CornerRadius="3"
Text="{Binding GitDefaultCloneDir, Mode=TwoWay}">
@ -280,31 +241,31 @@
</TextBox.InnerRightContent>
</TextBox>
<TextBlock Grid.Row="4" Grid.Column="0"
<TextBlock Grid.Row="3" Grid.Column="0"
Text="{DynamicResource Text.Preference.Git.User}"
HorizontalAlignment="Right"
Margin="0,0,16,0"/>
<TextBox Grid.Row="4" Grid.Column="1"
<TextBox Grid.Row="3" Grid.Column="1"
Height="28"
CornerRadius="3"
Text="{Binding #ThisControl.DefaultUser, Mode=TwoWay}"
Watermark="{DynamicResource Text.Preference.Git.User.Placeholder}"/>
<TextBlock Grid.Row="5" Grid.Column="0"
<TextBlock Grid.Row="4" Grid.Column="0"
Text="{DynamicResource Text.Preference.Git.Email}"
HorizontalAlignment="Right"
Margin="0,0,16,0"/>
<TextBox Grid.Row="5" Grid.Column="1"
<TextBox Grid.Row="4" Grid.Column="1"
Height="28"
CornerRadius="3"
Text="{Binding #ThisControl.DefaultEmail, Mode=TwoWay}"
Watermark="{DynamicResource Text.Preference.Git.Email.Placeholder}"/>
<TextBlock Grid.Row="6" Grid.Column="0"
<TextBlock Grid.Row="5" Grid.Column="0"
Text="{DynamicResource Text.Preference.Git.CRLF}"
HorizontalAlignment="Right"
Margin="0,0,16,0"/>
<ComboBox Grid.Row="6" Grid.Column="1"
<ComboBox Grid.Row="5" Grid.Column="1"
MinHeight="28"
Padding="8,0"
HorizontalAlignment="Stretch"
@ -320,16 +281,16 @@
</ComboBox.ItemTemplate>
</ComboBox>
<CheckBox Grid.Row="7" Grid.Column="1"
<CheckBox Grid.Row="6" Grid.Column="1"
Content="{DynamicResource Text.Preference.Git.AutoFetch}"
IsChecked="{Binding GitAutoFetch, Mode=TwoWay}"/>
<TextBlock Grid.Row="8" Grid.Column="0"
<TextBlock Grid.Row="7" Grid.Column="0"
IsVisible="{Binding GitAutoFetch}"
Text="{DynamicResource Text.Preference.Git.AutoFetchInterval}"
HorizontalAlignment="Right"
Margin="0,0,16,0"/>
<Grid Grid.Row="8" Grid.Column="1" Height="32" ColumnDefinitions="*,Auto" IsVisible="{Binding GitAutoFetch}">
<Grid Grid.Row="7" Grid.Column="1" Height="32" ColumnDefinitions="*,Auto" IsVisible="{Binding GitAutoFetch}">
<NumericUpDown Grid.Column="0"
Minimum="1" Maximum="60" Increment="1"
Height="28"
@ -348,41 +309,6 @@
</Grid>
</TabItem>
<TabItem>
<TabItem.Header>
<TextBlock Classes="tab_header" Text="{DynamicResource Text.Preference.AI}"/>
</TabItem.Header>
<Grid Margin="8" RowDefinitions="32,32,32" ColumnDefinitions="Auto,*">
<TextBlock Grid.Row="0" Grid.Column="0"
Text="{DynamicResource Text.Preference.AI.Server}"
HorizontalAlignment="Right"
Margin="0,0,16,0"/>
<TextBox Grid.Row="0" Grid.Column="1"
Height="28"
CornerRadius="3"
Text="{Binding OpenAIServer, Mode=TwoWay}"/>
<TextBlock Grid.Row="1" Grid.Column="0"
Text="{DynamicResource Text.Preference.AI.Model}"
HorizontalAlignment="Right"
Margin="0,0,16,0"/>
<TextBox Grid.Row="1" Grid.Column="1"
Height="28"
CornerRadius="3"
Text="{Binding OpenAIModel, Mode=TwoWay}"/>
<TextBlock Grid.Row="2" Grid.Column="0"
Text="{DynamicResource Text.Preference.AI.ApiKey}"
HorizontalAlignment="Right"
Margin="0,0,16,0"/>
<TextBox Grid.Row="2" Grid.Column="1"
Height="28"
CornerRadius="3"
Text="{Binding OpenAIApiKey, Mode=TwoWay}"/>
</Grid>
</TabItem>
<TabItem>
<TabItem.Header>
<TextBlock Classes="tab_header" Text="{DynamicResource Text.Preference.GPG}"/>
@ -449,51 +375,142 @@
<TabItem>
<TabItem.Header>
<TextBlock Classes="tab_header" Text="{DynamicResource Text.Preference.DiffMerge}"/>
<TextBlock Classes="tab_header" Text="{DynamicResource Text.Preference.Integration}"/>
</TabItem.Header>
<Grid Margin="8" RowDefinitions="32,Auto" ColumnDefinitions="Auto,*">
<TextBlock Grid.Row="0" Grid.Column="0"
Text="{DynamicResource Text.Preference.DiffMerge.Type}"
HorizontalAlignment="Right"
Margin="0,0,16,0"/>
<ComboBox Grid.Row="0" Grid.Column="1"
MinHeight="28"
Padding="8,0"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
RenderOptions.BitmapInterpolationMode="HighQuality"
FontSize="{Binding DefaultFontSize}"
ItemsSource="{Binding Source={x:Static m:ExternalMerger.Supported}}"
SelectedIndex="{Binding ExternalMergeToolType, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="{x:Type m:ExternalMerger}">
<Grid ColumnDefinitions="Auto,*">
<Image Grid.Column="0" Width="16" Height="16" Source="{Binding IconImage}" RenderOptions.BitmapInterpolationMode="HighQuality"/>
<TextBlock Grid.Column="1" Text="{Binding Name}" Margin="6,0,0,0"/>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<StackPanel Margin="8" MaxWidth="580" Orientation="Vertical" Grid.IsSharedSizeScope="True">
<TextBlock Classes="bold" Text="{DynamicResource Text.Preference.Shell}"/>
<Rectangle Margin="0,8" Fill="{DynamicResource Brush.Border2}" Height=".6" HorizontalAlignment="Stretch"/>
<Grid Margin="8,0,0,0" RowDefinitions="32,Auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="IntegrationLabel"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="1" Grid.Column="0"
Text="{DynamicResource Text.Preference.DiffMerge.Path}"
HorizontalAlignment="Right"
Margin="0,0,16,0"
IsVisible="{Binding ExternalMergeToolType, Converter={x:Static c:IntConverters.IsGreaterThanZero}}"/>
<TextBox Grid.Row="1" Grid.Column="1"
Height="28"
CornerRadius="3"
Text="{Binding ExternalMergeToolPath, Mode=TwoWay}"
Watermark="{DynamicResource Text.Preference.DiffMerge.Path.Placeholder}"
IsVisible="{Binding ExternalMergeToolType, Converter={x:Static c:IntConverters.IsGreaterThanZero}}">
<TextBox.InnerRightContent>
<Button Classes="icon_button" Width="30" Height="30" Click="SelectExternalMergeTool">
<Path Data="{StaticResource Icons.Folder.Open}" Fill="{DynamicResource Brush.FG1}"/>
</Button>
</TextBox.InnerRightContent>
</TextBox>
</Grid>
<TextBlock Grid.Row="0" Grid.Column="0"
Text="{DynamicResource Text.Preference.Shell.Type}"
HorizontalAlignment="Right"
Margin="0,0,16,0"/>
<ComboBox Grid.Row="0" Grid.Column="1"
MinHeight="28"
Padding="8,0"
HorizontalAlignment="Stretch"
ItemsSource="{Binding Source={x:Static m:ShellOrTerminal.Supported}}"
SelectedIndex="{Binding ShellOrTerminal, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="{x:Type m:ShellOrTerminal}">
<Grid ColumnDefinitions="Auto,*">
<Image Grid.Column="0" Width="16" Height="16" Source="{Binding Icon}" RenderOptions.BitmapInterpolationMode="HighQuality"/>
<TextBlock Grid.Column="1" Text="{Binding Name}" Margin="6,0,0,0"/>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBlock Grid.Row="1" Grid.Column="0"
Text="{DynamicResource Text.Preference.Shell.Path}"
HorizontalAlignment="Right"
Margin="0,0,16,0"
IsVisible="{OnPlatform True, macOS=False}"/>
<TextBox Grid.Row="1" Grid.Column="1"
Height="28"
CornerRadius="3"
Text="{Binding ShellOrTerminalPath, Mode=TwoWay}"
IsVisible="{OnPlatform True, macOS=False}">
<TextBox.InnerRightContent>
<Button Classes="icon_button" Width="30" Height="30" Click="SelectShellOrTerminal">
<Path Data="{StaticResource Icons.Folder.Open}" Fill="{DynamicResource Brush.FG1}"/>
</Button>
</TextBox.InnerRightContent>
</TextBox>
</Grid>
<TextBlock Classes="bold" Margin="0,24,0,0" Text="{DynamicResource Text.Preference.DiffMerge}"/>
<Rectangle Margin="0,8" Fill="{DynamicResource Brush.Border2}" Height=".6" HorizontalAlignment="Stretch"/>
<Grid Margin="8,0,0,0" RowDefinitions="32,Auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="IntegrationLabel"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0"
Text="{DynamicResource Text.Preference.DiffMerge.Type}"
HorizontalAlignment="Right"
Margin="0,0,16,0"/>
<ComboBox Grid.Row="0" Grid.Column="1"
MinHeight="28"
Padding="8,0"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
RenderOptions.BitmapInterpolationMode="HighQuality"
FontSize="{Binding DefaultFontSize}"
ItemsSource="{Binding Source={x:Static m:ExternalMerger.Supported}}"
SelectedIndex="{Binding ExternalMergeToolType, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="{x:Type m:ExternalMerger}">
<Grid ColumnDefinitions="Auto,*">
<Image Grid.Column="0" Width="16" Height="16" Source="{Binding IconImage}" RenderOptions.BitmapInterpolationMode="HighQuality"/>
<TextBlock Grid.Column="1" Text="{Binding Name}" Margin="6,0,0,0"/>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBlock Grid.Row="1" Grid.Column="0"
Text="{DynamicResource Text.Preference.DiffMerge.Path}"
HorizontalAlignment="Right"
Margin="0,0,16,0"
IsVisible="{Binding ExternalMergeToolType, Converter={x:Static c:IntConverters.IsGreaterThanZero}}"/>
<TextBox Grid.Row="1" Grid.Column="1"
Height="28"
CornerRadius="3"
Text="{Binding ExternalMergeToolPath, Mode=TwoWay}"
Watermark="{DynamicResource Text.Preference.DiffMerge.Path.Placeholder}"
IsVisible="{Binding ExternalMergeToolType, Converter={x:Static c:IntConverters.IsGreaterThanZero}}">
<TextBox.InnerRightContent>
<Button Classes="icon_button" Width="30" Height="30" Click="SelectExternalMergeTool">
<Path Data="{StaticResource Icons.Folder.Open}" Fill="{DynamicResource Brush.FG1}"/>
</Button>
</TextBox.InnerRightContent>
</TextBox>
</Grid>
<TextBlock Classes="bold" Margin="0,24,0,0" Text="{DynamicResource Text.Preference.AI}"/>
<Rectangle Margin="0,8" Fill="{DynamicResource Brush.Border2}" Height=".6" HorizontalAlignment="Stretch"/>
<Grid Margin="8,0,0,0" RowDefinitions="32,32,32">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="IntegrationLabel"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0"
Text="{DynamicResource Text.Preference.AI.Server}"
HorizontalAlignment="Right"
Margin="0,0,16,0"/>
<TextBox Grid.Row="0" Grid.Column="1"
Height="28"
CornerRadius="3"
Text="{Binding OpenAIServer, Mode=TwoWay}"/>
<TextBlock Grid.Row="1" Grid.Column="0"
Text="{DynamicResource Text.Preference.AI.Model}"
HorizontalAlignment="Right"
Margin="0,0,16,0"/>
<TextBox Grid.Row="1" Grid.Column="1"
Height="28"
CornerRadius="3"
Text="{Binding OpenAIModel, Mode=TwoWay}"/>
<TextBlock Grid.Row="2" Grid.Column="0"
Text="{DynamicResource Text.Preference.AI.ApiKey}"
HorizontalAlignment="Right"
Margin="0,0,16,0"/>
<TextBox Grid.Row="2" Grid.Column="1"
Height="28"
CornerRadius="3"
Text="{Binding OpenAIApiKey, Mode=TwoWay}"/>
</Grid>
</StackPanel>
</TabItem>
</TabControl>
</Border>

View file

@ -183,7 +183,7 @@ namespace SourceGit.Views
e.Handled = true;
}
private async void SelectDefaultCloneDir(object _1, RoutedEventArgs _2)
private async void SelectDefaultCloneDir(object _, RoutedEventArgs e)
{
var options = new FolderPickerOpenOptions() { AllowMultiple = false };
try
@ -194,13 +194,15 @@ namespace SourceGit.Views
ViewModels.Preference.Instance.GitDefaultCloneDir = selected[0].Path.LocalPath;
}
}
catch (Exception e)
catch (Exception ex)
{
App.RaiseException(string.Empty, $"Failed to select default clone directory: {e.Message}");
App.RaiseException(string.Empty, $"Failed to select default clone directory: {ex.Message}");
}
e.Handled = true;
}
private async void SelectGPGExecutable(object _1, RoutedEventArgs _2)
private async void SelectGPGExecutable(object _, RoutedEventArgs e)
{
var patterns = new List<string>();
if (OperatingSystem.IsWindows())
@ -219,14 +221,39 @@ namespace SourceGit.Views
{
GPGExecutableFile = selected[0].Path.LocalPath;
}
e.Handled = true;
}
private async void SelectExternalMergeTool(object _1, RoutedEventArgs _2)
private async void SelectShellOrTerminal(object _, RoutedEventArgs e)
{
var type = ViewModels.Preference.Instance.ShellOrTerminal;
if (type == -1)
return;
var shell = Models.ShellOrTerminal.Supported[type];
var options = new FilePickerOpenOptions()
{
FileTypeFilter = [new FilePickerFileType(shell.Name) { Patterns = [shell.Exec] }],
AllowMultiple = false,
};
var selected = await StorageProvider.OpenFilePickerAsync(options);
if (selected.Count == 1)
{
ViewModels.Preference.Instance.ShellOrTerminalPath = selected[0].Path.LocalPath;
}
e.Handled = true;
}
private async void SelectExternalMergeTool(object _, RoutedEventArgs e)
{
var type = ViewModels.Preference.Instance.ExternalMergeToolType;
if (type < 0 || type >= Models.ExternalMerger.Supported.Count)
{
ViewModels.Preference.Instance.ExternalMergeToolType = 0;
e.Handled = true;
return;
}
@ -242,6 +269,8 @@ namespace SourceGit.Views
{
ViewModels.Preference.Instance.ExternalMergeToolPath = selected[0].Path.LocalPath;
}
e.Handled = true;
}
private void SetIfChanged(Dictionary<string, string> cached, string key, string value, string defValue)