diff --git a/README.md b/README.md index 77ce194..846a150 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@
- - Logo + + Logo

Pip Manager

@@ -14,11 +14,11 @@ Explore the docs (Chinese Simplified) »

- Screenshots + Screenshots · - Report Bug + Report Bug · - Request Feature + Request Feature

@@ -70,13 +70,11 @@ Double click `PipManager.exe` or `PipManager_withRuntime.exe` *If you have not i ### Arguments - `/console`: Show console while program running -- `/experiment`: Enable a secret page for test *Currently this feature is useless*

(back to top)

See the [Open Issues](https://github.com/Pip-Manager/PipManager.Wpf/issues) for a full list of proposed features (and known issues). - ## Contributing 1. Fork the Project diff --git a/src/App.xaml.cs b/src/App.xaml.cs index f25a77f..4b4f7a8 100644 --- a/src/App.xaml.cs +++ b/src/App.xaml.cs @@ -11,7 +11,6 @@ using PipManager.Core.Services.PackageSearchService; using PipManager.Windows.Services; using PipManager.Windows.Services.Action; -using PipManager.Windows.Services.Configuration; using PipManager.Windows.Services.Environment; using PipManager.Windows.Services.Mask; using PipManager.Windows.Services.Overlay; @@ -39,9 +38,6 @@ namespace PipManager.Windows; using AboutViewModel = ViewModels.Pages.About.AboutViewModel; -using ActionViewModel = ViewModels.Pages.Action.ActionViewModel; -using LibraryViewModel = ViewModels.Pages.Library.LibraryViewModel; -using SearchViewModel = ViewModels.Pages.Search.SearchViewModel; using SettingsViewModel = ViewModels.Pages.Settings.SettingsViewModel; /// @@ -78,7 +74,6 @@ public partial class App services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); @@ -141,7 +136,7 @@ public static T GetService() private static partial void FreeConsole(); private bool _showConsoleWindow; - private bool _experimentMode; + public static bool IsDebugMode { get; private set; } /// /// Occurs when the application is loading. @@ -157,10 +152,6 @@ private void OnStartup(object sender, StartupEventArgs e) case "/debug": _showConsoleWindow = true; break; - - case "/experiment": - _experimentMode = true; - break; } } var appStarting = new AppStarting @@ -169,13 +160,9 @@ private void OnStartup(object sender, StartupEventArgs e) }; appStarting.StartLogging(); appStarting.LoadLanguage(); - appStarting.LogDeletion(); - appStarting.CrushesDeletion(); appStarting.CachesDeletion(); Host.Start(); - GetService().DebugMode = _showConsoleWindow; - GetService().ExperimentMode = _experimentMode; - GetService().ExperimentMode = _experimentMode; + IsDebugMode = _showConsoleWindow; } /// diff --git a/src/AppInfo.cs b/src/AppInfo.cs index e0cfabe..44b1d8c 100644 --- a/src/AppInfo.cs +++ b/src/AppInfo.cs @@ -7,7 +7,7 @@ public static class AppInfo { public static readonly string AppVersion = Assembly.GetExecutingAssembly().GetName().Version!.ToString(3); - public static readonly string ConfigPath = Path.Combine(Directory.GetCurrentDirectory(), "config.json"); + public static readonly string ConfigDirectory = Directory.GetCurrentDirectory(); public static readonly string CrushesDir = Path.Combine(Directory.GetCurrentDirectory(), "crashes"); public static readonly string LogDir = Path.Combine(Directory.GetCurrentDirectory(), "logs"); public static readonly string CachesDir = Path.Combine(Directory.GetCurrentDirectory(), "caches"); diff --git a/src/AppStarting.cs b/src/AppStarting.cs index 9564176..2f92515 100644 --- a/src/AppStarting.cs +++ b/src/AppStarting.cs @@ -1,10 +1,8 @@ -using PipManager.Windows.Services.Configuration; -using Serilog; +using Serilog; using System.Globalization; using System.IO; using System.Runtime.InteropServices; -using PipManager.Windows.Helpers; -using PipManager.Windows.Models; +using PipManager.Core.Configuration; namespace PipManager.Windows; @@ -14,12 +12,11 @@ public partial class AppStarting [return: MarshalAs(UnmanagedType.Bool)] private static partial void AllocConsole(); - private readonly AppConfig _config; public bool ShowConsoleWindow = false; public AppStarting() { - _config = ConfigurationService.LoadConfiguration(); + Configuration.Initialize(AppInfo.ConfigDirectory); Directory.CreateDirectory(AppInfo.CrushesDir); Directory.CreateDirectory(AppInfo.LogDir); Directory.CreateDirectory(AppInfo.CachesDir); @@ -27,7 +24,7 @@ public AppStarting() public void LoadLanguage() { - var language = _config.Personalization.Language; + var language = Configuration.AppConfig!.Personalization.Language; if (language != "Auto") { I18NExtension.Culture = new CultureInfo(language); @@ -40,80 +37,16 @@ public void StartLogging() if (ShowConsoleWindow) { AllocConsole(); - Log.Logger = new LoggerConfiguration() - .Enrich.WithProperty("Version", AppInfo.AppVersion) - .MinimumLevel.Debug() - .WriteTo.Console(outputTemplate: "{Timestamp:HH:mm:ss} [{Level}] (Thread: {ThreadId}) {Message}{NewLine}{Exception}") - .Enrich.With(new ThreadIdEnricher()) - .WriteTo.File(Path.Combine(AppInfo.LogDir, $"log_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.txt"), outputTemplate: "{Timestamp:HH:mm:ss} [{Level}] (Thread: {ThreadId}) {Message}{NewLine}{Exception}") - .CreateLogger(); - } - else - { - Log.Logger = new LoggerConfiguration() - .Enrich.WithProperty("Version", AppInfo.AppVersion) - .MinimumLevel.Debug() - .Enrich.With(new ThreadIdEnricher()) - .WriteTo.File(Path.Combine(AppInfo.LogDir, $"log_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.txt"), outputTemplate: "{Timestamp:HH:mm:ss} [{Level}] (Thread: {ThreadId}) {Message}{NewLine}{Exception}") - .CreateLogger(); } + Log.Logger = new LoggerConfiguration() + .Enrich.WithProperty("Version", AppInfo.AppVersion) + .MinimumLevel.Debug() + .WriteTo.Console(outputTemplate: "{Timestamp:HH:mm:ss} [{Level}] {Message}{NewLine}{Exception}") + .WriteTo.File(Path.Combine(AppInfo.LogDir, "log_.txt"), outputTemplate: "{Timestamp:HH:mm:ss} [{Level}] {Message}{NewLine}{Exception}", rollingInterval: RollingInterval.Day, rollOnFileSizeLimit: true) + .CreateLogger(); Log.Information("Logging started"); } - public void LogDeletion() - { - if (!_config.Personalization.LogAutoDeletion || !Directory.Exists(AppInfo.LogDir)) return; - var fileList = Directory.GetFileSystemEntries(AppInfo.LogDir); - var logFileAmount = fileList.Count(file => File.Exists(file) && file.EndsWith(".txt")); - if (logFileAmount < _config.Personalization.LogAutoDeletionTimes) - { - return; - } - - var directoryInfo = new DirectoryInfo(AppInfo.LogDir); - var filesInfo = directoryInfo.GetFileSystemInfos(); - foreach (var file in filesInfo) - { - if (file.Extension != ".txt") continue; - try - { - File.Delete(file.FullName); - } - catch - { - Log.Warning("Failed to delete log: {FileFullName}", file.FullName); - } - } - Log.Information($"{logFileAmount} log file(s) deleted"); - } - - public void CrushesDeletion() - { - if (!_config.Personalization.CrushesAutoDeletion || !Directory.Exists(AppInfo.CrushesDir)) return; - var fileList = Directory.GetFileSystemEntries(AppInfo.CrushesDir); - var crushFileAmount = fileList.Count(file => File.Exists(file) && file.EndsWith(".txt")); - if (crushFileAmount < _config.Personalization.CrushesAutoDeletionTimes) - { - return; - } - - var directoryInfo = new DirectoryInfo(AppInfo.CrushesDir); - var filesInfo = directoryInfo.GetFileSystemInfos(); - foreach (var file in filesInfo) - { - if (file.Extension != ".txt") continue; - try - { - File.Delete(file.FullName); - } - catch - { - Log.Warning("Failed to delete crush file: {FileFullName}", file.FullName); - } - } - Log.Information($"{crushFileAmount} crush file(s) deleted"); - } - public void CachesDeletion() { if (!Directory.Exists(AppInfo.CachesDir)) return; diff --git a/src/Converters/PackageSourceEnumToBooleanConverter.cs b/src/Converters/PackageSourceEnumToBooleanConverter.cs index 7bfb28c..553934d 100644 --- a/src/Converters/PackageSourceEnumToBooleanConverter.cs +++ b/src/Converters/PackageSourceEnumToBooleanConverter.cs @@ -8,28 +8,35 @@ internal class PackageSourceEnumToBooleanConverter : IValueConverter { public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) { - if (parameter is not string enumString) + if (value == null || parameter == null) { - throw new ArgumentException("ExceptionPackageSourceTypeToBooleanConverterParameterMustBeAnEnumName"); + return false; } - if (value != null && !Enum.IsDefined(typeof(PackageSourceType), value)) + var currentPackageSource = (string)value; + var currentParameter = (string)parameter; + + if (currentPackageSource == "default" && currentParameter == "Official") { - throw new ArgumentException("ExceptionPackageSourceTypeToBooleanConverterValueMustBeAnEnum"); + return true; } - - var enumValue = Enum.Parse(typeof(PackageSourceType), enumString); - - return enumValue.Equals(value); + return currentPackageSource.Equals(currentParameter, StringComparison.OrdinalIgnoreCase); } public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) { - if (parameter is not string enumString) + if (value == null || parameter == null) { - throw new ArgumentException("ExceptionPackageSourceTypeToBooleanConverterParameterMustBeAnEnumName"); + return "default"; } - return Enum.Parse(typeof(PackageSourceType), enumString); + var isChecked = (bool)value; + var currentParameter = (string)parameter; + + if (isChecked) + { + return currentParameter == "Official" ? "default" : currentParameter; + } + return "default"; } } \ No newline at end of file diff --git a/src/Helpers/PackageValidator.cs b/src/Helpers/PackageValidator.cs deleted file mode 100644 index c776797..0000000 --- a/src/Helpers/PackageValidator.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Text.RegularExpressions; -using PipManager.Windows.Models.Package; - -namespace PipManager.Windows.Helpers; - -public static partial class PackageValidator -{ - public static PackageVersion CheckVersion(string version) - { - var match = VersionRegex().Match(version); - if (match.Success) - { - return new PackageVersion - { - Epoch = match.Groups["epoch"].Value, - Release = match.Groups["release"].Value, - PreL = match.Groups["pre_l"].Value, - PreN = match.Groups["pre_n"].Value, - PostN1 = match.Groups["post_n1"].Value, - PostL = match.Groups["post_l"].Value, - PostN2 = match.Groups["post_n2"].Value, - DevL = match.Groups["dev_l"].Value, - DevN = match.Groups["dev_n"].Value, - Local = match.Groups["local"].Value - }; - } - - return new PackageVersion(); - } - - public static bool IsReleaseVersion(string version) - { - var match = VersionRegex().Match(version); - if (match.Success) - { - return string.IsNullOrEmpty(match.Groups["pre_l"].Value) && string.IsNullOrEmpty(match.Groups["post_l"].Value) && string.IsNullOrEmpty(match.Groups["dev_l"].Value); - } - - return false; - } - - [GeneratedRegex( - "^\\s*\r\n v?\r\n (?:\r\n (?:(?[0-9]+)!)?\r\n (?[0-9]+(?:\\.[0-9]+)*)\r\n (?
\r\n                    [-_\\.]?\r\n                    (?(a|b|c|rc|alpha|beta|pre|preview))\r\n                    [-_\\.]?\r\n                    (?[0-9]+)?\r\n                )?\r\n                (?\r\n                    (?:-(?[0-9]+))\r\n                    |\r\n                    (?:\r\n                        [-_\\.]?\r\n                        (?post|rev|r)\r\n                        [-_\\.]?\r\n                        (?[0-9]+)?\r\n                    )\r\n                )?\r\n                (?\r\n                    [-_\\.]?\r\n                    (?dev)\r\n                    [-_\\.]?\r\n                    (?[0-9]+)?\r\n                )?\r\n            )\r\n            (?:\\+(?[a-z0-9]+(?:[-_\\.][a-z0-9]+)*))?\\s*$",
-        RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace)]
-    private static partial Regex VersionRegex();
-}
\ No newline at end of file
diff --git a/src/Helpers/ThreadIdEnricher.cs b/src/Helpers/ThreadIdEnricher.cs
deleted file mode 100644
index d935a43..0000000
--- a/src/Helpers/ThreadIdEnricher.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using Serilog.Core;
-using Serilog.Events;
-
-namespace PipManager.Windows.Helpers;
-
-internal class ThreadIdEnricher : ILogEventEnricher
-{
-    public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
-    {
-        logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty(
-            "ThreadId", Environment.CurrentManagedThreadId));
-    }
-}
\ No newline at end of file
diff --git a/src/Models/AppConfig.cs b/src/Models/AppConfig.cs
deleted file mode 100644
index c0984ba..0000000
--- a/src/Models/AppConfig.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Newtonsoft.Json;
-using PipManager.Windows.Models.AppConfigModels;
-
-namespace PipManager.Windows.Models;
-
-public class AppConfig
-{
-    [JsonProperty("currentEnvironment")] public EnvironmentItem? CurrentEnvironment { get; set; }
-    [JsonProperty("environments")] public List EnvironmentItems { get; set; } = [];
-    [JsonProperty("packageSource")] public PackageSource PackageSource { get; set; } = new();
-    [JsonProperty("personalization")] public Personalization Personalization { get; set; } = new();
-}
\ No newline at end of file
diff --git a/src/Models/AppConfigModels/EnvironmentItem.cs b/src/Models/AppConfigModels/EnvironmentItem.cs
deleted file mode 100644
index 8eaf886..0000000
--- a/src/Models/AppConfigModels/EnvironmentItem.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using Newtonsoft.Json;
-
-namespace PipManager.Windows.Models.AppConfigModels;
-
-public class EnvironmentItem
-{
-    public EnvironmentItem()
-    {
-    }
-
-    public EnvironmentItem(string pipVersion, string pythonPath, string pythonVersion, string pythonDllPath)
-    {
-        PipVersion = pipVersion;
-        PythonPath = pythonPath;
-        PythonVersion = pythonVersion;
-        PythonDllPath = pythonDllPath;
-    }
-
-    [JsonProperty("pipVersion")] public string? PipVersion { get; set; }
-    [JsonProperty("pythonPath")] public string? PythonPath { get; set; }
-    [JsonProperty("pythonVersion")] public string? PythonVersion { get; set; }
-    [JsonProperty("pythonDllPath")] public string? PythonDllPath { get; set; }
-}
\ No newline at end of file
diff --git a/src/Models/AppConfigModels/PackageSource.cs b/src/Models/AppConfigModels/PackageSource.cs
deleted file mode 100644
index e0b45f5..0000000
--- a/src/Models/AppConfigModels/PackageSource.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using Newtonsoft.Json;
-using PipManager.Windows.Models.Package;
-
-namespace PipManager.Windows.Models.AppConfigModels;
-
-public class PackageSource
-{
-    [JsonProperty("packageSourceType")] public PackageSourceType PackageSourceType { get; set; } = PackageSourceType.Official;
-    [JsonProperty("detectNonReleaseVersion")] public bool DetectNonReleaseVersion { get; set; }
-}
\ No newline at end of file
diff --git a/src/Models/AppConfigModels/Personalization.cs b/src/Models/AppConfigModels/Personalization.cs
deleted file mode 100644
index 3a21720..0000000
--- a/src/Models/AppConfigModels/Personalization.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using Newtonsoft.Json;
-
-namespace PipManager.Windows.Models.AppConfigModels;
-
-public class Personalization
-{
-    [JsonProperty("language")]
-    public string Language { get; set; } = "Auto";
-
-    [JsonProperty("theme")]
-    public string Theme { get; set; } = "dark";
-
-    [JsonProperty("logAutoDeletion")]
-    public bool LogAutoDeletion { get; set; } = true;
-
-    [JsonProperty("logAutoDeletionTimes")]
-    public int LogAutoDeletionTimes { get; set; } = 7;
-
-    [JsonProperty("crushesAutoDeletion")]
-    public bool CrushesAutoDeletion { get; set; } = true;
-
-    [JsonProperty("crushesAutoDeletionTimes")]
-    public int CrushesAutoDeletionTimes { get; set; } = 7;
-}
\ No newline at end of file
diff --git a/src/Models/Package/PackageItem.cs b/src/Models/Package/PackageItem.cs
index 47353bb..7e24dcf 100644
--- a/src/Models/Package/PackageItem.cs
+++ b/src/Models/Package/PackageItem.cs
@@ -1,4 +1,5 @@
-using PipManager.Windows.Models.Pages;
+using PipManager.Core.PyPackage.Models;
+using PipManager.Windows.Models.Pages;
 
 namespace PipManager.Windows.Models.Package;
 
