From 25fe12e7db73851137b628100ca5ad4e93e3ad77 Mon Sep 17 00:00:00 2001 From: Alejandro Mora Date: Sat, 25 Apr 2026 07:20:36 -0400 Subject: [PATCH 1/7] feat: :lipstick: modernize the ux Now that we have a better UI foundation, we can start playing with the UX to make it easier to understand --- Symlinker/MainWindow.xaml | 583 ++++++++++++++------- Symlinker/MainWindow.xaml.cs | 128 ++--- Symlinker/Properties/Resources.Designer.cs | 224 +++++--- Symlinker/Properties/Resources.resx | 101 ++-- Symlinker/Settings.StyleCop | 2 +- 5 files changed, 647 insertions(+), 391 deletions(-) diff --git a/Symlinker/MainWindow.xaml b/Symlinker/MainWindow.xaml index 969be73..4788f8d 100644 --- a/Symlinker/MainWindow.xaml +++ b/Symlinker/MainWindow.xaml @@ -1,227 +1,422 @@ - - - - - - - - - + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls" + xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks" + xmlns:p="clr-namespace:Symlinker.Properties" + Title="Symlinker" + Width="560" Height="280" + ResizeMode="NoResize" + WindowStartupLocation="CenterScreen" + Icon="icon.ico" + TitleCharacterCasing="Normal" + BorderThickness="0" + GlowBrush="Transparent" + NonActiveGlowBrush="Transparent" + TitleAlignment="Center" + ShowIconOnTitleBar="False" + TitleBarHeight="32" + WindowTitleBrush="{DynamicResource MahApps.Brushes.Gray10}" + NonActiveWindowTitleBrush="{DynamicResource MahApps.Brushes.Gray10}" + TitleForeground="{DynamicResource MahApps.Brushes.ThemeForeground}" + OverrideDefaultWindowCommandsBrush="{DynamicResource MahApps.Brushes.ThemeForeground}" + Background="{DynamicResource MahApps.Brushes.Control.Background}"> - - 4 - - - - - - - - - - - - - + - + + - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + - - - + + - + + + + + + + + + + + + + + + - + + + - - + + + - - - - - + + + + + diff --git a/Symlinker/MainWindow.xaml.cs b/Symlinker/MainWindow.xaml.cs index 3463fd2..54affbc 100644 --- a/Symlinker/MainWindow.xaml.cs +++ b/Symlinker/MainWindow.xaml.cs @@ -10,8 +10,10 @@ namespace Symlinker using System.Windows; using System.Windows.Controls; using System.Windows.Interop; + using System.Windows.Media; using MahApps.Metro.Controls; + using MahApps.Metro.IconPacks; using Res = Symlinker.Properties.Resources; @@ -24,101 +26,68 @@ public partial class MainWindow : MetroWindow [DllImport("dwmapi.dll")] private static extern int DwmSetWindowAttribute(nint hwnd, int attr, ref int attrValue, int attrSize); - private bool isFolder; + private Brush PlaceholderBrush => (Brush)FindResource("MahApps.Brushes.Gray5"); + private Brush PrimaryTextBrush => (Brush)FindResource("MahApps.Brushes.ThemeForeground"); + + private bool isFolder = true; public MainWindow() { InitializeComponent(); - - linkTypeComboBox.SelectedIndex = 0; - typeSelectorComboBox.SelectedIndex = 0; - Loaded += OnLoaded; } private void OnLoaded(object sender, RoutedEventArgs e) { - // Request Windows 11 rounded corners for this window var hwnd = new WindowInteropHelper(this).Handle; var preference = DWMWCP_ROUND; DwmSetWindowAttribute(hwnd, DWMWA_WINDOW_CORNER_PREFERENCE, ref preference, sizeof(int)); + + UpdateMode(); } - private void TypeSelectorComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) + private void TypeRadio_Checked(object sender, RoutedEventArgs e) { - Switcher(); + UpdateMode(); } - private void Switcher() + private void UpdateMode() { - if (groupBox1Header == null || groupBox2Header == null) - return; + if (folderTypeRadio == null || junctionRadio == null || symLinkRadio == null || sourceIcon == null) return; - if (typeSelectorComboBox.SelectedIndex == 0) - { - groupBox1Header.Text = Res.MainWindow_Switcher_Link_Folder; - groupBox2Header.Text = Res.MainWindow_Switcher_Destination_Folder; - label2.Text = Res.MainWindow_Switcher_Now_give_a_name_to_the_link_; - label3.Text = Res.MainWindow_Switcher_Please_select_the_path_to_the_real_folder_you_want_to_link_; - isFolder = true; - - // Add Directory Junction if not present - bool hasJunction = false; - foreach (ComboBoxItem item in linkTypeComboBox.Items) - { - if (item.Content as string == "Directory Junction") - { - hasJunction = true; - break; - } - } - if (!hasJunction) - linkTypeComboBox.Items.Add(new ComboBoxItem { Content = "Directory Junction" }); + isFolder = folderTypeRadio.IsChecked == true; - linkTypeComboBox.ToolTip = Res.TooltipLinkTypeFolderDescription; - } - else - { - groupBox1Header.Text = Res.MainWindow_Switcher_Link_File; - groupBox2Header.Text = Res.MainWindow_Switcher_Destination_File; - label2.Text = Res.MainWindow_Switcher_Now_give_a_name_to_your_file_; - label3.Text = Res.MainWindow_Switcher_Please_select_the_path_to_the_real_file_you_want_to_link_; - isFolder = false; - - // Remove Directory Junction for file mode - ComboBoxItem junctionItem = null; - foreach (ComboBoxItem item in linkTypeComboBox.Items) - { - if (item.Content as string == "Directory Junction") - { - junctionItem = item; - break; - } - } - if (junctionItem != null) - { - if (linkTypeComboBox.SelectedIndex == 2) - linkTypeComboBox.SelectedIndex = 0; - linkTypeComboBox.Items.Remove(junctionItem); - } + junctionRadio.Visibility = isFolder ? Visibility.Visible : Visibility.Collapsed; - linkTypeComboBox.ToolTip = Res.TooltipLinkTypeFileDescription; - } + if (!isFolder && junctionRadio.IsChecked == true) + symLinkRadio.IsChecked = true; - typeSelectorComboBox.ToolTip = Res.TooltipTypeSelectorDescription; + sourceIcon.Kind = isFolder + ? PackIconMaterialKind.FolderOutline + : PackIconMaterialKind.FileOutline; } - private string ComboBoxSelection() + private string GetLinkTypeFlag() { - switch (linkTypeComboBox.SelectedIndex) + if (hardLinkRadio.IsChecked == true) return "/H "; + if (junctionRadio.IsChecked == true) return "/J "; + return string.Empty; + } + + private void DestinationPath_TextChanged(object sender, TextChangedEventArgs e) + { + var path = destinationLocationTextBox.Text; + if (string.IsNullOrEmpty(path)) { - case 1: - return "/H "; - case 2: - return "/J "; - default: - return string.Empty; + destinationFolderName.Text = "Target folder"; + destinationFolderName.Foreground = PlaceholderBrush; + return; } + + var trimmed = path.TrimEnd('\\', '/'); + var lastSep = trimmed.LastIndexOfAny(new[] { '\\', '/' }); + destinationFolderName.Text = lastSep >= 0 ? trimmed[(lastSep + 1)..] : trimmed; + destinationFolderName.Foreground = PrimaryTextBrush; } private void CreateLink() @@ -212,7 +181,7 @@ private void SendCommand(string link) try { var target = string.Format(CultureInfo.InvariantCulture, "\"{0}\"", destinationLocationTextBox.Text); - var typeLink = ComboBoxSelection(); + var typeLink = GetLinkTypeFlag(); var directory = isFolder ? "/D " : string.Empty; var stringCommand = string.Format(CultureInfo.InvariantCulture, "/c mklink {0}{1}{2}{3}", directory, typeLink, link, target); var processStartInfo = new ProcessStartInfo @@ -244,17 +213,13 @@ private void SendCommand(string link) private void Process_OutputDataReceived(object sender, DataReceivedEventArgs e) { if (!string.IsNullOrEmpty(e.Data)) - { MessageBox.Show(e.Data, Res.MessageBoxSuccessTitle, MessageBoxButton.OK, MessageBoxImage.Information); - } } private void Process_ErrorDataReceived(object sender, DataReceivedEventArgs e) { if (!string.IsNullOrEmpty(e.Data)) - { MessageBox.Show(e.Data, Res.MessageBoxErrorTitle, MessageBoxButton.OK, MessageBoxImage.Error); - } } private void ExploreButton1_Click(object sender, RoutedEventArgs e) @@ -318,16 +283,23 @@ private void TextBox_PreviewDragEnter(object sender, DragEventArgs e) e.Handled = true; } - private void TextBox_Drop(object sender, DragEventArgs e) + private void SourceCard_Drop(object sender, DragEventArgs e) { if (e.Data.GetDataPresent(DataFormats.FileDrop)) { var files = (string[])e.Data.GetData(DataFormats.FileDrop); if (files != null && files.Length != 0) - { - var textBox = (TextBox)sender; - textBox.Text = files[0]; - } + destinationLocationTextBox.Text = files[0]; + } + } + + private void LinkCard_Drop(object sender, DragEventArgs e) + { + if (e.Data.GetDataPresent(DataFormats.FileDrop)) + { + var files = (string[])e.Data.GetData(DataFormats.FileDrop); + if (files != null && files.Length != 0) + linkLocationTextBox.Text = files[0]; } } } diff --git a/Symlinker/Properties/Resources.Designer.cs b/Symlinker/Properties/Resources.Designer.cs index 7796723..0d75522 100644 --- a/Symlinker/Properties/Resources.Designer.cs +++ b/Symlinker/Properties/Resources.Designer.cs @@ -22,7 +22,7 @@ namespace Symlinker.Properties { [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { + public class Resources { private static global::System.Resources.ResourceManager resourceMan; @@ -36,7 +36,7 @@ internal Resources() { /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { + public static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Symlinker.Properties.Resources", typeof(Resources).Assembly); @@ -51,7 +51,7 @@ internal Resources() { /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { + public static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } @@ -67,70 +67,70 @@ internal Resources() { /// ///Thanks to Microsoft for the use of their shortcut arrow :). /// - internal static string AboutDescription { + public static string AboutDescription { get { return ResourceManager.GetString("AboutDescription", resourceCulture); } } /// - /// Looks up a localized string similar to Cannot find the file needed to create links, the creation stopped. + /// Looks up a localized string similar to Cannot find the command-line tool needed to create links. /// - internal static string CmdNotFound { + public static string CmdNotFound { get { return ResourceManager.GetString("CmdNotFound", resourceCulture); } } /// - /// Looks up a localized string similar to The link name you are using already exists in the selected directory, would you like to DELETE the file and then create a new link?. + /// Looks up a localized string similar to A file with this link name already exists in the selected directory. Would you like to delete it and create a new link?. /// - internal static string DialogDeleteFile { + public static string DialogDeleteFile { get { return ResourceManager.GetString("DialogDeleteFile", resourceCulture); } } /// - /// Looks up a localized string similar to File already there.... + /// Looks up a localized string similar to File already exists. /// - internal static string DialogDeleteFileWarning { + public static string DialogDeleteFileWarning { get { return ResourceManager.GetString("DialogDeleteFileWarning", resourceCulture); } } /// - /// Looks up a localized string similar to The link name you are using already exists in the selected directory, would you like to DELETE the folder and then create a new link?. + /// Looks up a localized string similar to A folder with this link name already exists in the selected directory. Would you like to delete it and create a new link?. /// - internal static string DialogFolderExists { + public static string DialogFolderExists { get { return ResourceManager.GetString("DialogFolderExists", resourceCulture); } } /// - /// Looks up a localized string similar to Folder already there.... + /// Looks up a localized string similar to Folder already exists. /// - internal static string DialogFolderExistsDialog { + public static string DialogFolderExistsDialog { get { return ResourceManager.GetString("DialogFolderExistsDialog", resourceCulture); } } /// - /// Looks up a localized string similar to One of the directories/files does not exists, please provide valid directories/files. + /// Looks up a localized string similar to One of the specified directories or files does not exist. Please provide valid paths. /// - internal static string FilesOrFolderNotExists { + public static string FilesOrFolderNotExists { get { return ResourceManager.GetString("FilesOrFolderNotExists", resourceCulture); } } /// - /// Looks up a localized string similar to Please fill all the blanks spaces with the indicated info. + /// Looks up a localized string similar to Please fill in all the required fields. /// - internal static string FillBlanks { + public static string FillBlanks { get { return ResourceManager.GetString("FillBlanks", resourceCulture); } @@ -138,18 +138,18 @@ internal static string FillBlanks { /// - /// Looks up a localized string similar to Link creation aborted. + /// Looks up a localized string similar to Link creation was aborted. /// - internal static string LinkCreationAborted { + public static string LinkCreationAborted { get { return ResourceManager.GetString("LinkCreationAborted", resourceCulture); } } /// - /// Looks up a localized string similar to Aborted Operation. + /// Looks up a localized string similar to Operation aborted. /// - internal static string LinkCreationAbortedWarning { + public static string LinkCreationAbortedWarning { get { return ResourceManager.GetString("LinkCreationAbortedWarning", resourceCulture); } @@ -158,88 +158,142 @@ internal static string LinkCreationAbortedWarning { /// /// Looks up a localized string similar to Link successfully created. /// - internal static string LinkSuccessfullyCreated { + public static string LinkSuccessfullyCreated { get { return ResourceManager.GetString("LinkSuccessfullyCreated", resourceCulture); } } /// - /// Looks up a localized string similar to Destination File. + /// Looks up a localized string similar to Add another. /// - internal static string MainWindow_Switcher_Destination_File { + public static string ButtonAddAnother { get { - return ResourceManager.GetString("MainWindow_Switcher_Destination_File", resourceCulture); + return ResourceManager.GetString("ButtonAddAnother", resourceCulture); } } - + /// - /// Looks up a localized string similar to Destination Folder. + /// Looks up a localized string similar to Create link. /// - internal static string MainWindow_Switcher_Destination_Folder { + public static string ButtonCreateLink { get { - return ResourceManager.GetString("MainWindow_Switcher_Destination_Folder", resourceCulture); + return ResourceManager.GetString("ButtonCreateLink", resourceCulture); } } - + /// - /// Looks up a localized string similar to Link File. + /// Looks up a localized string similar to LINK LOCATION. /// - internal static string MainWindow_Switcher_Link_File { + public static string CardHeaderLinkLocation { get { - return ResourceManager.GetString("MainWindow_Switcher_Link_File", resourceCulture); + return ResourceManager.GetString("CardHeaderLinkLocation", resourceCulture); } } - + /// - /// Looks up a localized string similar to Link Folder. + /// Looks up a localized string similar to TARGET. /// - internal static string MainWindow_Switcher_Link_Folder { + public static string CardHeaderTarget { get { - return ResourceManager.GetString("MainWindow_Switcher_Link_Folder", resourceCulture); + return ResourceManager.GetString("CardHeaderTarget", resourceCulture); } } - + /// - /// Looks up a localized string similar to Now give a name to the link:. + /// Looks up a localized string similar to File. /// - internal static string MainWindow_Switcher_Now_give_a_name_to_the_link_ { + public static string PillFile { get { - return ResourceManager.GetString("MainWindow_Switcher_Now_give_a_name_to_the_link_", resourceCulture); + return ResourceManager.GetString("PillFile", resourceCulture); } } - + /// - /// Looks up a localized string similar to Now give a name to your file:. + /// Looks up a localized string similar to Folder. /// - internal static string MainWindow_Switcher_Now_give_a_name_to_your_file_ { + public static string PillFolder { get { - return ResourceManager.GetString("MainWindow_Switcher_Now_give_a_name_to_your_file_", resourceCulture); + return ResourceManager.GetString("PillFolder", resourceCulture); } } - + /// - /// Looks up a localized string similar to Please select the path to the real file you want to link:. + /// Looks up a localized string similar to Hard link. /// - internal static string MainWindow_Switcher_Please_select_the_path_to_the_real_file_you_want_to_link_ { + public static string PillHardLink { get { - return ResourceManager.GetString("MainWindow_Switcher_Please_select_the_path_to_the_real_file_you_want_to_link_", resourceCulture); + return ResourceManager.GetString("PillHardLink", resourceCulture); } } - + + /// + /// Looks up a localized string similar to Junction. + /// + public static string PillJunction { + get { + return ResourceManager.GetString("PillJunction", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Symbolic link. + /// + public static string PillSymbolicLink { + get { + return ResourceManager.GetString("PillSymbolicLink", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Link name. + /// + public static string PlaceholderLinkName { + get { + return ResourceManager.GetString("PlaceholderLinkName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to C:\path\to\location. + /// + public static string PlaceholderLinkPath { + get { + return ResourceManager.GetString("PlaceholderLinkPath", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Target file. + /// + public static string PlaceholderTargetFile { + get { + return ResourceManager.GetString("PlaceholderTargetFile", resourceCulture); + } + } + /// - /// Looks up a localized string similar to Please select the path to the real folder you want to link:. + /// Looks up a localized string similar to Target folder. /// - internal static string MainWindow_Switcher_Please_select_the_path_to_the_real_folder_you_want_to_link_ { + public static string PlaceholderTargetFolder { get { - return ResourceManager.GetString("MainWindow_Switcher_Please_select_the_path_to_the_real_folder_you_want_to_link_", resourceCulture); + return ResourceManager.GetString("PlaceholderTargetFolder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to D:\path\to\target. + /// + public static string PlaceholderTargetPath { + get { + return ResourceManager.GetString("PlaceholderTargetPath", resourceCulture); } } /// /// Looks up a localized string similar to About. /// - internal static string MessageBoxAboutTitle { + public static string MessageBoxAboutTitle { get { return ResourceManager.GetString("MessageBoxAboutTitle", resourceCulture); } @@ -248,16 +302,16 @@ internal static string MessageBoxAboutTitle { /// /// Looks up a localized string similar to Error. /// - internal static string MessageBoxErrorTitle { + public static string MessageBoxErrorTitle { get { return ResourceManager.GetString("MessageBoxErrorTitle", resourceCulture); } } /// - /// Looks up a localized string similar to An error has ocurred:. + /// Looks up a localized string similar to An error has occurred:. /// - internal static string MessageBoxExceptionOcurred { + public static string MessageBoxExceptionOcurred { get { return ResourceManager.GetString("MessageBoxExceptionOcurred", resourceCulture); } @@ -266,58 +320,70 @@ internal static string MessageBoxExceptionOcurred { /// /// Looks up a localized string similar to Success. /// - internal static string MessageBoxSuccessTitle { + public static string MessageBoxSuccessTitle { get { return ResourceManager.GetString("MessageBoxSuccessTitle", resourceCulture); } } /// - /// Looks up a localized string similar to This option allows you to select the style of your symbolic link, either - ///you choose to use symbolic links or hard links. - ///Use symbolic links as a default.. + /// Looks up a localized string similar to Choose where the link will be created and give it a name. /// - internal static string TooltipLinkTypeFileDescription { + public static string TooltipLinkLocationCard { + get { + return ResourceManager.GetString("TooltipLinkLocationCard", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Select the real file or folder the link will point to. + /// + public static string TooltipTargetCard { + get { + return ResourceManager.GetString("TooltipTargetCard", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Choose the link type: symbolic link or hard link. + /// + public static string TooltipLinkTypeFileDescription { get { return ResourceManager.GetString("TooltipLinkTypeFileDescription", resourceCulture); } } - + /// - /// Looks up a localized string similar to This option allows you to select the style of your symbolic link, either - ///you choose to use symbolic links, hard links or directory junctions. - ///Use symbolic links as a default.. + /// Looks up a localized string similar to Choose the link type: symbolic link, hard link, or directory junction. /// - internal static string TooltipLinkTypeFolderDescription { + public static string TooltipLinkTypeFolderDescription { get { return ResourceManager.GetString("TooltipLinkTypeFolderDescription", resourceCulture); } } - + /// - /// Looks up a localized string similar to Symbolic Link types. + /// Looks up a localized string similar to Symbolic link types. /// - internal static string TooltipLinkTypeTitle { + public static string TooltipLinkTypeTitle { get { return ResourceManager.GetString("TooltipLinkTypeTitle", resourceCulture); } } - + /// - /// Looks up a localized string similar to With this option you can choose between creating file symbolic links; - ///this is using a file to point to another file, or folder symbolic links; - ///this is using folders that point to other folders.. + /// Looks up a localized string similar to Choose between file mode or folder mode. /// - internal static string TooltipTypeSelectorDescription { + public static string TooltipTypeSelectorDescription { get { return ResourceManager.GetString("TooltipTypeSelectorDescription", resourceCulture); } } - + /// - /// Looks up a localized string similar to Symbolic Link type selector. + /// Looks up a localized string similar to Symbolic link type selector. /// - internal static string TooltipTypeSelectorTitle { + public static string TooltipTypeSelectorTitle { get { return ResourceManager.GetString("TooltipTypeSelectorTitle", resourceCulture); } diff --git a/Symlinker/Properties/Resources.resx b/Symlinker/Properties/Resources.resx index b9ed390..8176cae 100644 --- a/Symlinker/Properties/Resources.resx +++ b/Symlinker/Properties/Resources.resx @@ -118,34 +118,34 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - The link name you are using already exists in the selected directory, would you like to DELETE the file and then create a new link? + A file with this link name already exists in the selected directory. Would you like to delete it and create a new link? - File already there... + File already exists - Link creation aborted + Link creation was aborted. - Aborted Operation + Operation aborted - One of the directories/files does not exists, please provide valid directories/files + One of the specified directories or files does not exist. Please provide valid paths. - Please fill all the blanks spaces with the indicated info + Please fill in all the required fields. - Cannot find the file needed to create links, the creation stopped + Cannot find the command-line tool needed to create links. - Link successfully created + Link successfully created. - The link name you are using already exists in the selected directory, would you like to DELETE the folder and then create a new link? + A folder with this link name already exists in the selected directory. Would you like to delete it and create a new link? - Folder already there... + Folder already exists Success @@ -153,62 +153,85 @@ Error - - Link Folder + + LINK LOCATION - - Destination Folder + + TARGET - - Now give a name to the link: + + Link name - - Please select the path to the real folder you want to link: + + C:\path\to\location - - Link File + + D:\path\to\target - - Destination File + + Target folder - - Now give a name to your file: + + Target file - - Please select the path to the real file you want to link: + + Folder + + + File + + + Symbolic link + + + Hard link + + + Junction + + + Create link + + + Add another About - © 2010-2025 Alejandro Mora + © 2010-2026 Alejandro Mora Version: {0} e-mail: mail@alejandro.md Thanks to Microsoft for the use of their shortcut arrow :) - An error has ocurred: + An error has occurred: + + + Choose where the link will be created and give it a name. +Click the icon or drag and drop a folder here. + + + Select the real file or folder the link will point to. +Click the icon or drag and drop a file/folder here. - This option allows you to select the style of your symbolic link, either -you choose to use symbolic links or hard links. -Use symbolic links as a default. + Choose the link type: symbolic link or hard link. +Use symbolic links as the default. - This option allows you to select the style of your symbolic link, either -you choose to use symbolic links, hard links or directory junctions. -Use symbolic links as a default. + Choose the link type: symbolic link, hard link, or directory junction. +Use symbolic links as the default. - Symbolic Link types + Symbolic link types - With this option you can choose between creating file symbolic links; -this is using a file to point to another file, or folder symbolic links; -this is using folders that point to other folders. + Choose between file mode (link a file to another file) +or folder mode (link a folder to another folder). - Symbolic Link type selector + Symbolic link type selector \ No newline at end of file diff --git a/Symlinker/Settings.StyleCop b/Symlinker/Settings.StyleCop index d430cf0..d2077d8 100644 --- a/Symlinker/Settings.StyleCop +++ b/Symlinker/Settings.StyleCop @@ -3,7 +3,7 @@ alejandro.md - 2010-2025 + 2010-2026 From 9c75fe0dd2b8ff4059f896b91e6a3a6f74788e72 Mon Sep 17 00:00:00 2001 From: Alejandro Mora Date: Sat, 25 Apr 2026 08:13:44 -0400 Subject: [PATCH 2/7] refactor: :art: improve the logic to handle edge cases --- Symlinker/MainWindow.xaml.cs | 184 ++++++++++++--------- Symlinker/Properties/Resources.Designer.cs | 172 +++++++++---------- Symlinker/Symlinker.csproj | 11 ++ 3 files changed, 206 insertions(+), 161 deletions(-) diff --git a/Symlinker/MainWindow.xaml.cs b/Symlinker/MainWindow.xaml.cs index 54affbc..2547366 100644 --- a/Symlinker/MainWindow.xaml.cs +++ b/Symlinker/MainWindow.xaml.cs @@ -7,6 +7,7 @@ namespace Symlinker using System.Linq; using System.Reflection; using System.Runtime.InteropServices; + using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Interop; @@ -53,18 +54,36 @@ private void TypeRadio_Checked(object sender, RoutedEventArgs e) private void UpdateMode() { - if (folderTypeRadio == null || junctionRadio == null || symLinkRadio == null || sourceIcon == null) return; + if (folderTypeRadio == null || junctionRadio == null || symLinkRadio == null || hardLinkRadio == null || sourceIcon == null) return; isFolder = folderTypeRadio.IsChecked == true; junctionRadio.Visibility = isFolder ? Visibility.Visible : Visibility.Collapsed; + hardLinkRadio.Visibility = isFolder ? Visibility.Collapsed : Visibility.Visible; if (!isFolder && junctionRadio.IsChecked == true) symLinkRadio.IsChecked = true; + if (isFolder && hardLinkRadio.IsChecked == true) + symLinkRadio.IsChecked = true; + sourceIcon.Kind = isFolder ? PackIconMaterialKind.FolderOutline : PackIconMaterialKind.FileOutline; + + var linkTypeTooltip = isFolder ? Res.TooltipLinkTypeFolderDescription : Res.TooltipLinkTypeFileDescription; + symLinkRadio.ToolTip = linkTypeTooltip; + hardLinkRadio.ToolTip = linkTypeTooltip; + junctionRadio.ToolTip = linkTypeTooltip; + + if (destinationLocationTextBox != null) + destinationLocationTextBox.Text = string.Empty; + + if (destinationFolderName != null) + { + destinationFolderName.Text = isFolder ? Res.PlaceholderTargetFolder : Res.PlaceholderTargetFile; + destinationFolderName.Foreground = PlaceholderBrush; + } } private string GetLinkTypeFlag() @@ -79,7 +98,7 @@ private void DestinationPath_TextChanged(object sender, TextChangedEventArgs e) var path = destinationLocationTextBox.Text; if (string.IsNullOrEmpty(path)) { - destinationFolderName.Text = "Target folder"; + destinationFolderName.Text = isFolder ? Res.PlaceholderTargetFolder : Res.PlaceholderTargetFile; destinationFolderName.Foreground = PlaceholderBrush; return; } @@ -90,84 +109,89 @@ private void DestinationPath_TextChanged(object sender, TextChangedEventArgs e) destinationFolderName.Foreground = PrimaryTextBrush; } - private void CreateLink() + private async Task CreateLink() { try { - if (linkLocationTextBox.Text != string.Empty && linkNameTextBox.Text != string.Empty && destinationLocationTextBox.Text != string.Empty) + var linkLocation = linkLocationTextBox.Text.Trim(); + var linkName = linkNameTextBox.Text.Trim(); + var destination = destinationLocationTextBox.Text.Trim(); + + if (string.IsNullOrWhiteSpace(linkLocation) || string.IsNullOrWhiteSpace(linkName) || string.IsNullOrWhiteSpace(destination)) { - if (isFolder && Directory.Exists(linkLocationTextBox.Text) && Directory.Exists(destinationLocationTextBox.Text)) - { - var link = string.Format( - "\"{0}\\{1}\" ", linkLocationTextBox.Text, linkNameTextBox.Text); + MessageBox.Show(Res.FillBlanks, Res.MessageBoxErrorTitle, MessageBoxButton.OK, MessageBoxImage.Error); + return; + } - var directories = Directory.GetDirectories(linkLocationTextBox.Text); + if (linkName.Contains('"') || linkLocation.Contains('"') || destination.Contains('"')) + { + MessageBox.Show(Res.FilesOrFolderNotExists, Res.MessageBoxErrorTitle, MessageBoxButton.OK, MessageBoxImage.Warning); + return; + } - if (directories.Any(e => e.Split('\\').Last().Equals(linkNameTextBox.Text))) - { - var answer = MessageBox.Show( - Res.DialogFolderExists, - Res.DialogFolderExistsDialog, - MessageBoxButton.YesNo, - MessageBoxImage.Warning); - if (answer == MessageBoxResult.Yes) - { - var dir2Delete = directories.First(e => e.Split('\\').Last().Equals(linkNameTextBox.Text)); - Directory.Delete(dir2Delete); - SendCommand(link); - return; - } - - MessageBox.Show( - Res.LinkCreationAborted, - Res.LinkCreationAbortedWarning, - MessageBoxButton.OK, - MessageBoxImage.Stop); - } - else + var link = string.Format("\"{0}\\{1}\" ", linkLocation, linkName); + + if (isFolder && Directory.Exists(linkLocation) && Directory.Exists(destination)) + { + var directories = Directory.GetDirectories(linkLocation); + + if (directories.Any(e => Path.GetFileName(e).Equals(linkName, StringComparison.OrdinalIgnoreCase))) + { + var answer = MessageBox.Show( + Res.DialogFolderExists, + Res.DialogFolderExistsDialog, + MessageBoxButton.YesNo, + MessageBoxImage.Warning); + if (answer == MessageBoxResult.Yes) { - SendCommand(link); + var dir2Delete = directories.First(e => Path.GetFileName(e).Equals(linkName, StringComparison.OrdinalIgnoreCase)); + Directory.Delete(dir2Delete, true); + await SendCommand(link, destination); + return; } + + MessageBox.Show( + Res.LinkCreationAborted, + Res.LinkCreationAbortedWarning, + MessageBoxButton.OK, + MessageBoxImage.Stop); } - else if (Directory.Exists(linkLocationTextBox.Text) && File.Exists(destinationLocationTextBox.Text)) + else { - var link = string.Format( - "\"{0}\\{1}\" ", linkLocationTextBox.Text, linkNameTextBox.Text); - - var files = Directory.GetFiles(linkLocationTextBox.Text); - if (files.Any(e => e.Split('\\').Last().Equals(linkNameTextBox.Text))) - { - var answer = MessageBox.Show( - Res.DialogDeleteFile, - Res.DialogDeleteFileWarning, - MessageBoxButton.YesNo, - MessageBoxImage.Warning); - if (answer == MessageBoxResult.Yes) - { - var file2Delete = files.First(e => e.Split('\\').Last().Equals(linkNameTextBox.Text)); - File.Delete(file2Delete); - SendCommand(link); - return; - } - MessageBox.Show( - Res.LinkCreationAborted, - Res.LinkCreationAbortedWarning, - MessageBoxButton.OK, - MessageBoxImage.Stop); - } - else + await SendCommand(link, destination); + } + } + else if (!isFolder && Directory.Exists(linkLocation) && File.Exists(destination)) + { + var files = Directory.GetFiles(linkLocation); + if (files.Any(e => Path.GetFileName(e).Equals(linkName, StringComparison.OrdinalIgnoreCase))) + { + var answer = MessageBox.Show( + Res.DialogDeleteFile, + Res.DialogDeleteFileWarning, + MessageBoxButton.YesNo, + MessageBoxImage.Warning); + if (answer == MessageBoxResult.Yes) { - SendCommand(link); + var file2Delete = files.First(e => Path.GetFileName(e).Equals(linkName, StringComparison.OrdinalIgnoreCase)); + File.Delete(file2Delete); + await SendCommand(link, destination); + return; } + MessageBox.Show( + Res.LinkCreationAborted, + Res.LinkCreationAbortedWarning, + MessageBoxButton.OK, + MessageBoxImage.Stop); } else { - MessageBox.Show(Res.FilesOrFolderNotExists, Res.MessageBoxErrorTitle, MessageBoxButton.OK, MessageBoxImage.Warning); + await SendCommand(link, destination); } } else { - MessageBox.Show(Res.FillBlanks, Res.MessageBoxErrorTitle, MessageBoxButton.OK, MessageBoxImage.Error); + MessageBox.Show(Res.FilesOrFolderNotExists, Res.MessageBoxErrorTitle, MessageBoxButton.OK, MessageBoxImage.Warning); } } catch (Exception exception) @@ -176,11 +200,11 @@ private void CreateLink() } } - private void SendCommand(string link) + private async Task SendCommand(string link, string destination) { try { - var target = string.Format(CultureInfo.InvariantCulture, "\"{0}\"", destinationLocationTextBox.Text); + var target = string.Format(CultureInfo.InvariantCulture, "\"{0}\"", destination); var typeLink = GetLinkTypeFlag(); var directory = isFolder ? "/D " : string.Empty; var stringCommand = string.Format(CultureInfo.InvariantCulture, "/c mklink {0}{1}{2}{3}", directory, typeLink, link, target); @@ -200,8 +224,7 @@ private void SendCommand(string link) process.Start(); process.BeginOutputReadLine(); process.BeginErrorReadLine(); - process.WaitForExit(); - process.Close(); + await process.WaitForExitAsync(); process.Dispose(); } catch (Exception) @@ -213,13 +236,13 @@ private void SendCommand(string link) private void Process_OutputDataReceived(object sender, DataReceivedEventArgs e) { if (!string.IsNullOrEmpty(e.Data)) - MessageBox.Show(e.Data, Res.MessageBoxSuccessTitle, MessageBoxButton.OK, MessageBoxImage.Information); + Dispatcher.Invoke(() => MessageBox.Show(e.Data, Res.MessageBoxSuccessTitle, MessageBoxButton.OK, MessageBoxImage.Information)); } private void Process_ErrorDataReceived(object sender, DataReceivedEventArgs e) { if (!string.IsNullOrEmpty(e.Data)) - MessageBox.Show(e.Data, Res.MessageBoxErrorTitle, MessageBoxButton.OK, MessageBoxImage.Error); + Dispatcher.Invoke(() => MessageBox.Show(e.Data, Res.MessageBoxErrorTitle, MessageBoxButton.OK, MessageBoxImage.Error)); } private void ExploreButton1_Click(object sender, RoutedEventArgs e) @@ -245,19 +268,15 @@ private void ExploreButton2_Click(object sender, RoutedEventArgs e) } } - private void CreateLink_Click(object sender, RoutedEventArgs e) + private async void CreateLink_Click(object sender, RoutedEventArgs e) { - CreateLink(); + await CreateLink(); } private void AboutButton_Click(object sender, RoutedEventArgs e) { - string version; - try - { - version = Environment.GetEnvironmentVariable("ClickOnce_CurrentVersion"); - } - catch + var version = Environment.GetEnvironmentVariable("ClickOnce_CurrentVersion"); + if (string.IsNullOrEmpty(version)) { var assembly = Assembly.GetExecutingAssembly(); var fvi = FileVersionInfo.GetVersionInfo(assembly.Location); @@ -289,7 +308,18 @@ private void SourceCard_Drop(object sender, DragEventArgs e) { var files = (string[])e.Data.GetData(DataFormats.FileDrop); if (files != null && files.Length != 0) - destinationLocationTextBox.Text = files[0]; + { + var path = files[0]; + var droppedIsFolder = Directory.Exists(path); + if (droppedIsFolder != isFolder) + { + if (droppedIsFolder) + folderTypeRadio.IsChecked = true; + else + fileTypeRadio.IsChecked = true; + } + destinationLocationTextBox.Text = path; + } } } @@ -298,7 +328,7 @@ private void LinkCard_Drop(object sender, DragEventArgs e) if (e.Data.GetDataPresent(DataFormats.FileDrop)) { var files = (string[])e.Data.GetData(DataFormats.FileDrop); - if (files != null && files.Length != 0) + if (files != null && files.Length != 0 && Directory.Exists(files[0])) linkLocationTextBox.Text = files[0]; } } diff --git a/Symlinker/Properties/Resources.Designer.cs b/Symlinker/Properties/Resources.Designer.cs index 0d75522..2d34b03 100644 --- a/Symlinker/Properties/Resources.Designer.cs +++ b/Symlinker/Properties/Resources.Designer.cs @@ -19,7 +19,7 @@ namespace Symlinker.Properties { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class Resources { @@ -61,7 +61,7 @@ internal Resources() { } /// - /// Looks up a localized string similar to © 2010-2025 Alejandro Mora + /// Looks up a localized string similar to © 2010-2026 Alejandro Mora ///Version: {0} ///e-mail: mail@alejandro.md /// @@ -74,7 +74,43 @@ public static string AboutDescription { } /// - /// Looks up a localized string similar to Cannot find the command-line tool needed to create links. + /// Looks up a localized string similar to Add another. + /// + public static string ButtonAddAnother { + get { + return ResourceManager.GetString("ButtonAddAnother", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create link. + /// + public static string ButtonCreateLink { + get { + return ResourceManager.GetString("ButtonCreateLink", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to LINK LOCATION. + /// + public static string CardHeaderLinkLocation { + get { + return ResourceManager.GetString("CardHeaderLinkLocation", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to TARGET. + /// + public static string CardHeaderTarget { + get { + return ResourceManager.GetString("CardHeaderTarget", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot find the command-line tool needed to create links.. /// public static string CmdNotFound { get { @@ -119,7 +155,7 @@ public static string DialogFolderExistsDialog { } /// - /// Looks up a localized string similar to One of the specified directories or files does not exist. Please provide valid paths. + /// Looks up a localized string similar to One of the specified directories or files does not exist. Please provide valid paths.. /// public static string FilesOrFolderNotExists { get { @@ -128,7 +164,7 @@ public static string FilesOrFolderNotExists { } /// - /// Looks up a localized string similar to Please fill in all the required fields. + /// Looks up a localized string similar to Please fill in all the required fields.. /// public static string FillBlanks { get { @@ -136,9 +172,8 @@ public static string FillBlanks { } } - /// - /// Looks up a localized string similar to Link creation was aborted. + /// Looks up a localized string similar to Link creation was aborted.. /// public static string LinkCreationAborted { get { @@ -156,7 +191,7 @@ public static string LinkCreationAbortedWarning { } /// - /// Looks up a localized string similar to Link successfully created. + /// Looks up a localized string similar to Link successfully created.. /// public static string LinkSuccessfullyCreated { get { @@ -165,41 +200,41 @@ public static string LinkSuccessfullyCreated { } /// - /// Looks up a localized string similar to Add another. + /// Looks up a localized string similar to About. /// - public static string ButtonAddAnother { + public static string MessageBoxAboutTitle { get { - return ResourceManager.GetString("ButtonAddAnother", resourceCulture); + return ResourceManager.GetString("MessageBoxAboutTitle", resourceCulture); } } - + /// - /// Looks up a localized string similar to Create link. + /// Looks up a localized string similar to Error. /// - public static string ButtonCreateLink { + public static string MessageBoxErrorTitle { get { - return ResourceManager.GetString("ButtonCreateLink", resourceCulture); + return ResourceManager.GetString("MessageBoxErrorTitle", resourceCulture); } } - + /// - /// Looks up a localized string similar to LINK LOCATION. + /// Looks up a localized string similar to An error has occurred:. /// - public static string CardHeaderLinkLocation { + public static string MessageBoxExceptionOcurred { get { - return ResourceManager.GetString("CardHeaderLinkLocation", resourceCulture); + return ResourceManager.GetString("MessageBoxExceptionOcurred", resourceCulture); } } - + /// - /// Looks up a localized string similar to TARGET. + /// Looks up a localized string similar to Success. /// - public static string CardHeaderTarget { + public static string MessageBoxSuccessTitle { get { - return ResourceManager.GetString("CardHeaderTarget", resourceCulture); + return ResourceManager.GetString("MessageBoxSuccessTitle", resourceCulture); } } - + /// /// Looks up a localized string similar to File. /// @@ -208,7 +243,7 @@ public static string PillFile { return ResourceManager.GetString("PillFile", resourceCulture); } } - + /// /// Looks up a localized string similar to Folder. /// @@ -217,7 +252,7 @@ public static string PillFolder { return ResourceManager.GetString("PillFolder", resourceCulture); } } - + /// /// Looks up a localized string similar to Hard link. /// @@ -226,7 +261,7 @@ public static string PillHardLink { return ResourceManager.GetString("PillHardLink", resourceCulture); } } - + /// /// Looks up a localized string similar to Junction. /// @@ -235,7 +270,7 @@ public static string PillJunction { return ResourceManager.GetString("PillJunction", resourceCulture); } } - + /// /// Looks up a localized string similar to Symbolic link. /// @@ -244,7 +279,7 @@ public static string PillSymbolicLink { return ResourceManager.GetString("PillSymbolicLink", resourceCulture); } } - + /// /// Looks up a localized string similar to Link name. /// @@ -253,7 +288,7 @@ public static string PlaceholderLinkName { return ResourceManager.GetString("PlaceholderLinkName", resourceCulture); } } - + /// /// Looks up a localized string similar to C:\path\to\location. /// @@ -262,7 +297,7 @@ public static string PlaceholderLinkPath { return ResourceManager.GetString("PlaceholderLinkPath", resourceCulture); } } - + /// /// Looks up a localized string similar to Target file. /// @@ -271,7 +306,7 @@ public static string PlaceholderTargetFile { return ResourceManager.GetString("PlaceholderTargetFile", resourceCulture); } } - + /// /// Looks up a localized string similar to Target folder. /// @@ -280,7 +315,7 @@ public static string PlaceholderTargetFolder { return ResourceManager.GetString("PlaceholderTargetFolder", resourceCulture); } } - + /// /// Looks up a localized string similar to D:\path\to\target. /// @@ -290,78 +325,36 @@ public static string PlaceholderTargetPath { } } - /// - /// Looks up a localized string similar to About. - /// - public static string MessageBoxAboutTitle { - get { - return ResourceManager.GetString("MessageBoxAboutTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Error. - /// - public static string MessageBoxErrorTitle { - get { - return ResourceManager.GetString("MessageBoxErrorTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to An error has occurred:. - /// - public static string MessageBoxExceptionOcurred { - get { - return ResourceManager.GetString("MessageBoxExceptionOcurred", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Success. - /// - public static string MessageBoxSuccessTitle { - get { - return ResourceManager.GetString("MessageBoxSuccessTitle", resourceCulture); - } - } - /// /// Looks up a localized string similar to Choose where the link will be created and give it a name. + ///Click the icon or drag and drop a folder here.. /// public static string TooltipLinkLocationCard { get { return ResourceManager.GetString("TooltipLinkLocationCard", resourceCulture); } } - - /// - /// Looks up a localized string similar to Select the real file or folder the link will point to. - /// - public static string TooltipTargetCard { - get { - return ResourceManager.GetString("TooltipTargetCard", resourceCulture); - } - } - + /// /// Looks up a localized string similar to Choose the link type: symbolic link or hard link. + ///Use symbolic links as the default.. /// public static string TooltipLinkTypeFileDescription { get { return ResourceManager.GetString("TooltipLinkTypeFileDescription", resourceCulture); } } - + /// /// Looks up a localized string similar to Choose the link type: symbolic link, hard link, or directory junction. + ///Use symbolic links as the default.. /// public static string TooltipLinkTypeFolderDescription { get { return ResourceManager.GetString("TooltipLinkTypeFolderDescription", resourceCulture); } } - + /// /// Looks up a localized string similar to Symbolic link types. /// @@ -370,16 +363,27 @@ public static string TooltipLinkTypeTitle { return ResourceManager.GetString("TooltipLinkTypeTitle", resourceCulture); } } - + + /// + /// Looks up a localized string similar to Select the real file or folder the link will point to. + ///Click the icon or drag and drop a file/folder here.. + /// + public static string TooltipTargetCard { + get { + return ResourceManager.GetString("TooltipTargetCard", resourceCulture); + } + } + /// - /// Looks up a localized string similar to Choose between file mode or folder mode. + /// Looks up a localized string similar to Choose between file mode (link a file to another file) + ///or folder mode (link a folder to another folder).. /// public static string TooltipTypeSelectorDescription { get { return ResourceManager.GetString("TooltipTypeSelectorDescription", resourceCulture); } } - + /// /// Looks up a localized string similar to Symbolic link type selector. /// diff --git a/Symlinker/Symlinker.csproj b/Symlinker/Symlinker.csproj index 7f267e7..2619d44 100644 --- a/Symlinker/Symlinker.csproj +++ b/Symlinker/Symlinker.csproj @@ -68,12 +68,23 @@ + + True + True + Resources.resx + True True Settings.settings + + + PublicResXFileCodeGenerator + Resources.Designer.cs + + SettingsSingleFileGenerator From ca4742a78baa7adeba284d87215c8f0c80019b33 Mon Sep 17 00:00:00 2001 From: Alejandro Mora Date: Sat, 25 Apr 2026 09:06:46 -0400 Subject: [PATCH 3/7] =?UTF-8?q?fix:=20=F0=9F=A7=B9=20cleanup=20around?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/settings.local.json | 13 ++ Symlinker/MainWindow.xaml | 134 ++++++++++++------ Symlinker/MainWindow.xaml.cs | 107 +++++++------- .../PublishProfiles/ClickOnceProfile.pubxml | 2 +- Symlinker/Properties/Resources.Designer.cs | 29 +++- Symlinker/Properties/Resources.resx | 11 +- Symlinker/Symlinker.csproj | 2 +- 7 files changed, 205 insertions(+), 93 deletions(-) create mode 100644 .claude/settings.local.json diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..9d36e22 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,13 @@ +{ + "permissions": { + "allow": [ + "Bash(git tag v1.1.1.15 fd3dc4312b4e1d6e6dc6998f9dcf15a35f006157)", + "Bash(git cliff --config cliff.toml --output CHANGELOG_NEW.md)", + "Bash(env)", + "Bash(if not exist .signpath mkdir .signpath)", + "Bash(powershell -Command:*)" + ], + "deny": [], + "ask": [] + } +} diff --git a/Symlinker/MainWindow.xaml b/Symlinker/MainWindow.xaml index 4788f8d..8d7eaed 100644 --- a/Symlinker/MainWindow.xaml +++ b/Symlinker/MainWindow.xaml @@ -23,6 +23,8 @@ Background="{DynamicResource MahApps.Brushes.Control.Background}"> + + 10 + + @@ -194,11 +242,15 @@ ShadowDepth="1" Opacity="0.08" Direction="270"/> - - - - + @@ -231,7 +283,7 @@ @@ -245,11 +297,10 @@ - + - + @@ -261,7 +312,7 @@ @@ -282,11 +333,15 @@ ShadowDepth="2" Opacity="0.10" Direction="270"/> - - - - + @@ -325,7 +380,7 @@ TextChanged="DestinationPath_TextChanged"/> - + @@ -398,18 +453,15 @@ - + + +