diff --git a/src/AppCommon/FileTime.App.DependencyInjection/DependencyInjection.cs b/src/AppCommon/FileTime.App.DependencyInjection/DependencyInjection.cs
new file mode 100644
index 0000000..3a4e9ea
--- /dev/null
+++ b/src/AppCommon/FileTime.App.DependencyInjection/DependencyInjection.cs
@@ -0,0 +1,16 @@
+using FileTime.Providers.Local;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace FileTime.App.DependencyInjection
+{
+ public static class DependencyInjection
+ {
+ public static IServiceCollection RegisterDefaultServices(IServiceCollection? serviceCollection = null)
+ {
+ serviceCollection ??= new ServiceCollection();
+
+ return serviceCollection
+ .AddLocalServices();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AppCommon/FileTime.App.DependencyInjection/FileTime.App.DependencyInjection.csproj b/src/AppCommon/FileTime.App.DependencyInjection/FileTime.App.DependencyInjection.csproj
new file mode 100644
index 0000000..9252fe1
--- /dev/null
+++ b/src/AppCommon/FileTime.App.DependencyInjection/FileTime.App.DependencyInjection.csproj
@@ -0,0 +1,15 @@
+
+
+
+ net6.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
diff --git a/src/Core/FileTime.Core.Abstraction/Behaviors/IOnContainerEnter.cs b/src/Core/FileTime.Core.Abstraction/Behaviors/IOnContainerEnter.cs
new file mode 100644
index 0000000..47bc880
--- /dev/null
+++ b/src/Core/FileTime.Core.Abstraction/Behaviors/IOnContainerEnter.cs
@@ -0,0 +1,7 @@
+namespace FileTime.Core.Behaviors
+{
+ public interface IOnContainerEnter
+ {
+ Task OnEnter();
+ }
+}
\ No newline at end of file
diff --git a/src/Core/FileTime.Core.Abstraction/Enums/AbsolutePathType.cs b/src/Core/FileTime.Core.Abstraction/Enums/AbsolutePathType.cs
new file mode 100644
index 0000000..8ea7f63
--- /dev/null
+++ b/src/Core/FileTime.Core.Abstraction/Enums/AbsolutePathType.cs
@@ -0,0 +1,9 @@
+namespace FileTime.Core.Enums
+{
+ public enum AbsolutePathType
+ {
+ Unknown,
+ Container,
+ Element
+ }
+}
\ No newline at end of file
diff --git a/src/Core/FileTime.Core.Abstraction/Enums/SupportsDelete.cs b/src/Core/FileTime.Core.Abstraction/Enums/SupportsDelete.cs
new file mode 100644
index 0000000..ce052d5
--- /dev/null
+++ b/src/Core/FileTime.Core.Abstraction/Enums/SupportsDelete.cs
@@ -0,0 +1,9 @@
+namespace FileTime.Core.Enums
+{
+ public enum SupportsDelete
+ {
+ False,
+ True,
+ HardDeleteOnly,
+ }
+}
\ No newline at end of file
diff --git a/src/Core/FileTime.Core.Abstraction/FileTime.Core.Abstraction.csproj b/src/Core/FileTime.Core.Abstraction/FileTime.Core.Abstraction.csproj
new file mode 100644
index 0000000..f2d4187
--- /dev/null
+++ b/src/Core/FileTime.Core.Abstraction/FileTime.Core.Abstraction.csproj
@@ -0,0 +1,13 @@
+
+
+
+ net6.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/src/Core/FileTime.Core.Abstraction/Models/FullName.cs b/src/Core/FileTime.Core.Abstraction/Models/FullName.cs
new file mode 100644
index 0000000..128f48d
--- /dev/null
+++ b/src/Core/FileTime.Core.Abstraction/Models/FullName.cs
@@ -0,0 +1,4 @@
+namespace FileTime.Core.Models
+{
+ public record FullName(string Path);
+}
\ No newline at end of file
diff --git a/src/Core/FileTime.Core.Abstraction/Models/IAbsolutePath.cs b/src/Core/FileTime.Core.Abstraction/Models/IAbsolutePath.cs
new file mode 100644
index 0000000..7cc5837
--- /dev/null
+++ b/src/Core/FileTime.Core.Abstraction/Models/IAbsolutePath.cs
@@ -0,0 +1,13 @@
+using FileTime.Core.Enums;
+using FileTime.Core.Services;
+
+namespace FileTime.Core.Models
+{
+ public interface IAbsolutePath
+ {
+ IContentProvider ContentProvider { get; }
+ IContentProvider? VirtualContentProvider { get; }
+ FullName Path { get; }
+ AbsolutePathType Type { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/Core/FileTime.Core.Abstraction/Models/IContainer.cs b/src/Core/FileTime.Core.Abstraction/Models/IContainer.cs
new file mode 100644
index 0000000..e5922a7
--- /dev/null
+++ b/src/Core/FileTime.Core.Abstraction/Models/IContainer.cs
@@ -0,0 +1,7 @@
+namespace FileTime.Core.Models
+{
+ public interface IContainer : IItem
+ {
+ IReadOnlyList Items { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/Core/FileTime.Core.Abstraction/Models/IElement.cs b/src/Core/FileTime.Core.Abstraction/Models/IElement.cs
new file mode 100644
index 0000000..cb68137
--- /dev/null
+++ b/src/Core/FileTime.Core.Abstraction/Models/IElement.cs
@@ -0,0 +1,7 @@
+namespace FileTime.Core.Models
+{
+ public interface IElement : IItem
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/src/Core/FileTime.Core.Abstraction/Models/IFileElement.cs b/src/Core/FileTime.Core.Abstraction/Models/IFileElement.cs
new file mode 100644
index 0000000..1c907bd
--- /dev/null
+++ b/src/Core/FileTime.Core.Abstraction/Models/IFileElement.cs
@@ -0,0 +1,7 @@
+namespace FileTime.Core.Models
+{
+ public interface IFileElement : IElement
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/src/Core/FileTime.Core.Abstraction/Models/IItem.cs b/src/Core/FileTime.Core.Abstraction/Models/IItem.cs
new file mode 100644
index 0000000..a04964c
--- /dev/null
+++ b/src/Core/FileTime.Core.Abstraction/Models/IItem.cs
@@ -0,0 +1,18 @@
+using FileTime.Core.Enums;
+using FileTime.Core.Services;
+
+namespace FileTime.Core.Models
+{
+ public interface IItem
+ {
+ string Name { get; }
+ string DisplayName { get; }
+ FullName? FullName { get; }
+ NativePath? NativePath { get; }
+ bool IsHidden { get; }
+ bool IsExists { get; }
+ SupportsDelete CanDelete { get; }
+ bool CanRename { get; }
+ IContentProvider Provider { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/Core/FileTime.Core.Abstraction/Models/NativePath.cs b/src/Core/FileTime.Core.Abstraction/Models/NativePath.cs
new file mode 100644
index 0000000..8f1d703
--- /dev/null
+++ b/src/Core/FileTime.Core.Abstraction/Models/NativePath.cs
@@ -0,0 +1,4 @@
+namespace FileTime.Core.Models
+{
+ public record NativePath(string Path);
+}
\ No newline at end of file
diff --git a/src/Core/FileTime.Core.Abstraction/Services/IContentProvider.cs b/src/Core/FileTime.Core.Abstraction/Services/IContentProvider.cs
new file mode 100644
index 0000000..30df12a
--- /dev/null
+++ b/src/Core/FileTime.Core.Abstraction/Services/IContentProvider.cs
@@ -0,0 +1,12 @@
+using FileTime.Core.Behaviors;
+using FileTime.Core.Models;
+
+namespace FileTime.Core.Services
+{
+ public interface IContentProvider : IContainer, IOnContainerEnter
+ {
+ Task GetItemByFullNameAsync(FullName fullName);
+ Task GetItemByNativePathAsync(NativePath nativePath);
+ Task> GetItemsByContainerAsync(FullName fullName);
+ }
+}
\ No newline at end of file
diff --git a/src/Core/FileTime.Core.Abstraction/Services/ITab.cs b/src/Core/FileTime.Core.Abstraction/Services/ITab.cs
new file mode 100644
index 0000000..7420885
--- /dev/null
+++ b/src/Core/FileTime.Core.Abstraction/Services/ITab.cs
@@ -0,0 +1,7 @@
+namespace FileTime.Core.Services
+{
+ public interface ITab
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/src/Core/FileTime.Core.Models/AbsolutePath.cs b/src/Core/FileTime.Core.Models/AbsolutePath.cs
new file mode 100644
index 0000000..f5f41f0
--- /dev/null
+++ b/src/Core/FileTime.Core.Models/AbsolutePath.cs
@@ -0,0 +1,22 @@
+using FileTime.Core.Enums;
+using FileTime.Core.Services;
+
+namespace FileTime.Core.Models
+{
+ public class AbsolutePath : IAbsolutePath
+ {
+ public IContentProvider ContentProvider { get; }
+ public IContentProvider? VirtualContentProvider { get; }
+
+ public FullName Path { get; }
+ public AbsolutePathType Type { get; }
+
+ public AbsolutePath(IContentProvider contentProvider, FullName path, AbsolutePathType type, IContentProvider? virtualContentProvider = null)
+ {
+ ContentProvider = contentProvider;
+ Path = path;
+ VirtualContentProvider = virtualContentProvider;
+ Type = type;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Core/FileTime.Core.Models/Constants.cs b/src/Core/FileTime.Core.Models/Constants.cs
new file mode 100644
index 0000000..f8d8aaf
--- /dev/null
+++ b/src/Core/FileTime.Core.Models/Constants.cs
@@ -0,0 +1,8 @@
+namespace FileTime.Core.Models
+{
+ public static class Constants
+ {
+ public const char SeparatorChar = '/';
+ public const string ContentProviderProtocol = "ctp://";
+ }
+}
\ No newline at end of file
diff --git a/src/Core/FileTime.Core.Models/Container.cs b/src/Core/FileTime.Core.Models/Container.cs
new file mode 100644
index 0000000..d0d245f
--- /dev/null
+++ b/src/Core/FileTime.Core.Models/Container.cs
@@ -0,0 +1,17 @@
+using FileTime.Core.Enums;
+using FileTime.Core.Services;
+
+namespace FileTime.Core.Models
+{
+ public record Container
+ (string Name,
+ string DisplayName,
+ FullName FullName,
+ NativePath NativePath,
+ bool IsHidden,
+ bool IsExists,
+ SupportsDelete CanDelete,
+ bool CanRename,
+ IContentProvider Provider,
+ IReadOnlyList Items) : IContainer;
+}
\ No newline at end of file
diff --git a/src/Core/FileTime.Core.Models/Element.cs b/src/Core/FileTime.Core.Models/Element.cs
new file mode 100644
index 0000000..392ace9
--- /dev/null
+++ b/src/Core/FileTime.Core.Models/Element.cs
@@ -0,0 +1,16 @@
+using FileTime.Core.Enums;
+using FileTime.Core.Services;
+
+namespace FileTime.Core.Models
+{
+ public record Element
+ (string Name,
+ string DisplayName,
+ FullName FullName,
+ NativePath NativePath,
+ bool IsHidden,
+ bool IsExists,
+ SupportsDelete CanDelete,
+ bool CanRename,
+ IContentProvider Provider) : IElement;
+}
\ No newline at end of file
diff --git a/src/Core/FileTime.Core.Models/FileTime.Core.Models.csproj b/src/Core/FileTime.Core.Models/FileTime.Core.Models.csproj
new file mode 100644
index 0000000..72dd3f9
--- /dev/null
+++ b/src/Core/FileTime.Core.Models/FileTime.Core.Models.csproj
@@ -0,0 +1,13 @@
+
+
+
+ net6.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/src/Core/FileTime.Core.Services/ContentProviderBase.cs b/src/Core/FileTime.Core.Services/ContentProviderBase.cs
new file mode 100644
index 0000000..90c97e4
--- /dev/null
+++ b/src/Core/FileTime.Core.Services/ContentProviderBase.cs
@@ -0,0 +1,41 @@
+using FileTime.Core.Enums;
+using FileTime.Core.Models;
+
+namespace FileTime.Core.Services
+{
+ public abstract class ContentProviderBase : IContentProvider
+ {
+ protected List Items { get; set; } = new List();
+
+ IReadOnlyList IContainer.Items => Items;
+
+ public string Name { get; }
+
+ public string DisplayName { get; }
+
+ public FullName? FullName => null;
+
+ public NativePath? NativePath => null;
+
+ public bool IsHidden => false;
+
+ public bool IsExists => true;
+
+ public SupportsDelete CanDelete => SupportsDelete.False;
+
+ public bool CanRename => false;
+
+ public IContentProvider Provider => this;
+
+ protected ContentProviderBase(string name)
+ {
+ DisplayName = Name = name;
+ }
+
+ public virtual Task OnEnter() => Task.CompletedTask;
+ public virtual async Task GetItemByFullNameAsync(FullName fullName) => await GetItemByNativePathAsync(GetNativePath(fullName));
+ public abstract Task GetItemByNativePathAsync(NativePath nativePath);
+ public abstract Task> GetItemsByContainerAsync(FullName fullName);
+ public abstract NativePath GetNativePath(FullName fullName);
+ }
+}
\ No newline at end of file
diff --git a/src/Core/FileTime.Core.Services/FileTime.Core.Services.csproj b/src/Core/FileTime.Core.Services/FileTime.Core.Services.csproj
new file mode 100644
index 0000000..13eed31
--- /dev/null
+++ b/src/Core/FileTime.Core.Services/FileTime.Core.Services.csproj
@@ -0,0 +1,18 @@
+
+
+
+ net6.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Core/FileTime.Core.Services/Tab.cs b/src/Core/FileTime.Core.Services/Tab.cs
new file mode 100644
index 0000000..78913ad
--- /dev/null
+++ b/src/Core/FileTime.Core.Services/Tab.cs
@@ -0,0 +1,7 @@
+namespace FileTime.Core.Services
+{
+ public class Tab : ITab
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/src/FileTime.sln b/src/FileTime.sln
new file mode 100644
index 0000000..ad96f6e
--- /dev/null
+++ b/src/FileTime.sln
@@ -0,0 +1,76 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.1.32203.90
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{3324D046-1E05-46B5-B1BA-82910D56B332}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileTime.Core.Abstraction", "Core\FileTime.Core.Abstraction\FileTime.Core.Abstraction.csproj", "{99562652-F633-4496-9932-AF8E614906C4}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileTime.Core.Models", "Core\FileTime.Core.Models\FileTime.Core.Models.csproj", "{E4681ADE-BAF7-4903-BD2F-59C144AE5637}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileTime.Core.Services", "Core\FileTime.Core.Services\FileTime.Core.Services.csproj", "{5DEA3A08-62FF-4508-B755-3DABA96FC2B2}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GuiApp", "GuiApp", "{8ADCA794-440F-4E8F-B9F2-2A30E54119A7}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ConsoleApp", "ConsoleApp", "{CAEEAD3C-41EB-405C-ACA9-BA1E4C352549}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AppCommon", "AppCommon", "{A5291117-3001-498B-AC8B-E14F71F72570}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Avalonia", "Avalonia", "{01F231DE-4A65-435F-B4BB-77EE5221890C}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileTime.GuiApp", "GuiApp\Avalonia\FileTime.GuiApp\FileTime.GuiApp.csproj", "{C389087E-EB78-4DCD-96AF-F1E2A4DEE0B0}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Providers", "Providers", "{2FC40FE1-4446-44AB-BF77-00F94D995FA3}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileTime.Providers.Local", "Providers\FileTime.Providers.Local\FileTime.Providers.Local.csproj", "{EA3C5B46-3950-4CD6-8FA9-45E17C63DAFA}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileTime.App.DependencyInjection", "AppCommon\FileTime.App.DependencyInjection\FileTime.App.DependencyInjection.csproj", "{A55F4A86-03ED-4D25-807A-04619A1D9507}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {99562652-F633-4496-9932-AF8E614906C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {99562652-F633-4496-9932-AF8E614906C4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {99562652-F633-4496-9932-AF8E614906C4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {99562652-F633-4496-9932-AF8E614906C4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E4681ADE-BAF7-4903-BD2F-59C144AE5637}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E4681ADE-BAF7-4903-BD2F-59C144AE5637}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E4681ADE-BAF7-4903-BD2F-59C144AE5637}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E4681ADE-BAF7-4903-BD2F-59C144AE5637}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5DEA3A08-62FF-4508-B755-3DABA96FC2B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5DEA3A08-62FF-4508-B755-3DABA96FC2B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5DEA3A08-62FF-4508-B755-3DABA96FC2B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5DEA3A08-62FF-4508-B755-3DABA96FC2B2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C389087E-EB78-4DCD-96AF-F1E2A4DEE0B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C389087E-EB78-4DCD-96AF-F1E2A4DEE0B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C389087E-EB78-4DCD-96AF-F1E2A4DEE0B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C389087E-EB78-4DCD-96AF-F1E2A4DEE0B0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EA3C5B46-3950-4CD6-8FA9-45E17C63DAFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EA3C5B46-3950-4CD6-8FA9-45E17C63DAFA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EA3C5B46-3950-4CD6-8FA9-45E17C63DAFA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EA3C5B46-3950-4CD6-8FA9-45E17C63DAFA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A55F4A86-03ED-4D25-807A-04619A1D9507}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A55F4A86-03ED-4D25-807A-04619A1D9507}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A55F4A86-03ED-4D25-807A-04619A1D9507}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A55F4A86-03ED-4D25-807A-04619A1D9507}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {99562652-F633-4496-9932-AF8E614906C4} = {3324D046-1E05-46B5-B1BA-82910D56B332}
+ {E4681ADE-BAF7-4903-BD2F-59C144AE5637} = {3324D046-1E05-46B5-B1BA-82910D56B332}
+ {5DEA3A08-62FF-4508-B755-3DABA96FC2B2} = {3324D046-1E05-46B5-B1BA-82910D56B332}
+ {01F231DE-4A65-435F-B4BB-77EE5221890C} = {8ADCA794-440F-4E8F-B9F2-2A30E54119A7}
+ {C389087E-EB78-4DCD-96AF-F1E2A4DEE0B0} = {01F231DE-4A65-435F-B4BB-77EE5221890C}
+ {EA3C5B46-3950-4CD6-8FA9-45E17C63DAFA} = {2FC40FE1-4446-44AB-BF77-00F94D995FA3}
+ {A55F4A86-03ED-4D25-807A-04619A1D9507} = {A5291117-3001-498B-AC8B-E14F71F72570}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {859FB3DF-C60A-46B1-82E5-90274905D1EF}
+ EndGlobalSection
+EndGlobal
diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/App.axaml b/src/GuiApp/Avalonia/FileTime.GuiApp/App.axaml
new file mode 100644
index 0000000..a325e5c
--- /dev/null
+++ b/src/GuiApp/Avalonia/FileTime.GuiApp/App.axaml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/App.axaml.cs b/src/GuiApp/Avalonia/FileTime.GuiApp/App.axaml.cs
new file mode 100644
index 0000000..7a7883f
--- /dev/null
+++ b/src/GuiApp/Avalonia/FileTime.GuiApp/App.axaml.cs
@@ -0,0 +1,29 @@
+using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Markup.Xaml;
+using FileTime.GuiApp.ViewModels;
+using FileTime.GuiApp.Views;
+
+namespace FileTime.GuiApp
+{
+ public partial class App : Application
+ {
+ public override void Initialize()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ public override void OnFrameworkInitializationCompleted()
+ {
+ if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+ {
+ desktop.MainWindow = new MainWindow
+ {
+ DataContext = new MainWindowViewModel(),
+ };
+ }
+
+ base.OnFrameworkInitializationCompleted();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/Assets/filetime.ico b/src/GuiApp/Avalonia/FileTime.GuiApp/Assets/filetime.ico
new file mode 100644
index 0000000..217f28f
Binary files /dev/null and b/src/GuiApp/Avalonia/FileTime.GuiApp/Assets/filetime.ico differ
diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/FileTime.GuiApp.csproj b/src/GuiApp/Avalonia/FileTime.GuiApp/FileTime.GuiApp.csproj
new file mode 100644
index 0000000..a4ccf87
--- /dev/null
+++ b/src/GuiApp/Avalonia/FileTime.GuiApp/FileTime.GuiApp.csproj
@@ -0,0 +1,36 @@
+
+
+ WinExe
+ net6.0
+ enable
+
+ copyused
+ true
+ Assets\filetime.ico
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/Program.cs b/src/GuiApp/Avalonia/FileTime.GuiApp/Program.cs
new file mode 100644
index 0000000..1c8c973
--- /dev/null
+++ b/src/GuiApp/Avalonia/FileTime.GuiApp/Program.cs
@@ -0,0 +1,23 @@
+using System;
+using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.ReactiveUI;
+
+namespace FileTime.GuiApp
+{
+ class Program
+ {
+ // Initialization code. Don't use any Avalonia, third-party APIs or any
+ // SynchronizationContext-reliant code before AppMain is called: things aren't initialized
+ // yet and stuff might break.
+ [STAThread]
+ public static void Main(string[] args) => BuildAvaloniaApp()
+ .StartWithClassicDesktopLifetime(args);
+
+ // Avalonia configuration, don't remove; also used by visual designer.
+ public static AppBuilder BuildAvaloniaApp()
+ => AppBuilder.Configure()
+ .UsePlatformDetect()
+ .LogToTrace();
+ }
+}
diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/ViewModels/MainWindowViewModel.cs b/src/GuiApp/Avalonia/FileTime.GuiApp/ViewModels/MainWindowViewModel.cs
new file mode 100644
index 0000000..76038d6
--- /dev/null
+++ b/src/GuiApp/Avalonia/FileTime.GuiApp/ViewModels/MainWindowViewModel.cs
@@ -0,0 +1,10 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace FileTime.GuiApp.ViewModels
+{
+ public class MainWindowViewModel
+ {
+ }
+}
diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/Views/MainWindow.axaml b/src/GuiApp/Avalonia/FileTime.GuiApp/Views/MainWindow.axaml
new file mode 100644
index 0000000..71e0c5b
--- /dev/null
+++ b/src/GuiApp/Avalonia/FileTime.GuiApp/Views/MainWindow.axaml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/Views/MainWindow.axaml.cs b/src/GuiApp/Avalonia/FileTime.GuiApp/Views/MainWindow.axaml.cs
new file mode 100644
index 0000000..181d0c0
--- /dev/null
+++ b/src/GuiApp/Avalonia/FileTime.GuiApp/Views/MainWindow.axaml.cs
@@ -0,0 +1,12 @@
+using Avalonia.Controls;
+
+namespace FileTime.GuiApp.Views
+{
+ public partial class MainWindow : Window
+ {
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Providers/FileTime.Providers.Local/FileTime.Providers.Local.csproj b/src/Providers/FileTime.Providers.Local/FileTime.Providers.Local.csproj
new file mode 100644
index 0000000..4c5b018
--- /dev/null
+++ b/src/Providers/FileTime.Providers.Local/FileTime.Providers.Local.csproj
@@ -0,0 +1,19 @@
+
+
+
+ net6.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Providers/FileTime.Providers.Local/LocalContentProvider.cs b/src/Providers/FileTime.Providers.Local/LocalContentProvider.cs
new file mode 100644
index 0000000..0f9347f
--- /dev/null
+++ b/src/Providers/FileTime.Providers.Local/LocalContentProvider.cs
@@ -0,0 +1,99 @@
+using System.Runtime.InteropServices;
+using FileTime.Core.Enums;
+using FileTime.Core.Models;
+using FileTime.Core.Services;
+
+namespace FileTime.Providers.Local
+{
+ public class LocalContentProvider : ContentProviderBase
+ {
+ protected bool IsCaseInsensitive { get; init; }
+ public LocalContentProvider() : base("local")
+ {
+ IsCaseInsensitive = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
+ RefreshRootDirectories();
+ }
+
+ public override Task OnEnter()
+ {
+ RefreshRootDirectories();
+
+ return Task.CompletedTask;
+ }
+
+ private void RefreshRootDirectories()
+ {
+ var rootDirectories = RuntimeInformation.IsOSPlatform(OSPlatform.Linux)
+ ? new DirectoryInfo("/").GetDirectories()
+ : Environment.GetLogicalDrives().Select(d => new DirectoryInfo(d));
+
+ Items.Clear();
+ Items.AddRange(rootDirectories.Select(DirectoryToAbsolutePath));
+ }
+
+ public override Task GetItemByNativePathAsync(NativePath nativePath)
+ {
+ var path = nativePath.Path;
+ if (Directory.Exists(path))
+ {
+ return Task.FromResult((IItem)DirectoryToContainer(new DirectoryInfo(path)));
+ }
+ else if (File.Exists(path))
+ {
+ return Task.FromResult((IItem)FileToElement(new FileInfo(path)));
+ }
+
+ throw new FileNotFoundException();
+ }
+
+ public override Task> GetItemsByContainerAsync(FullName fullName) => Task.FromResult(GetItemsByContainer(fullName));
+ public List GetItemsByContainer(FullName fullName) => GetItemsByContainer(new DirectoryInfo(GetNativePath(fullName).Path));
+ public List GetItemsByContainer(DirectoryInfo directoryInfo) => directoryInfo.GetDirectories().Select(DirectoryToAbsolutePath).Concat(directoryInfo.GetFiles().Select(FileToAbsolutePath)).ToList();
+
+ private IAbsolutePath DirectoryToAbsolutePath(DirectoryInfo directoryInfo)
+ {
+ var fullName = GetFullName(directoryInfo);
+ return new AbsolutePath(this, fullName, AbsolutePathType.Container);
+ }
+
+ private IAbsolutePath FileToAbsolutePath(FileInfo file)
+ {
+ var fullName = GetFullName(file);
+ return new AbsolutePath(this, fullName, AbsolutePathType.Element);
+ }
+
+ private Container DirectoryToContainer(DirectoryInfo directoryInfo) =>
+ new(
+ directoryInfo.Name,
+ directoryInfo.Name,
+ GetFullName(directoryInfo.FullName),
+ new(directoryInfo.FullName),
+ (directoryInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden,
+ directoryInfo.Exists,
+ SupportsDelete.True,
+ true,
+ this,
+ GetItemsByContainer(directoryInfo)
+ );
+
+ private Element FileToElement(FileInfo fileInfo) =>
+ new(
+ fileInfo.Name,
+ fileInfo.Name,
+ GetFullName(fileInfo),
+ new(fileInfo.FullName),
+ (fileInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden,
+ fileInfo.Exists,
+ SupportsDelete.True,
+ true,
+ this
+ );
+
+ private FullName GetFullName(DirectoryInfo directoryInfo) => GetFullName(directoryInfo.FullName);
+ private FullName GetFullName(FileInfo fileInfo) => GetFullName(fileInfo.FullName);
+ private FullName GetFullName(NativePath nativePath) => GetFullName(nativePath.Path);
+ private FullName GetFullName(string nativePath) => new(Name + Constants.SeparatorChar + string.Join(Constants.SeparatorChar, nativePath.Split(Path.DirectorySeparatorChar)));
+
+ public override NativePath GetNativePath(FullName fullName) => new(string.Join(Path.DirectorySeparatorChar, fullName.Path.Split(Constants.SeparatorChar).Skip(1)));
+ }
+}
\ No newline at end of file
diff --git a/src/Providers/FileTime.Providers.Local/Startup.cs b/src/Providers/FileTime.Providers.Local/Startup.cs
new file mode 100644
index 0000000..f01f4da
--- /dev/null
+++ b/src/Providers/FileTime.Providers.Local/Startup.cs
@@ -0,0 +1,15 @@
+using FileTime.Core.Services;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace FileTime.Providers.Local
+{
+ public static class Startup
+ {
+ public static IServiceCollection AddLocalServices(this IServiceCollection serviceCollection)
+ {
+ return serviceCollection
+ .AddSingleton()
+ .AddSingleton(sp => sp.GetService() ?? throw new Exception($"No {nameof(LocalContentProvider)} instance found"));
+ }
+ }
+}
\ No newline at end of file