diff --git a/src/Models/Package/PackageVersion.cs b/src/Models/Package/PackageVersion.cs
deleted file mode 100644
index 935ef15..0000000
--- a/src/Models/Package/PackageVersion.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace PipManager.Windows.Models.Package;
-
-public class PackageVersion
-{
-    public string Epoch { get; set; } = "";
-    public string Release { get; set; } = "";
-    public string PreL { get; set; } = "";
-    public string PreN { get; set; } = "";
-    public string PostN1 { get; set; } = "";
-    public string PostL { get; set; } = "";
-    public string PostN2 { get; set; } = "";
-    public string DevL { get; set; } = "";
-    public string DevN { get; set; } = "";
-    public string Local { get; set; } = "";
-}
\ No newline at end of file
diff --git a/src/Models/Pages/LibraryListItem.cs b/src/Models/Pages/LibraryListItem.cs
index 4b3b6f4..1c2034d 100644
--- a/src/Models/Pages/LibraryListItem.cs
+++ b/src/Models/Pages/LibraryListItem.cs
@@ -1,4 +1,4 @@
-using PipManager.Windows.Models.Package;
+using PipManager.Core.PyPackage.Models;
 using Wpf.Ui.Controls;
 
 namespace PipManager.Windows.Models.Pages;
diff --git a/src/Models/Pypi/PypiPackageInfo.cs b/src/Models/Pypi/PypiPackageInfo.cs
deleted file mode 100644
index b7d96d0..0000000
--- a/src/Models/Pypi/PypiPackageInfo.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using Newtonsoft.Json;
-
-namespace PipManager.Windows.Models.Pypi;
-
-public class PypiPackageInfo
-{
-    [JsonProperty("releases")]
-    public Dictionary>? Releases;
-}
-
-public class PypiPackageRelease
-{
-    [JsonProperty("filename")]
-    public string? Filename;
-
-    [JsonProperty("upload_time")]
-    public string? UploadTime;
-
-    [JsonProperty("digests")]
-    public PypiPackageDigest? Digests;
-}
-
-public class PypiPackageDigest
-{
-    [JsonProperty("blake2b_256")]
-    public string? Blake2B256;
-
-    [JsonProperty("md5")]
-    public string? Md5;
-
-    [JsonProperty("sha256")]
-    public string? Sha256;
-}
\ No newline at end of file
diff --git a/src/PipManager.Windows.csproj b/src/PipManager.Windows.csproj
index f6f1c44..e05ef0e 100644
--- a/src/PipManager.Windows.csproj
+++ b/src/PipManager.Windows.csproj
@@ -29,18 +29,18 @@
 	
 		
 		
-		
-		
+		
+		
 		
