Skip to content

Commit f93960a

Browse files
authored
Merge branch 'main' into 5bfa/CQ-OmnibarIntroduction
Signed-off-by: 0x5BFA <[email protected]>
2 parents 2ce1ba4 + c489032 commit f93960a

40 files changed

+630
-174
lines changed

Diff for: .github/NOTICE.md

+30
Original file line numberDiff line numberDiff line change
@@ -674,3 +674,33 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
674674
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
675675
DEALINGS IN THE SOFTWARE.
676676
```
677+
678+
## NaturalStringComparer
679+
680+
**Source**: [https://github.com/GihanSoft/NaturalStringComparer](https://github.com/GihanSoft/NaturalStringComparer)
681+
682+
### License
683+
684+
```
685+
MIT License
686+
687+
Copyright (c) 2018 Mohammad Babayi
688+
689+
Permission is hereby granted, free of charge, to any person obtaining a copy
690+
of this software and associated documentation files (the "Software"), to deal
691+
in the Software without restriction, including without limitation the rights
692+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
693+
copies of the Software, and to permit persons to whom the Software is
694+
furnished to do so, subject to the following conditions:
695+
696+
The above copyright notice and this permission notice shall be included in all
697+
copies or substantial portions of the Software.
698+
699+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
700+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
701+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
702+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
703+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
704+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
705+
SOFTWARE.
706+
```

Diff for: Directory.Packages.props

+18-17
Original file line numberDiff line numberDiff line change
@@ -10,30 +10,31 @@
1010
<PackageVersion Include="CommunityToolkit.Labs.WinUI.DependencyPropertyGenerator" Version="0.1.250206-build.2040" />
1111
<PackageVersion Include="CommunityToolkit.Labs.WinUI.Shimmer" Version="0.1.250206-build.2040" />
1212
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0" />
13-
<PackageVersion Include="CommunityToolkit.WinUI.Behaviors" Version="8.2.250129-preview2" />
14-
<PackageVersion Include="CommunityToolkit.WinUI.Controls.ColorPicker" Version="8.2.250129-preview2" />
15-
<PackageVersion Include="CommunityToolkit.WinUI.Controls.Primitives" Version="8.2.250129-preview2" />
16-
<PackageVersion Include="CommunityToolkit.WinUI.Controls.SettingsControls" Version="8.2.241112-preview1" />
17-
<PackageVersion Include="CommunityToolkit.WinUI.Controls.Sizers" Version="8.2.250129-preview2" />
18-
<PackageVersion Include="CommunityToolkit.WinUI.Converters" Version="8.2.250129-preview2" />
19-
<PackageVersion Include="CommunityToolkit.WinUI.Extensions" Version="8.2.250129-preview2" />
20-
<PackageVersion Include="CommunityToolkit.WinUI.Helpers" Version="8.2.250129-preview2" />
21-
<PackageVersion Include="CommunityToolkit.WinUI.Triggers" Version="8.2.250129-preview2" />
13+
<PackageVersion Include="CommunityToolkit.WinUI.Behaviors" Version="8.2.250402" />
14+
<PackageVersion Include="CommunityToolkit.WinUI.Controls.ColorPicker" Version="8.2.250402" />
15+
<PackageVersion Include="CommunityToolkit.WinUI.Controls.Primitives" Version="8.2.250402" />
16+
<PackageVersion Include="CommunityToolkit.WinUI.Controls.SettingsControls" Version="8.2.250402" />
17+
<PackageVersion Include="CommunityToolkit.WinUI.Controls.Sizers" Version="8.2.250402" />
18+
<PackageVersion Include="CommunityToolkit.WinUI.Converters" Version="8.2.250402" />
19+
<PackageVersion Include="CommunityToolkit.WinUI.Extensions" Version="8.2.250402" />
20+
<PackageVersion Include="CommunityToolkit.WinUI.Helpers" Version="8.2.250402" />
21+
<PackageVersion Include="CommunityToolkit.WinUI.Triggers" Version="8.2.250402" />
2222
<PackageVersion Include="DiscUtils.Udf" Version="0.16.13" />
23-
<PackageVersion Include="FluentFTP" Version="52.0.0" />
24-
<PackageVersion Include="LibGit2Sharp" Version="0.30.0" />
25-
<PackageVersion Include="MessageFormat" Version="7.1.2" />
23+
<PackageVersion Include="FluentFTP" Version="52.1.0" />
24+
<PackageVersion Include="LibGit2Sharp" Version="0.31.0" />
25+
<PackageVersion Include="MessageFormat" Version="7.1.3" />
2626
<PackageVersion Include="Microsoft.Data.Sqlite.Core" Version="9.0.2" />
2727
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.2" />
2828
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
2929
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.2" />
3030
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.1742" />
3131
<PackageVersion Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="3.0.0" />
3232
<PackageVersion Include="OwlCore.Storage" Version="0.12.2" />
33-
<PackageVersion Include="Sentry" Version="5.1.1" />
33+
<PackageVersion Include="Sentry" Version="5.5.1" />
3434
<PackageVersion Include="SevenZipSharp" Version="1.0.2" />
35+
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
3536
<PackageVersion Include="SQLitePCLRaw.bundle_green" Version="2.1.10" />
36-
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.7.250310001" />
37+
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.7.250401001" />
3738
<PackageVersion Include="Microsoft.Graphics.Win2D" Version="1.3.2" />
3839
<PackageVersion Include="TagLibSharp" Version="2.3.0" />
3940
<PackageVersion Include="Tulpep.ActiveDirectoryObjectPicker" Version="3.0.11" />
@@ -48,9 +49,9 @@
4849
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0" />
4950
<PackageVersion Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.12.0" />
5051
<PackageVersion Include="PolySharp" Version="1.15.0" />
51-
<PackageVersion Include="System.Text.Json" Version="9.0.2" />
52-
<PackageVersion Include="System.IO.Hashing" Version="9.0.2" />
53-
<PackageVersion Include="System.Threading.Tasks.Dataflow" Version="9.0.2" />
52+
<PackageVersion Include="System.Text.Json" Version="9.0.4" />
53+
<PackageVersion Include="System.IO.Hashing" Version="9.0.4" />
54+
<PackageVersion Include="System.Threading.Tasks.Dataflow" Version="9.0.4" />
5455
<PackageVersion Include="Appium.WebDriver" Version="4.4.5" />
5556
<PackageVersion Include="Axe.Windows" Version="2.4.2" />
5657
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />

Diff for: src/Files.App.Controls/AdaptiveGridView/AdaptiveGridView.Properties.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ private double ItemWidth
152152

153153
private static int CalculateColumns(double containerWidth, double itemWidth)
154154
{
155-
var columns = (int)Math.Round(containerWidth / itemWidth);
155+
var columns = itemWidth > 0 ? (int)Math.Round(containerWidth / itemWidth) : 0;
156156
if (columns == 0)
157157
{
158158
columns = 1;

Diff for: src/Files.App.Controls/AdaptiveGridView/AdaptiveGridView.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ protected virtual double CalculateItemWidth(double containerWidth)
119119
_needContainerMarginForLayout = true;
120120
}
121121

122-
return (containerWidth / columns) - itemMargin.Left - itemMargin.Right;
122+
return columns > 0 ? (containerWidth / columns) - itemMargin.Left - itemMargin.Right : 0;
123123
}
124124

125125
/// <summary>

Diff for: src/Files.App.Launcher/Files.App.Launcher.vcxproj

+2-2
Original file line numberDiff line numberDiff line change
@@ -137,14 +137,14 @@
137137
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
138138
<ImportGroup Label="ExtensionTargets">
139139
<Import Project="..\..\packages\Microsoft.Windows.CppWinRT.2.0.240405.15\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\packages\Microsoft.Windows.CppWinRT.2.0.240405.15\build\native\Microsoft.Windows.CppWinRT.targets')" />
140-
<Import Project="..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.240803.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.240803.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
140+
<Import Project="..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.250325.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.250325.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
141141
</ImportGroup>
142142
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
143143
<PropertyGroup>
144144
<ErrorText>This project references a NuGet package that is not on this computer. To download those packages, use Restore NuGet Packages. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
145145
</PropertyGroup>
146146
<Error Condition="!Exists('..\..\packages\Microsoft.Windows.CppWinRT.2.0.240405.15\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Windows.CppWinRT.2.0.240405.15\build\native\Microsoft.Windows.CppWinRT.props'))" />
147147
<Error Condition="!Exists('..\..\packages\Microsoft.Windows.CppWinRT.2.0.240405.15\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Windows.CppWinRT.2.0.240405.15\build\native\Microsoft.Windows.CppWinRT.targets'))" />
148-
<Error Condition="!Exists('..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.240803.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.240803.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
148+
<Error Condition="!Exists('..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.250325.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.250325.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
149149
</Target>
150150
</Project>

Diff for: src/Files.App.Launcher/packages.config

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<packages>
33
<package id="Microsoft.Windows.CppWinRT" version="2.0.240405.15" targetFramework="native" />
4-
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.240803.1" targetFramework="native" />
4+
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.250325.1" targetFramework="native" />
55
</packages>

Diff for: src/Files.App/Actions/Content/Archives/Decompress/DecompressArchive.cs

+6-1
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,15 @@ public override async Task ExecuteAsync(object? parameter = null)
4242
return;
4343

4444
var isArchiveEncrypted = await FilesystemTasks.Wrap(() => StorageArchiveService.IsEncryptedAsync(archive.Path));
45+
var isArchiveEncodingUndetermined = await FilesystemTasks.Wrap(() => StorageArchiveService.IsEncodingUndeterminedAsync(archive.Path));
4546
var password = string.Empty;
47+
Encoding? encoding = null;
4648

4749
DecompressArchiveDialog decompressArchiveDialog = new();
4850
DecompressArchiveDialogViewModel decompressArchiveViewModel = new(archive)
4951
{
5052
IsArchiveEncrypted = isArchiveEncrypted,
53+
IsArchiveEncodingUndetermined = isArchiveEncodingUndetermined,
5154
ShowPathSelection = true
5255
};
5356
decompressArchiveDialog.ViewModel = decompressArchiveViewModel;
@@ -62,6 +65,8 @@ public override async Task ExecuteAsync(object? parameter = null)
6265
if (isArchiveEncrypted && decompressArchiveViewModel.Password is not null)
6366
password = Encoding.UTF8.GetString(decompressArchiveViewModel.Password);
6467

68+
encoding = decompressArchiveViewModel.SelectedEncoding.Encoding;
69+
6570
// Check if archive still exists
6671
if (!StorageHelpers.Exists(archive.Path))
6772
return;
@@ -77,7 +82,7 @@ public override async Task ExecuteAsync(object? parameter = null)
7782

7883
// Operate decompress
7984
var result = await FilesystemTasks.Wrap(() =>
80-
StorageArchiveService.DecompressAsync(archive?.Path ?? string.Empty, destinationFolder?.Path ?? string.Empty, password));
85+
StorageArchiveService.DecompressAsync(archive?.Path ?? string.Empty, destinationFolder?.Path ?? string.Empty, password, encoding));
8186

8287
if (decompressArchiveViewModel.OpenDestinationFolderOnCompletion)
8388
await NavigationHelpers.OpenPath(destinationFolderPath, context.ShellPage, FilesystemItemType.Directory);
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Files Community
22
// Licensed under the MIT License.
33

4+
using Files.Shared.Helpers;
45
using Windows.Graphics.Imaging;
56

67
namespace Files.App.Actions
@@ -20,8 +21,14 @@ internal abstract class BaseRotateAction : ObservableObject, IAction
2021
protected abstract BitmapRotation Rotation { get; }
2122

2223
public bool IsExecutable =>
23-
IsContextPageTypeAdaptedToCommand() &&
24-
(context.ShellPage?.SlimContentPage?.SelectedItemsPropertiesViewModel?.IsCompatibleToSetAsWindowsWallpaper ?? false);
24+
context.ShellPage is not null &&
25+
context.ShellPage.SlimContentPage is not null &&
26+
context.PageType != ContentPageTypes.RecycleBin &&
27+
context.PageType != ContentPageTypes.ZipFolder &&
28+
context.PageType != ContentPageTypes.ReleaseNotes &&
29+
context.PageType != ContentPageTypes.Settings &&
30+
context.HasSelection &&
31+
context.SelectedItems.All(x => FileExtensionHelpers.IsCompatibleToSetAsWindowsWallpaper(x.FileExtension));
2532

2633
public BaseRotateAction()
2734
{
@@ -40,30 +47,10 @@ public async Task ExecuteAsync(object? parameter = null)
4047
await _infoPaneViewModel.UpdateSelectedItemPreviewAsync();
4148
}
4249

43-
private bool IsContextPageTypeAdaptedToCommand()
44-
{
45-
return
46-
context.PageType != ContentPageTypes.RecycleBin &&
47-
context.PageType != ContentPageTypes.ZipFolder &&
48-
context.PageType != ContentPageTypes.ReleaseNotes &&
49-
context.PageType != ContentPageTypes.Settings &&
50-
context.PageType != ContentPageTypes.None;
51-
}
52-
5350
private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e)
5451
{
55-
if (e.PropertyName is nameof(IContentPageContext.SelectedItem))
56-
{
57-
if (context.ShellPage is not null && context.ShellPage.SlimContentPage is not null)
58-
{
59-
var viewModel = context.ShellPage.SlimContentPage.SelectedItemsPropertiesViewModel;
60-
var extensions = context.SelectedItems.Select(selectedItem => selectedItem.FileExtension).Distinct().ToList();
61-
62-
viewModel.CheckAllFileExtensions(extensions);
63-
}
64-
52+
if (e.PropertyName is nameof(IContentPageContext.SelectedItems))
6553
OnPropertyChanged(nameof(IsExecutable));
66-
}
6754
}
6855
}
69-
}
56+
}

Diff for: src/Files.App/Actions/FileSystem/OpenFileLocationAction.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public async Task ExecuteAsync(object? parameter = null)
3535
if (context.ShellPage?.ShellViewModel is null)
3636
return;
3737

38-
var item = context.SelectedItem as ShortcutItem;
38+
var item = context.SelectedItem as IShortcutItem;
3939

4040
if (string.IsNullOrWhiteSpace(item?.TargetPath))
4141
return;

Diff for: src/Files.App/Actions/Navigation/OpenInNewTab/BaseOpenInNewTabAction.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(async () =>
4242
{
4343
await NavigationHelpers.AddNewTabByPathAsync(
4444
typeof(ShellPanesPage),
45-
(listedItem as ShortcutItem)?.TargetPath ?? listedItem.ItemPath,
45+
(listedItem as IShortcutItem)?.TargetPath ?? listedItem.ItemPath,
4646
false);
4747
},
4848
Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);

Diff for: src/Files.App/Actions/Navigation/OpenInNewWindow/BaseOpenInNewWindowAction.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public virtual async Task ExecuteAsync(object? parameter = null)
4848

4949
foreach (ListedItem listedItem in items)
5050
{
51-
var selectedItemPath = (listedItem as ShortcutItem)?.TargetPath ?? listedItem.ItemPath;
51+
var selectedItemPath = (listedItem as IShortcutItem)?.TargetPath ?? listedItem.ItemPath;
5252
var folderUri = new Uri($"files-dev:?folder={@selectedItemPath}");
5353

5454
await Launcher.LaunchUriAsync(folderUri);

Diff for: src/Files.App/Actions/Start/PinToStartAction.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ await SafetyExtensions.IgnoreExceptions(async () =>
3939
IStorable storable = listedItem.IsFolder switch
4040
{
4141
true => await StorageService.GetFolderAsync(listedItem.ItemPath),
42-
_ => await StorageService.GetFileAsync((listedItem as ShortcutItem)?.TargetPath ?? listedItem.ItemPath)
42+
_ => await StorageService.GetFileAsync((listedItem as IShortcutItem)?.TargetPath ?? listedItem.ItemPath)
4343
};
4444
await StartMenuService.PinAsync(storable, listedItem.Name);
4545
});

Diff for: src/Files.App/Actions/Start/UnpinFromStartAction.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ await SafetyExtensions.IgnoreExceptions(async () =>
3636
IStorable storable = listedItem.IsFolder switch
3737
{
3838
true => await StorageService.GetFolderAsync(listedItem.ItemPath),
39-
_ => await StorageService.GetFileAsync((listedItem as ShortcutItem)?.TargetPath ?? listedItem.ItemPath)
39+
_ => await StorageService.GetFileAsync((listedItem as IShortcutItem)?.TargetPath ?? listedItem.ItemPath)
4040
};
4141
await StartMenuService.UnpinAsync(storable);
4242
});

Diff for: src/Files.App/Data/Contracts/IStorageArchiveService.cs

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Files Community
22
// Licensed under the MIT License.
33

4+
using System.Text;
45
using SevenZip;
56

67
namespace Files.App.Data.Contracts
@@ -37,8 +38,9 @@ public interface IStorageArchiveService
3738
/// <param name="archiveFilePath">The archive file path to decompress.</param>
3839
/// <param name="destinationFolderPath">The destination folder path which the archive file will be decompressed to.</param>
3940
/// <param name="password">The password to decrypt the archive file if applicable.</param>
41+
/// <param name="encoding">The file name encoding to decrypt the archive file. If set to null, system default encoding will be used.</param>
4042
/// <returns>True if the decompression has done successfully; otherwise, false.</returns>
41-
Task<bool> DecompressAsync(string archiveFilePath, string destinationFolderPath, string password = "");
43+
Task<bool> DecompressAsync(string archiveFilePath, string destinationFolderPath, string password = "", Encoding? encoding = null);
4244

4345
/// <summary>
4446
/// Generates the archive file name from item names.
@@ -54,6 +56,13 @@ public interface IStorageArchiveService
5456
/// <returns>True if the archive file is encrypted; otherwise, false.</returns>
5557
Task<bool> IsEncryptedAsync(string archiveFilePath);
5658

59+
/// <summary>
60+
/// Gets the value that indicates whether the archive file's encoding is undetermined.
61+
/// </summary>
62+
/// <param name="archiveFilePath">The archive file path to check if the item is encrypted.</param>
63+
/// <returns>True if the archive file's encoding is undetermined; otherwise, false.</returns>
64+
Task<bool> IsEncodingUndeterminedAsync(string archiveFilePath);
65+
5766
/// <summary>
5867
/// Gets the <see cref="SevenZipExtractor"/> instance from the archive file path.
5968
/// </summary>

Diff for: src/Files.App/Data/Items/EncodingItem.cs

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) 2024 Files Community
2+
// Licensed under the MIT License. See the LICENSE.
3+
4+
using System.Text;
5+
6+
namespace Files.App.Data.Items
7+
{
8+
/// <summary>
9+
/// Represents a text encoding in the application.
10+
/// </summary>
11+
public sealed class EncodingItem
12+
{
13+
14+
public Encoding? Encoding { get; set; }
15+
16+
/// <summary>
17+
/// Gets the encoding name. e.g. English (United States)
18+
/// </summary>
19+
public string Name { get; set; }
20+
21+
/// <summary>
22+
/// Initializes a new instance of the <see cref="EncodingItem"/> class.
23+
/// </summary>
24+
/// <param name="code">The code of the language.</param>
25+
public EncodingItem(string code)
26+
{
27+
if (string.IsNullOrEmpty(code))
28+
{
29+
Encoding = null;
30+
Name = Strings.Default.GetLocalizedResource();
31+
}
32+
else
33+
{
34+
Encoding = Encoding.GetEncoding(code);
35+
Name = Encoding.EncodingName;
36+
}
37+
}
38+
39+
public override string ToString() => Name;
40+
}
41+
}

0 commit comments

Comments
 (0)