-		
+		
 		
 		
 		
 		
-		
+		
 		
-		
-		
+		
+		
 		
 		
 	
diff --git a/src/Services/Configuration/ConfigurationService.cs b/src/Services/Configuration/ConfigurationService.cs
deleted file mode 100644
index 095c909..0000000
--- a/src/Services/Configuration/ConfigurationService.cs
+++ /dev/null
@@ -1,196 +0,0 @@
-using Newtonsoft.Json;
-using System.Diagnostics;
-using System.IO;
-using System.Text.RegularExpressions;
-using PipManager.Windows.Models;
-using PipManager.Windows.Models.AppConfigModels;
-using PipManager.Windows.Models.Package;
-
-namespace PipManager.Windows.Services.Configuration;
-
-public partial class ConfigurationService : IConfigurationService
-{
-    public AppConfig AppConfig { get; set; } = LoadConfiguration();
-    public bool ExperimentMode { get; set; }
-    public bool DebugMode { get; set; }
-
-    public static AppConfig LoadConfiguration()
-    {
-        if (!File.Exists(AppInfo.ConfigPath))
-        {
-            File.WriteAllText(AppInfo.ConfigPath, JsonConvert.SerializeObject(new AppConfig(), Formatting.Indented));
-        }
-        return JsonConvert.DeserializeObject(File.ReadAllText(AppInfo.ConfigPath))!;
-    }
-
-    public void Save()
-    {
-        File.WriteAllText(AppInfo.ConfigPath, JsonConvert.SerializeObject(AppConfig, Formatting.Indented));
-    }
-
-    #region Environments
-
-    public string FindPythonPathByPipDir(string pipDir)
-    {
-        // Need more information
-        var pipExePath = Path.Combine(new DirectoryInfo(pipDir).Parent!.Parent!.Parent!.FullName,
-            "python.exe");
-        var pipExePathAttempt1 = Path.Combine(new DirectoryInfo(pipDir).Parent!.Parent!.FullName,
-            "python.exe");
-        if (!File.Exists(pipExePath))
-        {
-            pipExePath = pipExePathAttempt1;
-        }
-
-        return pipExePath;
-    }
-
-    [GeneratedRegex("__version__ = \"(.*?)\"", RegexOptions.IgnoreCase, "zh-CN")]
-    private static partial Regex GetPipVersionInInitFile();
-
-    /// 
-    /// Get the pip directory
-    /// 
-    /// Python path
-    /// Pip directory if exists, otherwise null
-    private string? GetPipDirectories(string pythonDirectory)
-    {
-        var sitePackageDirectory = Path.Combine(pythonDirectory, @"Lib\site-packages");
-        if (!Directory.Exists(sitePackageDirectory))
-        {
-            return null;
-        }
-
-        var pipDirectory = Path.Combine(sitePackageDirectory, "pip");
-        if (!Directory.Exists(pipDirectory))
-        {
-            return null;
-        }
-
-        return pipDirectory;
-    }
-
-    public EnvironmentItem? GetEnvironmentItem(string pythonPath)
-    {
-        var pythonVersion = FileVersionInfo.GetVersionInfo(pythonPath).FileVersion!;
-        var pythonDirectory = Directory.GetParent(pythonPath)!;
-        var pipDirectory = GetPipDirectories(pythonDirectory.FullName);
-        var pythonDllName = $"{pythonDirectory.Name.ToLower().Replace(".", "")}.dll";
-        var pythonDllPath = pythonDirectory.GetFiles(pythonDllName, SearchOption.AllDirectories).FirstOrDefault();
-        if (pythonDllPath == null || pipDirectory == null)
-        {
-            return null;
-        }
-
-        var pipVersion = GetPipVersionInInitFile().Match(File.ReadAllText(Path.Combine(pipDirectory, @"__init__.py"))).Groups[1].Value;
-        return new EnvironmentItem(pipVersion, pythonPath, pythonVersion, pythonDllPath.FullName);
-    }
-
-    public EnvironmentItem? GetEnvironmentItemFromCommand(string command, string arguments)
-    {
-        var proc = new Process
-        {
-            StartInfo = new ProcessStartInfo
-            {
-                FileName = command,
-                Arguments = arguments,
-                UseShellExecute = false,
-                RedirectStandardOutput = true,
-                CreateNoWindow = true,
-                WindowStyle = ProcessWindowStyle.Hidden
-            }
-        };
-        try
-        {
-            proc.Start();
-        }
-        catch
-        {
-            return null;
-        }
-
-        var pipVersion = "";
-        var pythonVersion = "";
-        var pipDir = "";
-        while (!proc.StandardOutput.EndOfStream)
-        {
-            var output = proc.StandardOutput.ReadLine();
-            if (string.IsNullOrWhiteSpace(output)) continue;
-            var sections = output.Split(' ');
-            var pipDirStart = false;
-            for (var i = 0; i < sections.Length; i++)
-            {
-                if (sections[i] == "from")
-                {
-                    pipVersion = sections[i - 1];
-                    pipDirStart = true;
-                }
-                else if (sections[i] == "(python")
-                {
-                    pythonVersion = sections[i + 1].Replace(")", "");
-                    break;
-                }
-                else if (pipDirStart)
-                {
-                    pipDir += sections[i] + ' ';
-                }
-            }
-        }
-        pipVersion = pipVersion.Trim();
-        var pythonPath = FindPythonPathByPipDir(pipDir.Trim());
-        var pythonDirectory = Directory.GetParent(pythonPath)!;
-        var pythonDllName = $"{pythonDirectory.Name.ToLower().Replace(".", "")}.dll";
-        var pythonDllPath = pythonDirectory.GetFiles(pythonDllName, SearchOption.AllDirectories).FirstOrDefault();
-        pythonVersion = pythonVersion.Trim();
-        proc.Close();
-        return pipDir.Length > 0 && pythonDllPath != null ? new EnvironmentItem(pipVersion, pythonPath, pythonVersion, pythonDllPath.FullName) : null;
-    }
-
-    public void RefreshAllEnvironmentVersions()
-    {
-        foreach (var item in AppConfig.EnvironmentItems)
-        {
-            var environmentItem = GetEnvironmentItem(item.PythonPath!);
-            if (environmentItem != null)
-            {
-                item.PipVersion = environmentItem.PipVersion;
-            }
-        }
-
-        Save();
-    }
-
-    #endregion Environments
-
-    #region Settings - Package Source
-
-    public string GetUrlFromPackageSourceType(string index = "simple")
-    {
-        return AppConfig.PackageSource.PackageSourceType switch
-        {
-            PackageSourceType.Official => $"https://pypi.org/{index}/",
-            PackageSourceType.Tsinghua => $"https://pypi.tuna.tsinghua.edu.cn/{index}/",
-            PackageSourceType.Aliyun => $"https://mirrors.aliyun.com/pypi/{index switch
-            {
-                "pypi" => "web/pypi/",
-                _ => index
-            }}/",
-            PackageSourceType.Douban => $"https://pypi.doubanio.com/{index}/",
-            _ => throw new ArgumentOutOfRangeException(nameof(AppConfig.PackageSource.PackageSourceType), AppConfig.PackageSource.PackageSourceType, null)
-        };
-    }
-
-    public string GetTestingUrlFromPackageSourceType(PackageSourceType packageSourceType)
-    {
-        return packageSourceType switch
-        {
-            PackageSourceType.Official => "https://files.pythonhosted.org/packages/62/35/0230421b8c4efad6624518028163329ad0c2df9e58e6b3bee013427bf8f6/requests-0.10.0.tar.gz",
-            PackageSourceType.Tsinghua => "https://pypi.tuna.tsinghua.edu.cn/packages/62/35/0230421b8c4efad6624518028163329ad0c2df9e58e6b3bee013427bf8f6/requests-0.10.0.tar.gz",
-            PackageSourceType.Aliyun => "https://mirrors.aliyun.com/pypi/packages/62/35/0230421b8c4efad6624518028163329ad0c2df9e58e6b3bee013427bf8f6/requests-0.10.0.tar.gz",
-            PackageSourceType.Douban => "https://mirrors.cloud.tencent.com/pypi/packages/62/35/0230421b8c4efad6624518028163329ad0c2df9e58e6b3bee013427bf8f6/requests-0.10.0.tar.gz",
-            _ => throw new ArgumentOutOfRangeException(nameof(packageSourceType), packageSourceType, null)
-        };
-    }
-
-    #endregion Settings - Package Source
-}
\ No newline at end of file
diff --git a/src/Services/Configuration/IConfigurationService.cs b/src/Services/Configuration/IConfigurationService.cs
deleted file mode 100644
index 354d647..0000000
--- a/src/Services/Configuration/IConfigurationService.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using PipManager.Windows.Models;
-using PipManager.Windows.Models.AppConfigModels;
-using PipManager.Windows.Models.Package;
-
-namespace PipManager.Windows.Services.Configuration;
-
-public interface IConfigurationService
-{
-    public AppConfig AppConfig { get; set; }
-    public bool DebugMode { get; set; }
-    public bool ExperimentMode { get; set; }
-
-    public string GetUrlFromPackageSourceType(string index = "simple");
-
-    public string GetTestingUrlFromPackageSourceType(PackageSourceType packageSourceType);
-
-    public void Save();
-
-    public string FindPythonPathByPipDir(string pipDir);
-
-    public EnvironmentItem? GetEnvironmentItem(string pythonPath);
-
-    public EnvironmentItem? GetEnvironmentItemFromCommand(string command, string arguments);
-
-    public void RefreshAllEnvironmentVersions();
-}
\ No newline at end of file
diff --git a/src/Services/Environment/EnvironmentService.cs b/src/Services/Environment/EnvironmentService.cs
index 911f71e..c71c575 100644
--- a/src/Services/Environment/EnvironmentService.cs
+++ b/src/Services/Environment/EnvironmentService.cs
@@ -1,49 +1,49 @@
-using Newtonsoft.Json;
-using Serilog;
-using System.Collections.Concurrent;
+using System.Collections.Concurrent;
 using System.Diagnostics;
 using System.IO;
 using System.Net.Http;
 using System.Text;
 using System.Text.RegularExpressions;
-using PipManager.Windows.Helpers;
+using Newtonsoft.Json;
+using PipManager.Core.Configuration;
+using PipManager.Core.Configuration.Models;
+using PipManager.Core.Extensions;
+using PipManager.Core.PyEnvironment.Helpers;
+using PipManager.Core.PyPackage.Helpers;
+using PipManager.Core.PyPackage.Models;
 using PipManager.Windows.Models;
-using PipManager.Windows.Models.AppConfigModels;
 using PipManager.Windows.Models.Package;
 using PipManager.Windows.Models.Pages;
-using PipManager.Windows.Models.Pypi;
-using PipManager.Windows.Services.Configuration;
 using PipManager.Windows.Services.Environment.Response;
+using Serilog;
 using Wpf.Ui.Controls;
 using Path = System.IO.Path;
 
 namespace PipManager.Windows.Services.Environment;
 
-public partial class EnvironmentService(IConfigurationService configurationService) : IEnvironmentService
+public partial class EnvironmentService(HttpClient httpClient) : IEnvironmentService
 {
-    private readonly HttpClient _httpClient = App.GetService();
-
-    public bool CheckEnvironmentExists(EnvironmentItem environmentItem)
+    public bool CheckEnvironmentExists(EnvironmentModel environmentModel)
     {
-        var environmentItems = configurationService.AppConfig.EnvironmentItems;
-        return environmentItems.Any(item => item.PythonPath == environmentItem.PythonPath);
+        var environmentItems = Configuration.AppConfig!.Environments;
+        return environmentItems.Any(item => item.PythonPath == environmentModel.PythonPath);
     }
 
-    public ActionResponse CheckEnvironmentAvailable(EnvironmentItem environmentItem)
+    public ActionResponse CheckEnvironmentAvailable(EnvironmentModel environmentModel)
     {
-        var verify = configurationService.GetEnvironmentItemFromCommand(environmentItem.PythonPath!, "-m pip -V");
-        return verify != null && environmentItem.PythonPath != string.Empty
+        var verify = WindowsSpecified.GetEnvironmentByCommand(environmentModel.PythonPath, "-m pip -V");
+        return verify != null && environmentModel.PythonPath != string.Empty
             ? new ActionResponse { Success = true }
             : new ActionResponse { Success = false, Exception = ExceptionType.EnvironmentBroken };
     }
 
-    public ActionResponse PurgeEnvironmentCache(EnvironmentItem environmentItem)
+    public ActionResponse PurgeEnvironmentCache(EnvironmentModel environmentModel)
     {
         var process = new Process
         {
             StartInfo = new ProcessStartInfo
             {
-                FileName = configurationService.AppConfig.CurrentEnvironment!.PythonPath,
+                FileName = Configuration.AppConfig!.SelectedEnvironment!.PythonPath,
                 Arguments = "-m pip cache purge",
                 UseShellExecute = false,
                 RedirectStandardOutput = true,
@@ -63,13 +63,13 @@ public ActionResponse PurgeEnvironmentCache(EnvironmentItem environmentItem)
 
     public async Task?> GetLibraries()
     {
-        if (configurationService.AppConfig.CurrentEnvironment is null)
+        if (Configuration.AppConfig!.SelectedEnvironment is null)
         {
             return null;
         }
 
         var packageDirInfo = new DirectoryInfo(Path.Combine(
-            Path.GetDirectoryName(configurationService.AppConfig.CurrentEnvironment!.PythonPath)!,
+            Path.GetDirectoryName(Configuration.AppConfig.SelectedEnvironment!.PythonPath)!,
             @"Lib\site-packages"));
         
         var packages = new ConcurrentBag();
@@ -239,11 +239,11 @@ public async Task GetVersions(string packageName, Cancellat
             if (!PackageNameVerificationRegex().IsMatch(packageName))
                 return new GetVersionsResponse { Status = 2, Versions = [] };
             var responseMessage =
-                await _httpClient.GetAsync(
-                    $"{configurationService.GetUrlFromPackageSourceType("pypi")}{packageName}/json", cancellationToken);
+                await httpClient.GetAsync(
+                    $"{Configuration.AppConfig!.PackageSource.Source.GetPackageSourceUrl("pypi")}{packageName}/json", cancellationToken);
             var response = await responseMessage.Content.ReadAsStringAsync(cancellationToken);
 
-            var pypiPackageInfo = JsonConvert.DeserializeObject(response)
+            var pypiPackageInfo = JsonConvert.DeserializeObject(response)
                 ?.Releases?
                 .Where(item => item.Value.Count != 0).OrderBy(e => e.Value[0].UploadTime)
                 .ThenBy(e => e.Value[0].UploadTime).ToDictionary(pair => pair.Key, pair => pair.Value);
@@ -308,7 +308,7 @@ private ActionResponse RaiseProcess(string arguments, DataReceivedEventHandler c
         {
             StartInfo = new ProcessStartInfo
             {
-                FileName = configurationService.AppConfig.CurrentEnvironment!.PythonPath,
+                FileName = Configuration.AppConfig!.SelectedEnvironment!.PythonPath,
                 Arguments = $"{arguments} {extra}",
                 UseShellExecute = false,
                 RedirectStandardOutput = true,
@@ -332,23 +332,23 @@ private ActionResponse RaiseProcess(string arguments, DataReceivedEventHandler c
     public ActionResponse Install(string packageName, DataReceivedEventHandler consoleOutputCallback,
         string[]? extraParameters = null)
         => RaiseProcess(
-            $"-m pip install \"{packageName}\" -i {configurationService.GetUrlFromPackageSourceType()} --retries 1 --timeout 6",
+            $"-m pip install \"{packageName}\" -i {Configuration.AppConfig!.PackageSource.Source.GetPackageSourceUrl()} --retries 1 --timeout 6",
             consoleOutputCallback, extraParameters);
     
     public ActionResponse InstallByRequirements(string requirementsFilePath,
         DataReceivedEventHandler consoleOutputCallback)
         => RaiseProcess(
-            $"-m pip install -r \"{requirementsFilePath}\" -i {configurationService.GetUrlFromPackageSourceType()} --retries 1 --timeout 6",
+            $"-m pip install -r \"{requirementsFilePath}\" -i {Configuration.AppConfig!.PackageSource.Source.GetPackageSourceUrl()} --retries 1 --timeout 6",
             consoleOutputCallback);
 
     public ActionResponse Download(string packageName, string downloadPath, DataReceivedEventHandler consoleOutputCallback, string[]? extraParameters = null)
         => RaiseProcess(
-            $"-m pip download -d \"{downloadPath}\" \"{packageName}\" -i {configurationService.GetUrlFromPackageSourceType()} --retries 1 --timeout 6",
+            $"-m pip download -d \"{downloadPath}\" \"{packageName}\" -i {Configuration.AppConfig!.PackageSource.Source.GetPackageSourceUrl()} --retries 1 --timeout 6",
             consoleOutputCallback, extraParameters);
 
     public ActionResponse Update(string packageName, DataReceivedEventHandler consoleOutputCallback)
         => RaiseProcess(
-            $"-m pip install --upgrade \"{packageName}\" -i {configurationService.GetUrlFromPackageSourceType()} --retries 1 --timeout 6",
+            $"-m pip install --upgrade \"{packageName}\" -i {Configuration.AppConfig!.PackageSource.Source.GetPackageSourceUrl()} --retries 1 --timeout 6",
             consoleOutputCallback);
 
     public ActionResponse Uninstall(string packageName, DataReceivedEventHandler consoleOutputCallback)
diff --git a/src/Services/Environment/IEnvironmentService.cs b/src/Services/Environment/IEnvironmentService.cs
index 022107e..28c7122 100644
--- a/src/Services/Environment/IEnvironmentService.cs
+++ b/src/Services/Environment/IEnvironmentService.cs
@@ -1,5 +1,5 @@
 using System.Diagnostics;
-using PipManager.Windows.Models.AppConfigModels;
+using PipManager.Core.Configuration.Models;
 using PipManager.Windows.Models.Package;
 using PipManager.Windows.Services.Environment.Response;
 
@@ -7,11 +7,11 @@ namespace PipManager.Windows.Services.Environment;
 
 public interface IEnvironmentService
 {
-    public bool CheckEnvironmentExists(EnvironmentItem environmentItem);
+    public bool CheckEnvironmentExists(EnvironmentModel environmentModel);
 
-    public ActionResponse CheckEnvironmentAvailable(EnvironmentItem environmentItem);
+    public ActionResponse CheckEnvironmentAvailable(EnvironmentModel environmentModel);
 
-    public ActionResponse PurgeEnvironmentCache(EnvironmentItem environmentItem);
+    public ActionResponse PurgeEnvironmentCache(EnvironmentModel environmentModel);
 
     public Task?> GetLibraries();
 
diff --git a/src/ViewModels/Pages/About/AboutViewModel.cs b/src/ViewModels/Pages/About/AboutViewModel.cs
index 6b693a8..459f643 100644
--- a/src/ViewModels/Pages/About/AboutViewModel.cs
+++ b/src/ViewModels/Pages/About/AboutViewModel.cs
@@ -1,18 +1,16 @@
 using Serilog;
 using System.Collections.ObjectModel;
 using PipManager.Windows.Models.Pages;
-using PipManager.Windows.Services.Configuration;
 using Wpf.Ui.Controls;
 
 namespace PipManager.Windows.ViewModels.Pages.About;
 
-public partial class AboutViewModel(IConfigurationService configurationService) : ObservableObject, INavigationAware
+public partial class AboutViewModel : ObservableObject, INavigationAware
 {
     private bool _isInitialized;
 
     [ObservableProperty] private string _appVersion = "Development";
     [ObservableProperty] private bool _debugMode;
-    [ObservableProperty] private bool _experimentMode;
 
     public void OnNavigatedTo()
     {
@@ -26,8 +24,7 @@ public void OnNavigatedFrom()
 
     private void InitializeViewModel()
     {
-        DebugMode = configurationService.DebugMode;
-        ExperimentMode = configurationService.ExperimentMode;
+        DebugMode = App.IsDebugMode;
         AppVersion = AppInfo.AppVersion;
         _isInitialized = true;
         NugetLibraryList =
diff --git a/src/ViewModels/Pages/Environment/AddEnvironmentViewModel.cs b/src/ViewModels/Pages/Environment/AddEnvironmentViewModel.cs
index aab27e3..6088e83 100644
--- a/src/ViewModels/Pages/Environment/AddEnvironmentViewModel.cs
+++ b/src/ViewModels/Pages/Environment/AddEnvironmentViewModel.cs
@@ -1,9 +1,11 @@
 using Microsoft.Win32;
 using Serilog;
 using System.IO;
+using PipManager.Core.Configuration;
+using PipManager.Core.Configuration.Models;
+using PipManager.Core.PyEnvironment;
+using PipManager.Core.PyEnvironment.Helpers;
 using PipManager.Windows.Languages;
-using PipManager.Windows.Models.AppConfigModels;
-using PipManager.Windows.Services.Configuration;
 using PipManager.Windows.Services.Environment;
 using PipManager.Windows.Services.Toast;
 using Wpf.Ui;
@@ -11,7 +13,7 @@
 
 namespace PipManager.Windows.ViewModels.Pages.Environment;
 
-public partial class AddEnvironmentViewModel(INavigationService navigationService, IConfigurationService configurationService, IEnvironmentService environmentService, IToastService toastService) : ObservableObject, INavigationAware
+public partial class AddEnvironmentViewModel(INavigationService navigationService, IEnvironmentService environmentService, IToastService toastService) : ObservableObject, INavigationAware
 {
     private bool _isInitialized;
 
@@ -48,10 +50,10 @@ private void ChangeWay()
     #region By Environment Variables
 
     [ObservableProperty]
-    private List _environmentItems = [];
+    private List _environmentItems = [];
 
     [ObservableProperty]
-    private EnvironmentItem? _environmentItemInList;
+    private EnvironmentModel? _environmentItemInList;
 
     [ObservableProperty]
     private bool _loading = true;
@@ -72,10 +74,10 @@ await Task.Run(() =>
             {
                 if (!File.Exists(Path.Combine(item, "python.exe")))
                     continue;
-                var environmentItem =
-                    configurationService.GetEnvironmentItem(Path.Combine(item, "python.exe"));
-                if (environmentItem == null) continue;
-                EnvironmentItems.Add(environmentItem);
+                var environmentModel =
+                    Detector.ByPythonPath(Path.Combine(item, "python.exe"));
+                if (environmentModel == null) continue;
+                EnvironmentItems.Add(environmentModel);
             }
         }).ContinueWith(_ => { Loading = false; Found = EnvironmentItems.Count == 0; Log.Information($"[AddEnvironment] Pip list in environment variable refreshed"); });
     }
@@ -144,9 +146,9 @@ private void AddEnvironment(string parameter)
                         }
                         else
                         {
-                            configurationService.AppConfig.CurrentEnvironment = EnvironmentItemInList;
-                            configurationService.AppConfig.EnvironmentItems.Add(EnvironmentItemInList);
-                            configurationService.Save();
+                            Configuration.AppConfig!.SelectedEnvironment = EnvironmentItemInList;
+                            Configuration.AppConfig.Environments.Add(EnvironmentItemInList);
+                            Configuration.Save();
                             Log.Information($"[AddEnvironment] Environment added ({EnvironmentItemInList.PipVersion} for {EnvironmentItemInList.PythonVersion})");
                             navigationService.GoBack();
                         }
@@ -160,7 +162,7 @@ private void AddEnvironment(string parameter)
                 }
             case 1:
                 {
-                    var result = configurationService.GetEnvironmentItemFromCommand(PipCommand, "-V");
+                    var result = WindowsSpecified.GetEnvironmentByCommand(PipCommand, "-V");
                     if (result != null)
                     {
                         var alreadyExists = environmentService.CheckEnvironmentExists(result);
@@ -170,10 +172,10 @@ private void AddEnvironment(string parameter)
                         }
                         else
                         {
-                            configurationService.AppConfig.CurrentEnvironment = result;
-                            configurationService.AppConfig.EnvironmentItems.Add(result);
+                            Configuration.AppConfig!.SelectedEnvironment = result;
+                            Configuration.AppConfig.Environments.Add(result);
                             Log.Information($"[AddEnvironment] Environment added ({result.PipVersion} for {result.PythonVersion})");
-                            configurationService.Save();
+                            Configuration.Save();
                             navigationService.GoBack();
                         }
                     }
@@ -186,7 +188,7 @@ private void AddEnvironment(string parameter)
                 }
             case 2:
                 {
-                    var result = configurationService.GetEnvironmentItem(PythonPath);
+                    var result = Detector.ByPythonPath(PythonPath);
                     if (result != null)
                     {
                         var alreadyExists = environmentService.CheckEnvironmentExists(result);
@@ -196,10 +198,10 @@ private void AddEnvironment(string parameter)
                         }
                         else
                         {
-                            configurationService.AppConfig.CurrentEnvironment = result;
-                            configurationService.AppConfig.EnvironmentItems.Add(result);
+                            Configuration.AppConfig!.SelectedEnvironment = result;
+                            Configuration.AppConfig.Environments.Add(result);
                             Log.Information($"[AddEnvironment] Environment added ({result.PipVersion} for {result.PythonVersion})");
-                            configurationService.Save();
+                            Configuration.Save();
                             navigationService.GoBack();
                         }
                     }
diff --git a/src/ViewModels/Pages/Environment/EnvironmentViewModel.cs b/src/ViewModels/Pages/Environment/EnvironmentViewModel.cs
index 6046a4c..63e8b06 100644
--- a/src/ViewModels/Pages/Environment/EnvironmentViewModel.cs
+++ b/src/ViewModels/Pages/Environment/EnvironmentViewModel.cs
@@ -1,12 +1,12 @@
 using Serilog;
 using System.Collections.ObjectModel;
 using System.ComponentModel;
+using PipManager.Core.Configuration;
+using PipManager.Core.Configuration.Models;
 using PipManager.Windows.Controls;
 using PipManager.Windows.Languages;
 using PipManager.Windows.Models.Action;
-using PipManager.Windows.Models.AppConfigModels;
 using PipManager.Windows.Services.Action;
-using PipManager.Windows.Services.Configuration;
 using PipManager.Windows.Services.Environment;
 using PipManager.Windows.Services.Mask;
 using PipManager.Windows.Services.Toast;
@@ -20,7 +20,7 @@
 namespace PipManager.Windows.ViewModels.Pages.Environment;
 
 public partial class EnvironmentViewModel(INavigationService navigationService,
-        IConfigurationService configurationService, IEnvironmentService environmentService,
+        IEnvironmentService environmentService,
         IActionService actionService, IMaskService maskService, IContentDialogService contentDialogService,
         IToastService toastService)
     : ObservableObject, INavigationAware
@@ -32,10 +32,10 @@ public void OnNavigatedTo()
         if (!_isInitialized)
             InitializeViewModel();
         
-        configurationService.RefreshAllEnvironmentVersions();
+        Configuration.RefreshAllEnvironments();
         EnvironmentItems =
-            new ObservableCollection(configurationService.AppConfig.EnvironmentItems);
-        var currentEnvironment = configurationService.AppConfig.CurrentEnvironment;
+            new ObservableCollection(Configuration.AppConfig!.Environments);
+        var currentEnvironment = Configuration.AppConfig.SelectedEnvironment;
         foreach (var environmentItem in EnvironmentItems)
         {
             if (currentEnvironment is null || environmentItem.PythonPath != currentEnvironment.PythonPath)
@@ -64,7 +64,7 @@ private void InitializeViewModel()
     }
 
     [ObservableProperty]
-    private EnvironmentItem? _currentEnvironment;
+    private EnvironmentModel? _currentEnvironment;
 
     [ObservableProperty]
     [NotifyCanExecuteChangedFor(nameof(DeleteEnvironmentCommand), nameof(CheckEnvironmentCommand))]
@@ -80,9 +80,9 @@ private async Task DeleteEnvironment()
         Log.Information($"[Environment] Environment has been removed from list ({CurrentEnvironment!.PipVersion} for {CurrentEnvironment.PythonVersion})");
         EnvironmentItems.Remove(CurrentEnvironment!);
         CurrentEnvironment = null;
-        configurationService.AppConfig.CurrentEnvironment = null;
-        configurationService.AppConfig.EnvironmentItems = [..EnvironmentItems];
-        configurationService.Save();
+        Configuration.AppConfig!.SelectedEnvironment = null;
+        Configuration.AppConfig.Environments = [..EnvironmentItems];
+        Configuration.Save();
         var mainWindowViewModel = App.GetService();
         mainWindowViewModel.ApplicationTitle = "Pip Manager";
         EnvironmentSelected = false;
@@ -117,7 +117,7 @@ private async Task CheckEnvironmentUpdate()
         var latest = "";
         await Task.Run(async () =>
         {
-            var versions = await environmentService.GetVersions("pip", new CancellationToken(), configurationService.AppConfig.PackageSource.DetectNonReleaseVersion);
+            var versions = await environmentService.GetVersions("pip", new CancellationToken(), Configuration.AppConfig!.PackageSource.AllowNonRelease);
             if (versions.Status == 0)
             {
                 latest = versions.Versions!.Last();
@@ -125,7 +125,7 @@ await Task.Run(async () =>
         });
         Task.WaitAll();
         maskService.Hide();
-        var current = configurationService.AppConfig.CurrentEnvironment!.PipVersion!.Trim();
+        var current = Configuration.AppConfig!.SelectedEnvironment!.PipVersion.Trim();
         if (latest != current && latest != string.Empty)
         {
             Log.Information($"[Environment] Environment update available ({current} => {latest})");
@@ -140,7 +140,7 @@ await Task.Run(async () =>
                     progressIntermediate: false
                 ));
                 navigationService.Navigate(typeof(ActionPage));
-                configurationService.RefreshAllEnvironmentVersions();
+                Configuration.RefreshAllEnvironments();
             }
         }
         else if (latest == string.Empty)
@@ -179,8 +179,8 @@ protected override void OnPropertyChanged(PropertyChangedEventArgs e)
         {
             var mainWindowViewModel = App.GetService();
             mainWindowViewModel.ApplicationTitle = $"Pip Manager | {CurrentEnvironment.PipVersion} for {CurrentEnvironment.PythonVersion}";
-            configurationService.AppConfig.CurrentEnvironment = CurrentEnvironment;
-            configurationService.Save();
+            Configuration.AppConfig!.SelectedEnvironment = CurrentEnvironment;
+            Configuration.Save();
             Log.Information($"[Environment] Environment changed ({CurrentEnvironment.PipVersion} for {CurrentEnvironment.PythonVersion})");
         }
     }
@@ -188,7 +188,7 @@ protected override void OnPropertyChanged(PropertyChangedEventArgs e)
     #region Add Environment
 
     [ObservableProperty]
-    private ObservableCollection _environmentItems = [];
+    private ObservableCollection _environmentItems = [];
 
     [RelayCommand]
     private void AddEnvironment()
diff --git a/src/ViewModels/Pages/Library/LibraryInstallViewModel.cs b/src/ViewModels/Pages/Library/LibraryInstallViewModel.cs
index ddfd5d6..6ad031e 100644
--- a/src/ViewModels/Pages/Library/LibraryInstallViewModel.cs
+++ b/src/ViewModels/Pages/Library/LibraryInstallViewModel.cs
@@ -6,12 +6,12 @@
 using System.Text;
 using ICSharpCode.SharpZipLib.GZip;
 using ICSharpCode.SharpZipLib.Tar;
+using PipManager.Core.Configuration;
 using PipManager.Windows.Languages;
 using PipManager.Windows.Models.Action;
 using PipManager.Windows.Models.Pages;
 using PipManager.Windows.Resources.Library;
 using PipManager.Windows.Services.Action;
-using PipManager.Windows.Services.Configuration;
 using PipManager.Windows.Services.Environment;
 using PipManager.Windows.Services.Mask;
 using PipManager.Windows.Services.Toast;
@@ -33,9 +33,8 @@ public record InstalledPackagesMessage(List InstalledPackages);
     private readonly IEnvironmentService _environmentService;
     private readonly IToastService _toastService;
     private readonly INavigationService _navigationService;
-    private readonly IConfigurationService _configurationService;
 
-    public LibraryInstallViewModel(IActionService actionService, IMaskService maskService, IContentDialogService contentDialogService, IEnvironmentService environmentService, IToastService toastService, INavigationService navigationService, IConfigurationService configurationService)
+    public LibraryInstallViewModel(IActionService actionService, IMaskService maskService, IContentDialogService contentDialogService, IEnvironmentService environmentService, IToastService toastService, INavigationService navigationService)
     {
         _actionService = actionService;
         _maskService = maskService;
@@ -44,7 +43,6 @@ public LibraryInstallViewModel(IActionService actionService, IMaskService maskSe
         _toastService = toastService;
         _installWheelDependencies = false;
         _navigationService = navigationService;
-        _configurationService = configurationService;
         WeakReferenceMessenger.Default.Register(this, Receive);
     }
 
@@ -96,7 +94,7 @@ private async Task AddDefaultTask()
             return;
         }
         _maskService.Show(Lang.LibraryInstall_Add_Verifying);
-        var detectNonRelease = _configurationService.AppConfig.PackageSource.DetectNonReleaseVersion;
+        var detectNonRelease = Configuration.AppConfig!.PackageSource.AllowNonRelease;
         var packageVersions = await _environmentService.GetVersions(packageName, new CancellationToken(), detectNonRelease);
         _maskService.Hide();
         switch (packageVersions.Status)
@@ -113,7 +111,7 @@ private async Task AddDefaultTask()
                 PreInstallPackages.Add(new LibraryInstallPackageItem
                 {
                     PackageName = packageName,
-                    AvailableVersions = new List(packageVersions.Versions!.Reverse())
+                    AvailableVersions = [..packageVersions.Versions!.Reverse()]
                 });
                 break;
         }
@@ -213,7 +211,7 @@ private async Task DownloadDistributionsTask()
             return;
         }
         _maskService.Show(Lang.LibraryInstall_Add_Verifying);
-        var packageVersions = await _environmentService.GetVersions(packageName, new CancellationToken(), _configurationService.AppConfig.PackageSource.DetectNonReleaseVersion);
+        var packageVersions = await _environmentService.GetVersions(packageName, new CancellationToken(), Configuration.AppConfig!.PackageSource.AllowNonRelease);
         _maskService.Hide();
         switch (packageVersions.Status)
         {
@@ -229,7 +227,7 @@ private async Task DownloadDistributionsTask()
                 PreDownloadPackages.Add(new LibraryInstallPackageItem
                 {
                     PackageName = packageName,
-                    AvailableVersions = new List(packageVersions.Versions!.Reverse())
+                    AvailableVersions = [..packageVersions.Versions!.Reverse()]
                 });
                 DownloadDistributionsEnabled = DownloadDistributionsFolderPath.Length > 0;
                 break;
diff --git a/src/ViewModels/Pages/Library/LibraryViewModel.cs b/src/ViewModels/Pages/Library/LibraryViewModel.cs
index 216d6cd..25fc9f8 100644
--- a/src/ViewModels/Pages/Library/LibraryViewModel.cs
+++ b/src/ViewModels/Pages/Library/LibraryViewModel.cs
@@ -2,13 +2,13 @@
 using Serilog;
 using System.Collections.ObjectModel;
 using System.Diagnostics;
+using PipManager.Core.Configuration;
 using PipManager.Windows.Languages;
 using PipManager.Windows.Models.Action;
 using PipManager.Windows.Models.Package;
 using PipManager.Windows.Models.Pages;
 using PipManager.Windows.Resources.Library;
 using PipManager.Windows.Services.Action;
-using PipManager.Windows.Services.Configuration;
 using PipManager.Windows.Services.Environment;
 using PipManager.Windows.Services.Mask;
 using PipManager.Windows.Services.Overlay;
@@ -19,8 +19,6 @@
 using Wpf.Ui;
 using Wpf.Ui.Appearance;
 using Wpf.Ui.Controls;
-using static PipManager.Windows.ViewModels.Pages.Library.LibraryDetailViewModel;
-using static PipManager.Windows.ViewModels.Pages.Library.LibraryInstallViewModel;
 
 namespace PipManager.Windows.ViewModels.Pages.Library;
 
@@ -30,7 +28,6 @@ public partial class LibraryViewModel : ObservableObject, INavigationAware
     private bool _isInitialized;
     private readonly INavigationService _navigationService;
     private readonly IEnvironmentService _environmentService;
-    private readonly IConfigurationService _configurationService;
     private readonly IActionService _actionService;
     private readonly IMaskService _maskService;
     private readonly IToastService _toastService;
@@ -38,18 +35,17 @@ public partial class LibraryViewModel : ObservableObject, INavigationAware
     private readonly IOverlayService _overlayService;
 
     public LibraryViewModel(INavigationService navigationService, IEnvironmentService environmentService, IOverlayService overlayService,
-        IConfigurationService configurationService, IActionService actionService, IThemeService themeService, IMaskService maskService, IToastService toastService, IContentDialogService contentDialogService)
+        IActionService actionService, IThemeService themeService, IMaskService maskService, IToastService toastService, IContentDialogService contentDialogService)
     {
         _navigationService = navigationService;
         _environmentService = environmentService;
-        _configurationService = configurationService;
         _actionService = actionService;
         _maskService = maskService;
         _toastService = toastService;
         _contentDialogService = contentDialogService;
         _overlayService = overlayService;
 
-        themeService.SetTheme(_configurationService.AppConfig.Personalization.Theme switch
+        themeService.SetTheme(Configuration.AppConfig!.Personalization.Theme switch
         {
             "light" => ApplicationTheme.Light,
             "dark" => ApplicationTheme.Dark,
@@ -116,7 +112,7 @@ private async Task CheckUpdate()
         var operationList = "";
         var ioTaskList = new List();
         var msgListLock = new object();
-        var detectNonRelease = _configurationService.AppConfig.PackageSource.DetectNonReleaseVersion;
+        var detectNonRelease = Configuration.AppConfig!.PackageSource.AllowNonRelease;
         await Task.Run(() =>
         {
             var selected = LibraryList.Where(libraryListItem => libraryListItem.IsSelected).ToList();
@@ -193,7 +189,7 @@ private async Task RefreshLibrary()
         EnvironmentFoundVisible = true;
         _maskService.Show(Lang.MainWindow_NavigationContent_Library);
         _library = [];
-        if (_configurationService.AppConfig.CurrentEnvironment == null)
+        if (Configuration.AppConfig!.SelectedEnvironment == null)
         {
             _maskService.Hide();
             EnvironmentFoundVisible = false;
diff --git a/src/ViewModels/Pages/Search/SearchDetailViewModel.cs b/src/ViewModels/Pages/Search/SearchDetailViewModel.cs
index 1b3fc08..7b0c7c6 100644
--- a/src/ViewModels/Pages/Search/SearchDetailViewModel.cs
+++ b/src/ViewModels/Pages/Search/SearchDetailViewModel.cs
@@ -6,11 +6,11 @@
 using System.Drawing;
 using System.Net.Http;
 using Microsoft.Win32;
+using PipManager.Core.Configuration;
 using PipManager.Core.Wrappers.PackageSearchQueryWrapper;
 using PipManager.Windows.Languages;
 using PipManager.Windows.Models.Action;
 using PipManager.Windows.Services.Action;
-using PipManager.Windows.Services.Configuration;
 using PipManager.Windows.Services.Environment;
 using PipManager.Windows.Services.Toast;
 using PipManager.Windows.Views.Pages.Search;
@@ -30,7 +30,6 @@ public record SearchDetailMessage(QueryListItemModel Package);
     private readonly IToastService _toastService;
     private readonly IActionService _actionService;
     private readonly IEnvironmentService _environmentService;
-    private readonly IConfigurationService _configurationService;
 
     [ObservableProperty]
     private bool _projectDescriptionVisibility;
@@ -61,7 +60,7 @@ public record SearchDetailMessage(QueryListItemModel Package);
     [ObservableProperty]
     private QueryListItemModel? _package;
 
-    public SearchDetailViewModel(INavigationService navigationService, HttpClient httpClient, IThemeService themeService, IToastService toastService, IEnvironmentService environmentService, IActionService actionService, IConfigurationService configurationService)
+    public SearchDetailViewModel(INavigationService navigationService, HttpClient httpClient, IThemeService themeService, IToastService toastService, IEnvironmentService environmentService, IActionService actionService)
     {
         _navigationService = navigationService;
         _httpClient = httpClient;
@@ -69,7 +68,6 @@ public SearchDetailViewModel(INavigationService navigationService, HttpClient ht
         _toastService = toastService;
         _environmentService = environmentService;
         _actionService = actionService;
-        _configurationService = configurationService;
 
         WeakReferenceMessenger.Default.Register(this, Receive);
     }
@@ -165,7 +163,7 @@ private void Receive(object recipient, SearchDetailMessage message)
         SearchDetailPage.ProjectDescriptionWebView!.Loaded += async (_, _) =>
         {
             ProjectDescriptionVisibility = false;
-            var packageVersions = await _environmentService.GetVersions(Package!.Name, new CancellationToken(), _configurationService.AppConfig.PackageSource.DetectNonReleaseVersion);
+            var packageVersions = await _environmentService.GetVersions(Package!.Name, new CancellationToken(), Configuration.AppConfig!.PackageSource.AllowNonRelease);
             switch (packageVersions.Status)
             {
                 case 1 or 2:
diff --git a/src/ViewModels/Pages/Search/SearchViewModel.cs b/src/ViewModels/Pages/Search/SearchViewModel.cs
index 423681e..e398299 100644
--- a/src/ViewModels/Pages/Search/SearchViewModel.cs
+++ b/src/ViewModels/Pages/Search/SearchViewModel.cs
@@ -9,7 +9,6 @@
 using PipManager.Windows.Views.Pages.Search;
 using Wpf.Ui;
 using Wpf.Ui.Controls;
-using static PipManager.Windows.ViewModels.Pages.Search.SearchDetailViewModel;
 
 namespace PipManager.Windows.ViewModels.Pages.Search;
 
diff --git a/src/ViewModels/Pages/Settings/SettingsViewModel.cs b/src/ViewModels/Pages/Settings/SettingsViewModel.cs
index f13d5a8..cdc6cd0 100644
--- a/src/ViewModels/Pages/Settings/SettingsViewModel.cs
+++ b/src/ViewModels/Pages/Settings/SettingsViewModel.cs
@@ -1,13 +1,11 @@
-using Newtonsoft.Json;
-using Serilog;
+using Serilog;
 using System.Diagnostics;
 using System.Globalization;
 using System.IO;
 using System.Net.Http;
+using PipManager.Core.Configuration;
+using PipManager.Core.Extensions;
 using PipManager.Windows.Languages;
-using PipManager.Windows.Models;
-using PipManager.Windows.Models.Package;
-using PipManager.Windows.Services.Configuration;
 using PipManager.Windows.Services.Toast;
 using PipManager.Windows.Views.Pages.About;
 using PipManager.Windows.Views.Pages.Settings;
@@ -22,16 +20,14 @@ public partial class SettingsViewModel : ObservableObject, INavigationAware
 {
     private readonly HttpClient _httpClient;
     private readonly ISnackbarService _snackbarService;
-    private readonly IConfigurationService _configurationService;
     private readonly IThemeService _themeService;
     private readonly INavigationService _navigationService;
     private readonly IToastService _toastService;
 
-    public SettingsViewModel(ISnackbarService snackbarService, IConfigurationService configurationService, IThemeService themeService, INavigationService navigationService, IToastService toastService)
+    public SettingsViewModel(ISnackbarService snackbarService, IThemeService themeService, INavigationService navigationService, IToastService toastService)
     {
         _httpClient = App.GetService();
         _snackbarService = snackbarService;
-        _configurationService = configurationService;
         _themeService = themeService;
         _navigationService = navigationService;
         _toastService = toastService;
@@ -57,27 +53,24 @@ public void OnNavigatedFrom()
 
     private void InitializeViewModel()
     {
-        CurrentPackageSource = _configurationService.AppConfig.PackageSource.PackageSourceType;
-        DetectNonReleaseVersion = _configurationService.AppConfig.PackageSource.DetectNonReleaseVersion;
-        var language = _configurationService.AppConfig.Personalization.Language;
+        var config = Configuration.AppConfig!;
+        CurrentPackageSource = config.PackageSource.Source;
+        AllowNonRelease = config.PackageSource.AllowNonRelease;
+        var language = config.Personalization.Language;
         Language = language != "Auto" ? GetLanguage.LanguageList.Select(x => x.Key).ToList()[GetLanguage.LanguageList.Select(x => x.Value).ToList().IndexOf(language)] : "Auto";
-        CurrentTheme = _configurationService.AppConfig.Personalization.Theme switch
+        CurrentTheme = config.Personalization.Theme switch
         {
             "light" => ApplicationTheme.Light,
             "dark" => ApplicationTheme.Dark,
             _ => ApplicationTheme.Dark
         };
-        LogAutoDeletion = _configurationService.AppConfig.Personalization.LogAutoDeletion;
-        LogAutoDeletionTimes = _configurationService.AppConfig.Personalization.LogAutoDeletionTimes;
-        CrushesAutoDeletion = _configurationService.AppConfig.Personalization.CrushesAutoDeletion;
-        CrushesAutoDeletionTimes = _configurationService.AppConfig.Personalization.CrushesAutoDeletionTimes;
         _isInitialized = true;
         Log.Information("[Settings] Initialized");
     }
 
     #region Package Source
 
-    [ObservableProperty] private PackageSourceType _currentPackageSource = PackageSourceType.Official;
+    [ObservableProperty] private string _currentPackageSource = "default";
     [ObservableProperty] private string _officialPackageSourceNetwork = string.Empty;
     [ObservableProperty] private string _tsinghuaPackageSourceNetwork = string.Empty;
     [ObservableProperty] private string _aliyunPackageSourceNetwork = string.Empty;
@@ -86,16 +79,8 @@ private void InitializeViewModel()
     [RelayCommand]
     private void OnChangePackageSource(string parameter)
     {
-        CurrentPackageSource = parameter switch
-        {
-            "official" => PackageSourceType.Official,
-            "tsinghua" => PackageSourceType.Tsinghua,
-            "aliyun" => PackageSourceType.Aliyun,
-            "douban" => PackageSourceType.Douban,
-            _ => PackageSourceType.Official
-        };
-        _configurationService.AppConfig.PackageSource.PackageSourceType = CurrentPackageSource;
-        _configurationService.Save();
+        Configuration.AppConfig!.PackageSource.Source = parameter;
+        Configuration.Save();
         Log.Information($"[Settings] Package source changes to {parameter}");
     }
 
@@ -112,7 +97,7 @@ async Task OfficialTask()
         {
             try
             {
-                await _httpClient.GetByteArrayAsync(_configurationService.GetTestingUrlFromPackageSourceType(PackageSourceType.Official));
+                await _httpClient.GetByteArrayAsync("default".GetPackageSourceTestingUrl());
                 OfficialPackageSourceNetwork = $"{stopwatch.ElapsedMilliseconds} ms";
             }
             catch (Exception exception) when (exception is HttpRequestException or TaskCanceledException)
@@ -125,7 +110,7 @@ async Task TsinghuaTask()
         {
             try
             {
-                await _httpClient.GetByteArrayAsync(_configurationService.GetTestingUrlFromPackageSourceType(PackageSourceType.Tsinghua));
+                await _httpClient.GetByteArrayAsync("tsinghua".GetPackageSourceTestingUrl());
                 TsinghuaPackageSourceNetwork = $"{stopwatch.ElapsedMilliseconds} ms";
             }
             catch (Exception exception) when (exception is HttpRequestException or TaskCanceledException)
@@ -138,7 +123,7 @@ async Task AliyunTask()
         {
             try
             {
-                await _httpClient.GetByteArrayAsync(_configurationService.GetTestingUrlFromPackageSourceType(PackageSourceType.Aliyun));
+                await _httpClient.GetByteArrayAsync("aliyun".GetPackageSourceTestingUrl());
                 AliyunPackageSourceNetwork = $"{stopwatch.ElapsedMilliseconds} ms";
             }
             catch (Exception exception) when (exception is HttpRequestException or TaskCanceledException)
@@ -151,7 +136,7 @@ async Task DoubanTask()
         {
             try
             {
-                await _httpClient.GetByteArrayAsync(_configurationService.GetTestingUrlFromPackageSourceType(PackageSourceType.Douban));
+                await _httpClient.GetByteArrayAsync("douban".GetPackageSourceTestingUrl());
                 DoubanPackageSourceNetwork = $"{stopwatch.ElapsedMilliseconds} ms";
             }
             catch (Exception exception) when (exception is HttpRequestException or TaskCanceledException)
@@ -165,14 +150,14 @@ async Task DoubanTask()
     }
     
     [ObservableProperty]
-    private bool _detectNonReleaseVersion;
+    private bool _allowNonRelease;
     
     [RelayCommand]
     private void OnChangeDetectNonReleaseVersion()
     {
-        _configurationService.AppConfig.PackageSource.DetectNonReleaseVersion = DetectNonReleaseVersion;
-        _configurationService.Save();
-        Log.Information($"[Settings] Detect non-release update changes to {DetectNonReleaseVersion}");
+        Configuration.AppConfig!.PackageSource.AllowNonRelease = AllowNonRelease;
+        Configuration.Save();
+        Log.Information($"[Settings] Detect non-release update changes to {AllowNonRelease}");
     }
 
     #endregion Package Source
@@ -187,8 +172,8 @@ private void OnChangeLanguage()
     {
         var language = Language != "Auto" ? GetLanguage.LanguageList[Language] : "Auto";
         I18NExtension.Culture = language != "Auto" ? new CultureInfo(language) : CultureInfo.CurrentCulture;
-        _configurationService.AppConfig.Personalization.Language = Language != "Auto" ? GetLanguage.LanguageList[Language] : "Auto";
-        _configurationService.Save();
+        Configuration.AppConfig!.Personalization.Language = Language != "Auto" ? GetLanguage.LanguageList[Language] : "Auto";
+        Configuration.Save();
         if (_isInitialized)
         {
             _navigationService.Navigate(typeof(AboutPage));
@@ -218,54 +203,13 @@ private void OnChangeTheme(string parameter)
                 CurrentTheme = ApplicationTheme.Dark;
                 break;
         }
-        _configurationService.AppConfig.Personalization.Theme = parameter;
-        _configurationService.Save();
+        Configuration.AppConfig!.Personalization.Theme = parameter;
+        Configuration.Save();
         Log.Information($"[Settings] Theme changes to {parameter}");
     }
 
     #endregion Theme
 
-    #region Log and Crushes Auto Deletion
-
-    [ObservableProperty] private bool _logAutoDeletion;
-    [ObservableProperty] private bool _crushesAutoDeletion;
-    [ObservableProperty] private int _logAutoDeletionTimes;
-    [ObservableProperty] private int _crushesAutoDeletionTimes;
-
-    [RelayCommand]
-    private void OnChangeLogAutoDeletion()
-    {
-        _configurationService.AppConfig.Personalization.LogAutoDeletion = LogAutoDeletion;
-        _configurationService.Save();
-        Log.Information($"[Settings] Log auto deletion now is {LogAutoDeletion}");
-    }
-
-    [RelayCommand]
-    private void OnChangeLogAutoDeletionTimes()
-    {
-        _configurationService.AppConfig.Personalization.LogAutoDeletionTimes = LogAutoDeletionTimes;
-        _configurationService.Save();
-        Log.Information($"[Settings] Log auto deletion will be executed when the number of files reaches {LogAutoDeletionTimes}");
-    }
-
-    [RelayCommand]
-    private void OnChangeCrushesAutoDeletion()
-    {
-        _configurationService.AppConfig.Personalization.CrushesAutoDeletion = CrushesAutoDeletion;
-        _configurationService.Save();
-        Log.Information($"[Settings] Crushes auto deletion now is {CrushesAutoDeletion}");
-    }
-
-    [RelayCommand]
-    private void OnChangeCrushesAutoDeletionTimes()
-    {
-        _configurationService.AppConfig.Personalization.CrushesAutoDeletionTimes = CrushesAutoDeletionTimes;
-        _configurationService.Save();
-        Log.Information($"[Settings] Crushes auto deletion will be executed when the number of files reaches {CrushesAutoDeletionTimes}");
-    }
-
-    #endregion Log and Crushes Auto Deletion
-
     #region File Management
 
     [RelayCommand]
@@ -340,7 +284,7 @@ private static async Task ResetConfigurationAsync()
         if (result == Wpf.Ui.Controls.MessageBoxResult.Primary)
         {
             Log.Information("Config reset");
-            await File.WriteAllTextAsync(AppInfo.ConfigPath, JsonConvert.SerializeObject(new AppConfig(), Formatting.Indented));
+            Configuration.Reset();
         }
     }
 
diff --git a/src/ViewModels/Windows/MainWindowViewModel.cs b/src/ViewModels/Windows/MainWindowViewModel.cs
index e9faaac..13ded03 100644
--- a/src/ViewModels/Windows/MainWindowViewModel.cs
+++ b/src/ViewModels/Windows/MainWindowViewModel.cs
@@ -1,4 +1,4 @@
-using PipManager.Windows.Services.Configuration;
+using PipManager.Core.Configuration;
 using Serilog;
 
 namespace PipManager.Windows.ViewModels.Windows;
@@ -7,13 +7,13 @@ public partial class MainWindowViewModel : ObservableObject
 {
     [ObservableProperty] private bool _experimentMode;
 
-    public MainWindowViewModel(IConfigurationService configurationService)
+    public MainWindowViewModel()
     {
-
-        if (configurationService.AppConfig.CurrentEnvironment != null)
+        var config = Configuration.AppConfig!;
+        if (config.SelectedEnvironment != null)
         {
-            Log.Information($"[MainWindow] Environment loaded ({configurationService.AppConfig.CurrentEnvironment.PipVersion} for {configurationService.AppConfig.CurrentEnvironment.PythonVersion})");
-            ApplicationTitle = $"Pip Manager | {configurationService.AppConfig.CurrentEnvironment.PipVersion} for {configurationService.AppConfig.CurrentEnvironment.PythonVersion}";
+            Log.Information($"[MainWindow] Environment loaded ({config.SelectedEnvironment.PipVersion} for {config.SelectedEnvironment.PythonVersion})");
+            ApplicationTitle = $"Pip Manager | {config.SelectedEnvironment.PipVersion} for {config.SelectedEnvironment.PythonVersion}";
         }
         else
         {
diff --git a/src/Views/Pages/About/AboutPage.xaml b/src/Views/Pages/About/AboutPage.xaml
index 88055a5..4652b81 100644
--- a/src/Views/Pages/About/AboutPage.xaml
+++ b/src/Views/Pages/About/AboutPage.xaml
@@ -41,13 +41,6 @@
                                     Margin="7,0,0,0"
                                     VerticalAlignment="Top"
                                     Visibility="{Binding ViewModel.DebugMode, Converter={StaticResource BoolToVisibility}}" />
-                                
 
                             
                             
diff --git a/src/Views/Pages/Environment/AddEnvironmentPage.xaml b/src/Views/Pages/Environment/AddEnvironmentPage.xaml
index a75b6b2..b094cdc 100644
--- a/src/Views/Pages/Environment/AddEnvironmentPage.xaml
+++ b/src/Views/Pages/Environment/AddEnvironmentPage.xaml
@@ -13,13 +13,13 @@
     ui:NavigationView.HeaderContent="{I18N {x:Static lang:LangKeys.EnvironmentAdd_Header}}"
     x:Class="PipManager.Windows.Views.Pages.Environment.AddEnvironmentPage"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
-    xmlns:appConfigModels="clr-namespace:PipManager.Windows.Models.AppConfigModels"
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
     xmlns:environment="clr-namespace:PipManager.Windows.Views.Pages.Environment"
     xmlns:lang="clr-namespace:PipManager.Windows.Languages"
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
     xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
-    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:models="clr-namespace:PipManager.Core.Configuration.Models;assembly=PipManager.Core">
 
     
         
                             
-                                
+                                
                                     
                                         
                                             
diff --git a/src/Views/Pages/Environment/EnvironmentPage.xaml b/src/Views/Pages/Environment/EnvironmentPage.xaml
index 1b26f81..8ebeab5 100644
--- a/src/Views/Pages/Environment/EnvironmentPage.xaml
+++ b/src/Views/Pages/Environment/EnvironmentPage.xaml
@@ -13,13 +13,13 @@
     x:Class="PipManager.Windows.Views.Pages.Environment.EnvironmentPage"
     x:Name="Environment"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
-    xmlns:appConfigModels="clr-namespace:PipManager.Windows.Models.AppConfigModels"
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
     xmlns:environment="clr-namespace:PipManager.Windows.Views.Pages.Environment"
     xmlns:lang="clr-namespace:PipManager.Windows.Languages"
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
     xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
-    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:models="clr-namespace:PipManager.Core.Configuration.Models;assembly=PipManager.Core">
 
     
         
@@ -61,7 +61,7 @@
                 SelectedItem="{Binding ViewModel.CurrentEnvironment}"
                 VerticalAlignment="Stretch">
                 
-                    
+                    
                         
                             
                                 
diff --git a/src/Views/Pages/Settings/SettingsPage.xaml b/src/Views/Pages/Settings/SettingsPage.xaml
index c2e4f7c..0294725 100644
--- a/src/Views/Pages/Settings/SettingsPage.xaml
+++ b/src/Views/Pages/Settings/SettingsPage.xaml
@@ -153,7 +153,7 @@
                                 Text="{I18N {x:Static lang:LangKeys.Settings_DetectNonReleaseVersion_Subtitle}}" />
                         
                     
-                    
+                    
                 
             
         
@@ -233,118 +233,6 @@
                             Margin="10,0,0,0" />
                     
                 
-
-                
-                
-                    
-                        
-                            
-                                
-                                
-                            
-                            
-                            
-                        
-                    
-                    
-                        
-                        
-                            
-                                
-                                    
-                                
-                            
-                        
-                        
-                            
-                                
-                                    
-                                
-                            
-                        
-                    
-                
-
-                
-                
-                    
-                        
-                            
-                                
-                                
-                            
-                            
-                            
-                        
-                    
-                    
-                        
-                        
-                            
-                                
-                                    
-                                
-                            
-                        
-                        
-                            
-                                
-                                    
-                                
-                            
-                        
-                    
-                
             
         
 
diff --git a/src/Views/Pages/Settings/SettingsPage.xaml.cs b/src/Views/Pages/Settings/SettingsPage.xaml.cs
index 52bf9f4..e4d4745 100644
--- a/src/Views/Pages/Settings/SettingsPage.xaml.cs
+++ b/src/Views/Pages/Settings/SettingsPage.xaml.cs
@@ -1,6 +1,5 @@
 using Wpf.Ui.Controls;
 using Settings_SettingsViewModel = PipManager.Windows.ViewModels.Pages.Settings.SettingsViewModel;
-using SettingsViewModel = PipManager.Windows.ViewModels.Pages.Settings.SettingsViewModel;
 
 namespace PipManager.Windows.Views.Pages.Settings;