diff --git a/Flow.Launcher.Core/ExternalPlugins/CommunityPluginSource.cs b/Flow.Launcher.Core/ExternalPlugins/CommunityPluginSource.cs
index 6f3b23e11..841099dd1 100644
--- a/Flow.Launcher.Core/ExternalPlugins/CommunityPluginSource.cs
+++ b/Flow.Launcher.Core/ExternalPlugins/CommunityPluginSource.cs
@@ -73,6 +73,17 @@ namespace Flow.Launcher.Core.ExternalPlugins
return null;
}
}
+ catch (OperationCanceledException) when (token.IsCancellationRequested)
+ {
+ API.LogDebug(ClassName, $"Fetching from {ManifestFileUrl} was cancelled by caller.");
+ return null;
+ }
+ catch (TaskCanceledException)
+ {
+ // Likely an HttpClient timeout or external cancellation not requested by our token
+ API.LogWarn(ClassName, $"Fetching from {ManifestFileUrl} timed out.");
+ return null;
+ }
catch (Exception e)
{
if (e is HttpRequestException or WebException or SocketException || e.InnerException is TimeoutException)
diff --git a/Flow.Launcher.Core/Flow.Launcher.Core.csproj b/Flow.Launcher.Core/Flow.Launcher.Core.csproj
index 527950061..1369d7e5d 100644
--- a/Flow.Launcher.Core/Flow.Launcher.Core.csproj
+++ b/Flow.Launcher.Core/Flow.Launcher.Core.csproj
@@ -55,8 +55,8 @@
-
-
+
+
diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs b/Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs
index 435d97ab7..9212dada6 100644
--- a/Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs
+++ b/Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs
@@ -27,6 +27,7 @@ namespace Flow.Launcher.Core.Plugin
private JsonStorage> _storage = null!;
+ private static readonly double MainGridColumn0MaxWidthRatio = 0.6;
private static readonly Thickness SettingPanelMargin = (Thickness)Application.Current.FindResource("SettingPanelMargin");
private static readonly Thickness SettingPanelItemLeftMargin = (Thickness)Application.Current.FindResource("SettingPanelItemLeftMargin");
private static readonly Thickness SettingPanelItemTopBottomMargin = (Thickness)Application.Current.FindResource("SettingPanelItemTopBottomMargin");
@@ -156,7 +157,7 @@ namespace Flow.Launcher.Core.Plugin
{
if (!NeedCreateSettingPanel()) return null!;
- // Create main grid with two columns (Column 1: Auto, Column 2: *)
+ // Create main grid with two columns (Column 0: Auto, Column 1: *)
var mainPanel = new Grid { Margin = SettingPanelMargin, VerticalAlignment = VerticalAlignment.Center };
mainPanel.ColumnDefinitions.Add(new ColumnDefinition()
{
@@ -200,7 +201,7 @@ namespace Flow.Launcher.Core.Plugin
{
Text = attributes.Label,
VerticalAlignment = VerticalAlignment.Center,
- TextWrapping = TextWrapping.WrapWithOverflow
+ TextWrapping = TextWrapping.Wrap
};
// Create a text block for description
@@ -211,7 +212,7 @@ namespace Flow.Launcher.Core.Plugin
{
Text = attributes.Description,
VerticalAlignment = VerticalAlignment.Center,
- TextWrapping = TextWrapping.WrapWithOverflow
+ TextWrapping = TextWrapping.Wrap
};
desc.SetResourceReference(TextBlock.StyleProperty, "SettingPanelTextBlockDescriptionStyle"); // for theme change
@@ -247,7 +248,8 @@ namespace Flow.Launcher.Core.Plugin
VerticalAlignment = VerticalAlignment.Center,
Margin = SettingPanelItemLeftTopBottomMargin,
Text = Settings[attributes.Name] as string ?? string.Empty,
- ToolTip = attributes.Description
+ ToolTip = attributes.Description,
+ TextWrapping = TextWrapping.Wrap
};
textBox.TextChanged += (_, _) =>
@@ -269,7 +271,8 @@ namespace Flow.Launcher.Core.Plugin
VerticalAlignment = VerticalAlignment.Center,
Margin = SettingPanelItemLeftMargin,
Text = Settings[attributes.Name] as string ?? string.Empty,
- ToolTip = attributes.Description
+ ToolTip = attributes.Description,
+ TextWrapping = TextWrapping.Wrap
};
textBox.TextChanged += (_, _) =>
@@ -333,7 +336,7 @@ namespace Flow.Launcher.Core.Plugin
HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Center,
Margin = SettingPanelItemLeftTopBottomMargin,
- TextWrapping = TextWrapping.WrapWithOverflow,
+ TextWrapping = TextWrapping.Wrap,
AcceptsReturn = true,
Text = Settings[attributes.Name] as string ?? string.Empty,
ToolTip = attributes.Description
@@ -488,6 +491,8 @@ namespace Flow.Launcher.Core.Plugin
rowCount++;
}
+ mainPanel.SizeChanged += MainPanel_SizeChanged;
+
// Wrap the main grid in a user control
return new UserControl()
{
@@ -495,6 +500,28 @@ namespace Flow.Launcher.Core.Plugin
};
}
+ private void MainPanel_SizeChanged(object sender, SizeChangedEventArgs e)
+ {
+ if (sender is not Grid grid) return;
+
+ var workingWidth = grid.ActualWidth;
+
+ if (workingWidth <= 0) return;
+
+ var constrainedWidth = MainGridColumn0MaxWidthRatio * workingWidth;
+
+ // Set MaxWidth of column 0 and its children
+ // We must set MaxWidth of its children to make text wrapping work correctly
+ grid.ColumnDefinitions[0].MaxWidth = constrainedWidth;
+ foreach (var child in grid.Children)
+ {
+ if (child is FrameworkElement element && Grid.GetColumn(element) == 0 && Grid.GetColumnSpan(element) == 1)
+ {
+ element.MaxWidth = constrainedWidth;
+ }
+ }
+ }
+
private static bool NeedSaveInSettings(string type)
{
return type != "textBlock" && type != "separator" && type != "hyperlink";
diff --git a/Flow.Launcher.Core/Resource/Internationalization.cs b/Flow.Launcher.Core/Resource/Internationalization.cs
index 4788efbf2..38eac13fb 100644
--- a/Flow.Launcher.Core/Resource/Internationalization.cs
+++ b/Flow.Launcher.Core/Resource/Internationalization.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
@@ -14,7 +14,7 @@ using Flow.Launcher.Plugin;
namespace Flow.Launcher.Core.Resource
{
- public class Internationalization
+ public class Internationalization : IDisposable
{
private static readonly string ClassName = nameof(Internationalization);
@@ -30,6 +30,7 @@ namespace Flow.Launcher.Core.Resource
private readonly List _languageDirectories = [];
private readonly List _oldResources = [];
private static string SystemLanguageCode;
+ private readonly SemaphoreSlim _langChangeLock = new(1, 1);
public Internationalization(Settings settings)
{
@@ -185,20 +186,33 @@ namespace Flow.Launcher.Core.Resource
private async Task ChangeLanguageAsync(Language language, bool updateMetadata = true)
{
- // Remove old language files and load language
- RemoveOldLanguageFiles();
- if (language != AvailableLanguages.English)
+ await _langChangeLock.WaitAsync();
+
+ try
{
- LoadLanguage(language);
+ // Remove old language files and load language
+ RemoveOldLanguageFiles();
+ if (language != AvailableLanguages.English)
+ {
+ LoadLanguage(language);
+ }
+
+ // Change culture info
+ ChangeCultureInfo(language.LanguageCode);
+
+ if (updateMetadata)
+ {
+ // Raise event for plugins after culture is set
+ await Task.Run(UpdatePluginMetadataTranslations);
+ }
}
-
- // Change culture info
- ChangeCultureInfo(language.LanguageCode);
-
- if (updateMetadata)
+ catch (Exception e)
{
- // Raise event for plugins after culture is set
- await Task.Run(UpdatePluginMetadataTranslations);
+ API.LogException(ClassName, $"Failed to change language to <{language.LanguageCode}>", e);
+ }
+ finally
+ {
+ _langChangeLock.Release();
}
}
@@ -257,6 +271,7 @@ namespace Flow.Launcher.Core.Resource
{
dicts.Remove(r);
}
+ _oldResources.Clear();
}
private void LoadLanguage(Language language)
@@ -384,5 +399,15 @@ namespace Flow.Launcher.Core.Resource
}
#endregion
+
+ #region IDisposable
+
+ public void Dispose()
+ {
+ RemoveOldLanguageFiles();
+ _langChangeLock.Dispose();
+ }
+
+ #endregion
}
}
diff --git a/Flow.Launcher.Core/packages.lock.json b/Flow.Launcher.Core/packages.lock.json
index 5e9abc24c..b7a00d94d 100644
--- a/Flow.Launcher.Core/packages.lock.json
+++ b/Flow.Launcher.Core/packages.lock.json
@@ -13,15 +13,15 @@
},
"FSharp.Core": {
"type": "Direct",
- "requested": "[9.0.300, )",
- "resolved": "9.0.300",
- "contentHash": "TVt2J7RCE1KCS2IaONF+p8/KIZ1eHNbW+7qmKF6hGoD4tXl+o07ja1mPtFjMqRa5uHMFaTrGTPn/m945WnDLiQ=="
+ "requested": "[9.0.303, )",
+ "resolved": "9.0.303",
+ "contentHash": "6JlV8aD8qQvcmfoe/PMOxCHXc0uX4lR23u0fAyQtnVQxYULLoTZgwgZHSnRcuUHOvS3wULFWcwdnP1iwslH60g=="
},
"Meziantou.Framework.Win32.Jobs": {
"type": "Direct",
- "requested": "[3.4.3, )",
- "resolved": "3.4.3",
- "contentHash": "REjInKnQ0OrhjjtSMPQtLtdURctCroB4L8Sd2gjTOYDysklvsdnrStx1tHS7uLv+fSyFF3aazZmo5Ka0v1oz/w=="
+ "requested": "[3.4.4, )",
+ "resolved": "3.4.4",
+ "contentHash": "AivBzH5wM1NHBLehclim+o37SmireP7JxCRUoTilsc/h7LH9+YCPjb6Ig6y0khnQhFcO1P8RHYw4oiR15TGHUg=="
},
"Microsoft.IO.RecyclableMemoryStream": {
"type": "Direct",
@@ -90,8 +90,8 @@
},
"JetBrains.Annotations": {
"type": "Transitive",
- "resolved": "2024.3.0",
- "contentHash": "ox5pkeLQXjvJdyAB4b2sBYAlqZGLh3PjSnP1bQNVx72ONuTJ9+34/+Rq91Fc0dG29XG9RgZur9+NcP4riihTug=="
+ "resolved": "2025.2.2",
+ "contentHash": "0X56ZRizuHdrnPpgXjWV7f2tQO1FlQg5O1967OGKnI/4ZRNOK642J8L7brM1nYvrxTTU5TP1yRyXLRLaXLPQ8A=="
},
"MemoryPack": {
"type": "Transitive",
@@ -199,21 +199,21 @@
},
"NLog": {
"type": "Transitive",
- "resolved": "6.0.1",
- "contentHash": "qDWiqy8/xdpZKtHna/645KbalwP86N2NFJEzfqhcv+Si4V2iNaEfR/dCneuF/4+Dcwl3f7jHMXj3ndWYftV3Ug=="
+ "resolved": "6.0.4",
+ "contentHash": "Xr+lIk1ZlTTFXEqnxQVLxrDqZlt2tm5X+/AhJbaY2emb/dVtGDiU5QuEtj3gHtwV/SWlP/rJ922I/BPuOJXlRw=="
},
"NLog.OutputDebugString": {
"type": "Transitive",
- "resolved": "6.0.1",
- "contentHash": "wwJCQLaHVzuRf8TsXB+EEdrzVvE3dnzCSMQMDgwkw3AXp8VSp3JSVF/Q/H0oEqggKgKhPs13hh3a7svyQr4s3A==",
+ "resolved": "6.0.4",
+ "contentHash": "TOP2Ap9BbE98B/l/TglnguowOD0rXo8B/20xAgvj9shO/kf6IJ5M4QMhVxq72mrneJ/ANhHY7Jcd+xJbzuI5PA==",
"dependencies": {
- "NLog": "6.0.1"
+ "NLog": "6.0.4"
}
},
"SharpVectors.Wpf": {
"type": "Transitive",
- "resolved": "1.8.4.2",
- "contentHash": "PNxLkMBJnV8A+6yH9OqOlhLJegvWP/dvh0rAJp2l0kcrR+rB4R2tQ9vhUqka+UilH4atN8T6zvjDOizVyfz2Ng=="
+ "resolved": "1.8.5",
+ "contentHash": "WURdBDq5AE8RjKV9pFS7lNkJe81gxja9SaMGE4URq9GJUZ6M+5DGUL0Lm3B0iYW2/Meyowaz4ffGsyW+RBSTtg=="
},
"Splat": {
"type": "Transitive",
@@ -254,14 +254,14 @@
"Ben.Demystifier": "[0.4.1, )",
"BitFaster.Caching": "[2.5.4, )",
"CommunityToolkit.Mvvm": "[8.4.0, )",
- "Flow.Launcher.Plugin": "[4.7.0, )",
+ "Flow.Launcher.Plugin": "[5.0.0, )",
"InputSimulator": "[1.0.4, )",
"MemoryPack": "[1.21.4, )",
"Microsoft.VisualStudio.Threading": "[17.14.15, )",
"NHotkey.Wpf": "[3.0.0, )",
- "NLog": "[6.0.1, )",
- "NLog.OutputDebugString": "[6.0.1, )",
- "SharpVectors.Wpf": "[1.8.4.2, )",
+ "NLog": "[6.0.4, )",
+ "NLog.OutputDebugString": "[6.0.4, )",
+ "SharpVectors.Wpf": "[1.8.5, )",
"System.Drawing.Common": "[7.0.0, )",
"ToolGood.Words.Pinyin": "[3.1.0.3, )"
}
@@ -269,7 +269,7 @@
"flow.launcher.plugin": {
"type": "Project",
"dependencies": {
- "JetBrains.Annotations": "[2024.3.0, )"
+ "JetBrains.Annotations": "[2025.2.2, )"
}
}
}
diff --git a/Flow.Launcher.Infrastructure/FileExplorerHelper.cs b/Flow.Launcher.Infrastructure/FileExplorerHelper.cs
index 1085cc833..6e2d86849 100644
--- a/Flow.Launcher.Infrastructure/FileExplorerHelper.cs
+++ b/Flow.Launcher.Infrastructure/FileExplorerHelper.cs
@@ -1,8 +1,4 @@
using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using Windows.Win32;
namespace Flow.Launcher.Infrastructure
{
@@ -13,9 +9,10 @@ namespace Flow.Launcher.Infrastructure
///
public static string GetActiveExplorerPath()
{
- var explorerWindow = GetActiveExplorer();
- string locationUrl = explorerWindow?.LocationURL;
- return !string.IsNullOrEmpty(locationUrl) ? GetDirectoryPath(new Uri(locationUrl).LocalPath) : null;
+ var explorerPath = DialogJump.DialogJump.GetActiveExplorerPath();
+ return !string.IsNullOrEmpty(explorerPath) ?
+ GetDirectoryPath(new Uri(explorerPath).LocalPath) :
+ null;
}
///
@@ -23,74 +20,12 @@ namespace Flow.Launcher.Infrastructure
///
private static string GetDirectoryPath(string path)
{
- if (!path.EndsWith("\\"))
+ if (!path.EndsWith('\\'))
{
return path + "\\";
}
return path;
}
-
- ///
- /// Gets the file explorer that is currently in the foreground
- ///
- private static dynamic GetActiveExplorer()
- {
- Type type = Type.GetTypeFromProgID("Shell.Application");
- if (type == null) return null;
- dynamic shell = Activator.CreateInstance(type);
- if (shell == null)
- {
- return null;
- }
-
- var explorerWindows = new List();
- var openWindows = shell.Windows();
- for (int i = 0; i < openWindows.Count; i++)
- {
- var window = openWindows.Item(i);
- if (window == null) continue;
-
- // find the desired window and make sure that it is indeed a file explorer
- // we don't want the Internet Explorer or the classic control panel
- // ToLower() is needed, because Windows can report the path as "C:\\Windows\\Explorer.EXE"
- if (Path.GetFileName((string)window.FullName)?.ToLower() == "explorer.exe")
- {
- explorerWindows.Add(window);
- }
- }
-
- if (explorerWindows.Count == 0) return null;
-
- var zOrders = GetZOrder(explorerWindows);
-
- return explorerWindows.Zip(zOrders).MinBy(x => x.Second).First;
- }
-
- ///
- /// Gets the z-order for one or more windows atomically with respect to each other. In Windows, smaller z-order is higher. If the window is not top level, the z order is returned as -1.
- ///
- private static IEnumerable GetZOrder(List hWnds)
- {
- var z = new int[hWnds.Count];
- for (var i = 0; i < hWnds.Count; i++) z[i] = -1;
-
- var index = 0;
- var numRemaining = hWnds.Count;
- PInvoke.EnumWindows((wnd, _) =>
- {
- var searchIndex = hWnds.FindIndex(x => new IntPtr(x.HWND) == wnd);
- if (searchIndex != -1)
- {
- z[searchIndex] = index;
- numRemaining--;
- if (numRemaining == 0) return false;
- }
- index++;
- return true;
- }, IntPtr.Zero);
-
- return z;
- }
}
}
diff --git a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj
index c32c36248..5b4eaf893 100644
--- a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj
+++ b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj
@@ -56,24 +56,24 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
all
-
+
diff --git a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs
index 64d323de6..598347fd2 100644
--- a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs
+++ b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs
@@ -22,7 +22,7 @@ namespace Flow.Launcher.Infrastructure.Image
private static Lock storageLock { get; } = new();
private static BinaryStorage> _storage;
private static readonly ConcurrentDictionary GuidToKey = new();
- private static IImageHashGenerator _hashGenerator;
+ private static ImageHashGenerator _hashGenerator;
private static readonly bool EnableImageHash = true;
public static ImageSource Image => ImageCache[Constant.ImageIcon, false];
public static ImageSource MissingImage => ImageCache[Constant.MissingImgIcon, false];
@@ -31,7 +31,7 @@ namespace Flow.Launcher.Infrastructure.Image
public const int FullIconSize = 256;
public const int FullImageSize = 320;
- private static readonly string[] ImageExtensions = { ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".tiff", ".ico" };
+ private static readonly string[] ImageExtensions = [".png", ".jpg", ".jpeg", ".gif", ".bmp", ".tiff", ".ico"];
private static readonly string SvgExtension = ".svg";
public static async Task InitializeAsync()
@@ -327,7 +327,7 @@ namespace Flow.Launcher.Infrastructure.Image
return img;
}
- private static ImageSource LoadFullImage(string path)
+ private static BitmapImage LoadFullImage(string path)
{
BitmapImage image = new BitmapImage();
image.BeginInit();
@@ -364,7 +364,7 @@ namespace Flow.Launcher.Infrastructure.Image
return image;
}
- private static ImageSource LoadSvgImage(string path, bool loadFullImage = false)
+ private static RenderTargetBitmap LoadSvgImage(string path, bool loadFullImage = false)
{
// Set up drawing settings
var desiredHeight = loadFullImage ? FullImageSize : SmallIconSize;
diff --git a/Flow.Launcher.Infrastructure/Logger/Log.cs b/Flow.Launcher.Infrastructure/Logger/Log.cs
index 09eb98f46..2a5b826a9 100644
--- a/Flow.Launcher.Infrastructure/Logger/Log.cs
+++ b/Flow.Launcher.Infrastructure/Logger/Log.cs
@@ -34,7 +34,7 @@ namespace Flow.Launcher.Infrastructure.Logger
var fileTarget = new FileTarget
{
- FileName = CurrentLogDirectory.Replace(@"\", "/") + "/${shortdate}.txt",
+ FileName = CurrentLogDirectory.Replace(@"\", "/") + "/Flow.Launcher.${date:format=yyyy-MM-dd}.log",
Layout = layout
};
@@ -65,26 +65,22 @@ namespace Flow.Launcher.Infrastructure.Logger
public static void SetLogLevel(LOGLEVEL level)
{
- switch (level)
+ var rule = LogManager.Configuration.FindRuleByName("file");
+
+ var nlogLevel = level switch
{
- case LOGLEVEL.DEBUG:
- UseDebugLogLevel();
- break;
- default:
- UseInfoLogLevel();
- break;
- }
- Info(nameof(Logger), $"Using log level: {level}.");
- }
+ LOGLEVEL.NONE => LogLevel.Off,
+ LOGLEVEL.ERROR => LogLevel.Error,
+ LOGLEVEL.DEBUG => LogLevel.Debug,
+ _ => LogLevel.Info
+ };
- private static void UseDebugLogLevel()
- {
- LogManager.Configuration.FindRuleByName("file").SetLoggingLevels(LogLevel.Debug, LogLevel.Fatal);
- }
+ rule.SetLoggingLevels(nlogLevel, LogLevel.Fatal);
- private static void UseInfoLogLevel()
- {
- LogManager.Configuration.FindRuleByName("file").SetLoggingLevels(LogLevel.Info, LogLevel.Fatal);
+ LogManager.ReconfigExistingLoggers();
+
+ // We can't log Info when level is set to Error or None, so we use Debug
+ Debug(nameof(Logger), $"Using log level: {level}.");
}
private static void LogFaultyFormat(string message)
@@ -169,7 +165,9 @@ namespace Flow.Launcher.Infrastructure.Logger
public enum LOGLEVEL
{
- DEBUG,
- INFO
+ NONE,
+ ERROR,
+ INFO,
+ DEBUG
}
}
diff --git a/Flow.Launcher.Infrastructure/UserSettings/CustomBrowserViewModel.cs b/Flow.Launcher.Infrastructure/UserSettings/CustomBrowserViewModel.cs
index 24584115d..9c795f952 100644
--- a/Flow.Launcher.Infrastructure/UserSettings/CustomBrowserViewModel.cs
+++ b/Flow.Launcher.Infrastructure/UserSettings/CustomBrowserViewModel.cs
@@ -1,11 +1,18 @@
+using System.Text.Json.Serialization;
+using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Plugin;
-using System.Text.Json.Serialization;
namespace Flow.Launcher.Infrastructure.UserSettings
{
public class CustomBrowserViewModel : BaseModel
{
+ // We should not initialize API in static constructor because it will create another API instance
+ private static IPublicAPI api = null;
+ private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService();
+
public string Name { get; set; }
+ [JsonIgnore]
+ public string DisplayName => Name == "Default" ? API.GetTranslation("defaultBrowser_default") : Name;
public string Path { get; set; }
public string PrivateArg { get; set; }
public bool EnablePrivate { get; set; }
@@ -26,8 +33,10 @@ namespace Flow.Launcher.Infrastructure.UserSettings
Editable = Editable
};
}
+
+ public void OnDisplayNameChanged()
+ {
+ OnPropertyChanged(nameof(DisplayName));
+ }
}
}
-
-
-
diff --git a/Flow.Launcher.Infrastructure/UserSettings/CustomExplorerViewModel.cs b/Flow.Launcher.Infrastructure/UserSettings/CustomExplorerViewModel.cs
index c54c30478..2af0bb0e5 100644
--- a/Flow.Launcher.Infrastructure/UserSettings/CustomExplorerViewModel.cs
+++ b/Flow.Launcher.Infrastructure/UserSettings/CustomExplorerViewModel.cs
@@ -1,10 +1,18 @@
-using Flow.Launcher.Plugin;
+using System.Text.Json.Serialization;
+using CommunityToolkit.Mvvm.DependencyInjection;
+using Flow.Launcher.Plugin;
-namespace Flow.Launcher.ViewModel
+namespace Flow.Launcher.Infrastructure.UserSettings
{
public class CustomExplorerViewModel : BaseModel
{
+ // We should not initialize API in static constructor because it will create another API instance
+ private static IPublicAPI api = null;
+ private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService();
+
public string Name { get; set; }
+ [JsonIgnore]
+ public string DisplayName => Name == "Explorer" ? API.GetTranslation("fileManagerExplorer") : Name;
public string Path { get; set; }
public string FileArgument { get; set; } = "\"%d\"";
public string DirectoryArgument { get; set; } = "\"%d\"";
@@ -21,5 +29,10 @@ namespace Flow.Launcher.ViewModel
Editable = Editable
};
}
+
+ public void OnDisplayNameChanged()
+ {
+ OnPropertyChanged(nameof(DisplayName));
+ }
}
}
diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs
index 0c3402050..f70c4559b 100644
--- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs
+++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs
@@ -1,4 +1,4 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text.Json.Serialization;
using System.Windows;
@@ -9,7 +9,6 @@ using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.Storage;
using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.SharedModels;
-using Flow.Launcher.ViewModel;
namespace Flow.Launcher.Infrastructure.UserSettings
{
diff --git a/Flow.Launcher.Infrastructure/Win32Helper.cs b/Flow.Launcher.Infrastructure/Win32Helper.cs
index 811733925..5d30b740d 100644
--- a/Flow.Launcher.Infrastructure/Win32Helper.cs
+++ b/Flow.Launcher.Infrastructure/Win32Helper.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
@@ -904,5 +904,19 @@ namespace Flow.Launcher.Infrastructure
}
#endregion
+
+ #region File / Folder Dialog
+
+ public static string SelectFile()
+ {
+ var dlg = new OpenFileDialog();
+ var result = dlg.ShowDialog();
+ if (result == true)
+ return dlg.FileName;
+
+ return string.Empty;
+ }
+
+ #endregion
}
}
diff --git a/Flow.Launcher.Infrastructure/packages.lock.json b/Flow.Launcher.Infrastructure/packages.lock.json
index abd250f7c..47c94d5f6 100644
--- a/Flow.Launcher.Infrastructure/packages.lock.json
+++ b/Flow.Launcher.Infrastructure/packages.lock.json
@@ -25,9 +25,9 @@
},
"Fody": {
"type": "Direct",
- "requested": "[6.9.2, )",
- "resolved": "6.9.2",
- "contentHash": "YBHobPGogb0vYhGYIxn/ndWqTjNWZveDi5jdjrcshL2vjwU3gQGyDeI7vGgye+2rAM5fGRvlLgNWLW3DpviS/w=="
+ "requested": "[6.9.3, )",
+ "resolved": "6.9.3",
+ "contentHash": "1CUGgFdyECDKgi5HaUBhdv6k+VG9Iy4OCforGfHyar3xQXAJypZkzymgKtWj/4SPd6nSG0Qi7NH71qHrDSZLaA=="
},
"InputSimulator": {
"type": "Direct",
@@ -58,9 +58,9 @@
},
"Microsoft.Windows.CsWin32": {
"type": "Direct",
- "requested": "[0.3.183, )",
- "resolved": "0.3.183",
- "contentHash": "Ze3aE2y7xgzKxEWtNb4SH0CExXpCHr3sbmwnvMiWMzJhWDX/G4Rs5wgg2UNs3VN+qVHh/DkDWLCPaVQv/b//Nw==",
+ "requested": "[0.3.205, )",
+ "resolved": "0.3.205",
+ "contentHash": "U5wGAnyKd7/I2YMd43nogm81VMtjiKzZ9dsLMVI4eAB7jtv5IEj0gprj0q/F3iRmAIaGv5omOf8iSYx2+nE6BQ==",
"dependencies": {
"Microsoft.Windows.SDK.Win32Docs": "0.1.42-alpha",
"Microsoft.Windows.SDK.Win32Metadata": "61.0.15-preview",
@@ -78,17 +78,17 @@
},
"NLog": {
"type": "Direct",
- "requested": "[6.0.1, )",
- "resolved": "6.0.1",
- "contentHash": "qDWiqy8/xdpZKtHna/645KbalwP86N2NFJEzfqhcv+Si4V2iNaEfR/dCneuF/4+Dcwl3f7jHMXj3ndWYftV3Ug=="
+ "requested": "[6.0.4, )",
+ "resolved": "6.0.4",
+ "contentHash": "Xr+lIk1ZlTTFXEqnxQVLxrDqZlt2tm5X+/AhJbaY2emb/dVtGDiU5QuEtj3gHtwV/SWlP/rJ922I/BPuOJXlRw=="
},
"NLog.OutputDebugString": {
"type": "Direct",
- "requested": "[6.0.1, )",
- "resolved": "6.0.1",
- "contentHash": "wwJCQLaHVzuRf8TsXB+EEdrzVvE3dnzCSMQMDgwkw3AXp8VSp3JSVF/Q/H0oEqggKgKhPs13hh3a7svyQr4s3A==",
+ "requested": "[6.0.4, )",
+ "resolved": "6.0.4",
+ "contentHash": "TOP2Ap9BbE98B/l/TglnguowOD0rXo8B/20xAgvj9shO/kf6IJ5M4QMhVxq72mrneJ/ANhHY7Jcd+xJbzuI5PA==",
"dependencies": {
- "NLog": "6.0.1"
+ "NLog": "6.0.4"
}
},
"PropertyChanged.Fody": {
@@ -102,9 +102,9 @@
},
"SharpVectors.Wpf": {
"type": "Direct",
- "requested": "[1.8.4.2, )",
- "resolved": "1.8.4.2",
- "contentHash": "PNxLkMBJnV8A+6yH9OqOlhLJegvWP/dvh0rAJp2l0kcrR+rB4R2tQ9vhUqka+UilH4atN8T6zvjDOizVyfz2Ng=="
+ "requested": "[1.8.5, )",
+ "resolved": "1.8.5",
+ "contentHash": "WURdBDq5AE8RjKV9pFS7lNkJe81gxja9SaMGE4URq9GJUZ6M+5DGUL0Lm3B0iYW2/Meyowaz4ffGsyW+RBSTtg=="
},
"System.Drawing.Common": {
"type": "Direct",
@@ -123,8 +123,8 @@
},
"JetBrains.Annotations": {
"type": "Transitive",
- "resolved": "2024.3.0",
- "contentHash": "ox5pkeLQXjvJdyAB4b2sBYAlqZGLh3PjSnP1bQNVx72ONuTJ9+34/+Rq91Fc0dG29XG9RgZur9+NcP4riihTug=="
+ "resolved": "2025.2.2",
+ "contentHash": "0X56ZRizuHdrnPpgXjWV7f2tQO1FlQg5O1967OGKnI/4ZRNOK642J8L7brM1nYvrxTTU5TP1yRyXLRLaXLPQ8A=="
},
"MemoryPack.Core": {
"type": "Transitive",
@@ -190,7 +190,7 @@
"flow.launcher.plugin": {
"type": "Project",
"dependencies": {
- "JetBrains.Annotations": "[2024.3.0, )"
+ "JetBrains.Annotations": "[2025.2.2, )"
}
}
}
diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj
index ae2454279..1ae0b1f58 100644
--- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj
+++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj
@@ -1,4 +1,4 @@
-
+
net9.0-windows
@@ -68,13 +68,13 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/Flow.Launcher.Plugin/packages.lock.json b/Flow.Launcher.Plugin/packages.lock.json
index af835c598..70f71f20d 100644
--- a/Flow.Launcher.Plugin/packages.lock.json
+++ b/Flow.Launcher.Plugin/packages.lock.json
@@ -4,15 +4,15 @@
"net9.0-windows7.0": {
"Fody": {
"type": "Direct",
- "requested": "[6.9.2, )",
- "resolved": "6.9.2",
- "contentHash": "YBHobPGogb0vYhGYIxn/ndWqTjNWZveDi5jdjrcshL2vjwU3gQGyDeI7vGgye+2rAM5fGRvlLgNWLW3DpviS/w=="
+ "requested": "[6.9.3, )",
+ "resolved": "6.9.3",
+ "contentHash": "1CUGgFdyECDKgi5HaUBhdv6k+VG9Iy4OCforGfHyar3xQXAJypZkzymgKtWj/4SPd6nSG0Qi7NH71qHrDSZLaA=="
},
"JetBrains.Annotations": {
"type": "Direct",
- "requested": "[2024.3.0, )",
- "resolved": "2024.3.0",
- "contentHash": "ox5pkeLQXjvJdyAB4b2sBYAlqZGLh3PjSnP1bQNVx72ONuTJ9+34/+Rq91Fc0dG29XG9RgZur9+NcP4riihTug=="
+ "requested": "[2025.2.2, )",
+ "resolved": "2025.2.2",
+ "contentHash": "0X56ZRizuHdrnPpgXjWV7f2tQO1FlQg5O1967OGKnI/4ZRNOK642J8L7brM1nYvrxTTU5TP1yRyXLRLaXLPQ8A=="
},
"Microsoft.SourceLink.GitHub": {
"type": "Direct",
@@ -26,9 +26,9 @@
},
"Microsoft.Windows.CsWin32": {
"type": "Direct",
- "requested": "[0.3.183, )",
- "resolved": "0.3.183",
- "contentHash": "Ze3aE2y7xgzKxEWtNb4SH0CExXpCHr3sbmwnvMiWMzJhWDX/G4Rs5wgg2UNs3VN+qVHh/DkDWLCPaVQv/b//Nw==",
+ "requested": "[0.3.205, )",
+ "resolved": "0.3.205",
+ "contentHash": "U5wGAnyKd7/I2YMd43nogm81VMtjiKzZ9dsLMVI4eAB7jtv5IEj0gprj0q/F3iRmAIaGv5omOf8iSYx2+nE6BQ==",
"dependencies": {
"Microsoft.Windows.SDK.Win32Docs": "0.1.42-alpha",
"Microsoft.Windows.SDK.Win32Metadata": "61.0.15-preview",
diff --git a/Flow.Launcher.Test/Flow.Launcher.Test.csproj b/Flow.Launcher.Test/Flow.Launcher.Test.csproj
index 33479c5a0..11ccff05b 100644
--- a/Flow.Launcher.Test/Flow.Launcher.Test.csproj
+++ b/Flow.Launcher.Test/Flow.Launcher.Test.csproj
@@ -39,6 +39,7 @@
+
@@ -49,8 +50,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/Flow.Launcher.Test/Plugins/CalculatorTest.cs b/Flow.Launcher.Test/Plugins/CalculatorTest.cs
new file mode 100644
index 000000000..b075813db
--- /dev/null
+++ b/Flow.Launcher.Test/Plugins/CalculatorTest.cs
@@ -0,0 +1,92 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using Flow.Launcher.Plugin.Calculator;
+using Mages.Core;
+using NUnit.Framework;
+using NUnit.Framework.Legacy;
+
+namespace Flow.Launcher.Test.Plugins
+{
+ [TestFixture]
+ public class CalculatorPluginTest
+ {
+ private readonly Main _plugin;
+ private readonly Settings _settings = new()
+ {
+ DecimalSeparator = DecimalSeparator.UseSystemLocale,
+ MaxDecimalPlaces = 10,
+ ShowErrorMessage = false // Make sure we return the empty results when error occurs
+ };
+ private readonly Engine _engine = new(new Configuration
+ {
+ Scope = new Dictionary
+ {
+ { "e", Math.E }, // e is not contained in the default mages engine
+ }
+ });
+
+ public CalculatorPluginTest()
+ {
+ _plugin = new Main();
+
+ var settingField = typeof(Main).GetField("_settings", BindingFlags.NonPublic | BindingFlags.Instance);
+ if (settingField == null)
+ Assert.Fail("Could not find field '_settings' on Flow.Launcher.Plugin.Calculator.Main");
+ settingField.SetValue(_plugin, _settings);
+
+ var engineField = typeof(Main).GetField("MagesEngine", BindingFlags.NonPublic | BindingFlags.Static);
+ if (engineField == null)
+ Assert.Fail("Could not find static field 'MagesEngine' on Flow.Launcher.Plugin.Calculator.Main");
+ engineField.SetValue(null, _engine);
+ }
+
+ // Basic operations
+ [TestCase(@"1+1", "2")]
+ [TestCase(@"2-1", "1")]
+ [TestCase(@"2*2", "4")]
+ [TestCase(@"4/2", "2")]
+ [TestCase(@"2^3", "8")]
+ // Decimal places
+ [TestCase(@"10/3", "3.3333333333")]
+ // Parentheses
+ [TestCase(@"(1+2)*3", "9")]
+ [TestCase(@"2^(1+2)", "8")]
+ // Functions
+ [TestCase(@"pow(2,3)", "8")]
+ [TestCase(@"min(1,-1,-2)", "-2")]
+ [TestCase(@"max(1,-1,-2)", "1")]
+ [TestCase(@"sqrt(16)", "4")]
+ [TestCase(@"sin(pi)", "0.0000000000")]
+ [TestCase(@"cos(0)", "1")]
+ [TestCase(@"tan(0)", "0")]
+ [TestCase(@"log10(100)", "2")]
+ [TestCase(@"log(100)", "2")]
+ [TestCase(@"log2(8)", "3")]
+ [TestCase(@"ln(e)", "1")]
+ [TestCase(@"abs(-5)", "5")]
+ // Constants
+ [TestCase(@"pi", "3.1415926536")]
+ // Complex expressions
+ [TestCase(@"(2+3)*sqrt(16)-log(100)/ln(e)", "18")]
+ [TestCase(@"sin(pi/2)+cos(0)+tan(0)", "2")]
+ // Error handling (should return empty result)
+ [TestCase(@"10/0", "")]
+ [TestCase(@"sqrt(-1)", "")]
+ [TestCase(@"log(0)", "")]
+ [TestCase(@"invalid_expression", "")]
+ public void CalculatorTest(string expression, string result)
+ {
+ ClassicAssert.AreEqual(GetCalculationResult(expression), result);
+ }
+
+ private string GetCalculationResult(string expression)
+ {
+ var results = _plugin.Query(new Plugin.Query()
+ {
+ Search = expression
+ });
+ return results.Count > 0 ? results[0].Title : string.Empty;
+ }
+ }
+}
diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs
index 55d974c2e..480c78c66 100644
--- a/Flow.Launcher/App.xaml.cs
+++ b/Flow.Launcher/App.xaml.cs
@@ -45,6 +45,7 @@ namespace Flow.Launcher
private static Settings _settings;
private static MainWindow _mainWindow;
private readonly MainViewModel _mainVM;
+ private readonly Internationalization _internationalization;
// To prevent two disposals running at the same time.
private static readonly object _disposingLock = new();
@@ -107,6 +108,7 @@ namespace Flow.Launcher
API = Ioc.Default.GetRequiredService();
_settings.Initialize();
_mainVM = Ioc.Default.GetRequiredService();
+ _internationalization = Ioc.Default.GetRequiredService();
}
catch (Exception e)
{
@@ -195,7 +197,7 @@ namespace Flow.Launcher
Win32Helper.EnableWin32DarkMode(_settings.ColorScheme);
// Initialize language before portable clean up since it needs translations
- await Ioc.Default.GetRequiredService().InitializeLanguageAsync();
+ await _internationalization.InitializeLanguageAsync();
// Clean up after portability update
Ioc.Default.GetRequiredService().PreStartCleanUpAfterPortabilityUpdate();
@@ -438,6 +440,7 @@ namespace Flow.Launcher
_mainWindow?.Dispatcher.Invoke(_mainWindow.Dispose);
_mainVM?.Dispose();
DialogJump.Dispose();
+ _internationalization.Dispose();
}
API.LogInfo(ClassName, "End Flow Launcher dispose ----------------------------------------------------");
diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj
index fa23d8886..a99d4d8c2 100644
--- a/Flow.Launcher/Flow.Launcher.csproj
+++ b/Flow.Launcher/Flow.Launcher.csproj
@@ -40,49 +40,11 @@
-
+
-
+
@@ -132,7 +94,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
@@ -141,8 +103,8 @@
-
-
+
+
@@ -152,7 +114,7 @@
-
+
diff --git a/Flow.Launcher/Helper/WallpaperPathRetrieval.cs b/Flow.Launcher/Helper/WallpaperPathRetrieval.cs
index 93b9a8aaa..fd04b3e88 100644
--- a/Flow.Launcher/Helper/WallpaperPathRetrieval.cs
+++ b/Flow.Launcher/Helper/WallpaperPathRetrieval.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Threading;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
@@ -16,7 +17,7 @@ public static class WallpaperPathRetrieval
private const int MaxCacheSize = 3;
private static readonly Dictionary<(string, DateTime), ImageBrush> WallpaperCache = new();
- private static readonly object CacheLock = new();
+ private static readonly Lock CacheLock = new();
public static Brush GetWallpaperBrush()
{
@@ -31,7 +32,7 @@ public static class WallpaperPathRetrieval
var wallpaperPath = Win32Helper.GetWallpaperPath();
if (string.IsNullOrEmpty(wallpaperPath) || !File.Exists(wallpaperPath))
{
- App.API.LogInfo(ClassName, $"Wallpaper path is invalid: {wallpaperPath}");
+ App.API.LogError(ClassName, $"Wallpaper path is invalid: {wallpaperPath}");
var wallpaperColor = GetWallpaperColor();
return new SolidColorBrush(wallpaperColor);
}
@@ -47,17 +48,22 @@ public static class WallpaperPathRetrieval
return cachedWallpaper;
}
}
-
- using var fileStream = File.OpenRead(wallpaperPath);
- var decoder = BitmapDecoder.Create(fileStream, BitmapCreateOptions.DelayCreation, BitmapCacheOption.None);
- var frame = decoder.Frames[0];
- var originalWidth = frame.PixelWidth;
- var originalHeight = frame.PixelHeight;
+
+ int originalWidth, originalHeight;
+ // Use `using ()` instead of `using var` sentence here to ensure the wallpaper file is not locked
+ using (var fileStream = File.OpenRead(wallpaperPath))
+ {
+ var decoder = BitmapDecoder.Create(fileStream, BitmapCreateOptions.DelayCreation, BitmapCacheOption.None);
+ var frame = decoder.Frames[0];
+ originalWidth = frame.PixelWidth;
+ originalHeight = frame.PixelHeight;
+ }
if (originalWidth == 0 || originalHeight == 0)
{
- App.API.LogInfo(ClassName, $"Failed to load bitmap: Width={originalWidth}, Height={originalHeight}");
- return new SolidColorBrush(Colors.Transparent);
+ App.API.LogError(ClassName, $"Failed to load bitmap: Width={originalWidth}, Height={originalHeight}");
+ var wallpaperColor = GetWallpaperColor();
+ return new SolidColorBrush(wallpaperColor);
}
// Calculate the scaling factor to fit the image within 800x600 while preserving aspect ratio
@@ -70,7 +76,9 @@ public static class WallpaperPathRetrieval
// Set DecodePixelWidth and DecodePixelHeight to resize the image while preserving aspect ratio
var bitmap = new BitmapImage();
bitmap.BeginInit();
+ bitmap.CacheOption = BitmapCacheOption.OnLoad; // Use OnLoad to ensure the wallpaper file is not locked
bitmap.UriSource = new Uri(wallpaperPath);
+ bitmap.CreateOptions = BitmapCreateOptions.IgnoreColorProfile;
bitmap.DecodePixelWidth = decodedPixelWidth;
bitmap.DecodePixelHeight = decodedPixelHeight;
bitmap.EndInit();
@@ -104,13 +112,13 @@ public static class WallpaperPathRetrieval
private static Color GetWallpaperColor()
{
- RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Control Panel\Colors", false);
+ using var key = Registry.CurrentUser.OpenSubKey(@"Control Panel\Colors", false);
var result = key?.GetValue("Background", null);
if (result is string strResult)
{
try
{
- var parts = strResult.Trim().Split(new[] { ' ' }, 3).Select(byte.Parse).ToList();
+ var parts = strResult.Trim().Split([' '], 3).Select(byte.Parse).ToList();
return Color.FromRgb(parts[0], parts[1], parts[2]);
}
catch (Exception ex)
diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml
index d2f78e1f6..626fe1385 100644
--- a/Flow.Launcher/Languages/en.xaml
+++ b/Flow.Launcher/Languages/en.xaml
@@ -219,6 +219,7 @@
Fail to uninstall {0}
Unable to find plugin.json from the extracted zip file, or this path {0} does not exist
A plugin with the same ID and version already exists, or the version is greater than this downloaded plugin
+ Error creating setting panel for plugin {0}:{1}{2}
Plugin Store
@@ -462,8 +463,10 @@
Open Folder
Advanced
Log Level
- Debug
+ Silent
+ Error
Info
+ Debug
Setting Window Font
@@ -485,6 +488,7 @@
Arg For File
The file manager '{0}' could not be located at '{1}'. Would you like to continue?
File Manager Path Error
+ File Explorer
Default Web Browser
@@ -495,6 +499,8 @@
New Window
New Tab
Private Mode
+ Default
+ New Profile
Change Priority
diff --git a/Flow.Launcher/SelectBrowserWindow.xaml b/Flow.Launcher/SelectBrowserWindow.xaml
index d51d597b7..67c22b07d 100644
--- a/Flow.Launcher/SelectBrowserWindow.xaml
+++ b/Flow.Launcher/SelectBrowserWindow.xaml
@@ -92,7 +92,7 @@
SelectedIndex="{Binding SelectedCustomBrowserIndex}">
-
+
diff --git a/Flow.Launcher/SelectBrowserWindow.xaml.cs b/Flow.Launcher/SelectBrowserWindow.xaml.cs
index 565b4cbc3..290712aad 100644
--- a/Flow.Launcher/SelectBrowserWindow.xaml.cs
+++ b/Flow.Launcher/SelectBrowserWindow.xaml.cs
@@ -1,6 +1,7 @@
using System.Windows;
using System.Windows.Controls;
using CommunityToolkit.Mvvm.DependencyInjection;
+using Flow.Launcher.Infrastructure;
using Flow.Launcher.ViewModel;
namespace Flow.Launcher
@@ -31,7 +32,7 @@ namespace Flow.Launcher
private void btnBrowseFile_Click(object sender, RoutedEventArgs e)
{
- var selectedFilePath = _viewModel.SelectFile();
+ var selectedFilePath = Win32Helper.SelectFile();
if (!string.IsNullOrEmpty(selectedFilePath))
{
diff --git a/Flow.Launcher/SelectFileManagerWindow.xaml b/Flow.Launcher/SelectFileManagerWindow.xaml
index b3b219d1c..cd4bec424 100644
--- a/Flow.Launcher/SelectFileManagerWindow.xaml
+++ b/Flow.Launcher/SelectFileManagerWindow.xaml
@@ -102,7 +102,7 @@
SelectedIndex="{Binding SelectedCustomExplorerIndex}">
-
+
diff --git a/Flow.Launcher/SelectFileManagerWindow.xaml.cs b/Flow.Launcher/SelectFileManagerWindow.xaml.cs
index d9c672aff..5143f9a56 100644
--- a/Flow.Launcher/SelectFileManagerWindow.xaml.cs
+++ b/Flow.Launcher/SelectFileManagerWindow.xaml.cs
@@ -2,6 +2,7 @@
using System.Windows.Controls;
using System.Windows.Navigation;
using CommunityToolkit.Mvvm.DependencyInjection;
+using Flow.Launcher.Infrastructure;
using Flow.Launcher.ViewModel;
namespace Flow.Launcher
@@ -32,13 +33,13 @@ namespace Flow.Launcher
private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e)
{
- _viewModel.OpenUrl(e.Uri.AbsoluteUri);
+ App.API.OpenUrl(e.Uri.AbsoluteUri);
e.Handled = true;
}
private void btnBrowseFile_Click(object sender, RoutedEventArgs e)
{
- var selectedFilePath = _viewModel.SelectFile();
+ var selectedFilePath = Win32Helper.SelectFile();
if (!string.IsNullOrEmpty(selectedFilePath))
{
diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs
index 1efc89972..647b36701 100644
--- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs
+++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs
@@ -231,35 +231,41 @@ public partial class SettingsPaneAboutViewModel : BaseModel
}
});
- // Firstly, delete plugin cache directories
- pluginCacheDirectory.EnumerateDirectories("*", SearchOption.TopDirectoryOnly)
- .ToList()
- .ForEach(dir =>
+ // Check if plugin cache directory exists before attempting to delete
+ // Or it will throw DirectoryNotFoundException in `pluginCacheDirectory.EnumerateDirectories`
+ if (pluginCacheDirectory.Exists)
+ {
+ // Firstly, delete plugin cache directories
+ pluginCacheDirectory.EnumerateDirectories("*", SearchOption.TopDirectoryOnly)
+ .ToList()
+ .ForEach(dir =>
+ {
+ try
+ {
+ // Plugin may create directories in its cache directory
+ dir.Delete(recursive: true);
+ }
+ catch (Exception e)
+ {
+ App.API.LogException(ClassName, $"Failed to delete cache directory: {dir.Name}", e);
+ success = false;
+ }
+ });
+
+ // Then, delete plugin directory
+ var dir = pluginCacheDirectory;
+ try
{
- try
- {
- // Plugin may create directories in its cache directory
- dir.Delete(recursive: true);
- }
- catch (Exception e)
- {
- App.API.LogException(ClassName, $"Failed to delete cache directory: {dir.Name}", e);
- success = false;
- }
- });
-
- // Then, delete plugin directory
- var dir = GetPluginCacheDir();
- try
- {
- dir.Delete(recursive: false);
- }
- catch (Exception e)
- {
- App.API.LogException(ClassName, $"Failed to delete cache directory: {dir.Name}", e);
- success = false;
+ dir.Delete(recursive: false);
+ }
+ catch (Exception e)
+ {
+ App.API.LogException(ClassName, $"Failed to delete cache directory: {dir.Name}", e);
+ success = false;
+ }
}
+ // Raise regardless to cover scenario where size needs to be recalculated if the folder is manually removed on disk.
OnPropertyChanged(nameof(CacheFolderSize));
return success;
diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs
index ec75ddf90..b47b53654 100644
--- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs
+++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
@@ -219,6 +219,8 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
DropdownDataGeneric.UpdateLabels(DialogJumpFileResultBehaviours);
// Since we are using Binding instead of DynamicResource, we need to manually trigger the update
OnPropertyChanged(nameof(AlwaysPreviewToolTip));
+ Settings.CustomExplorer.OnDisplayNameChanged();
+ Settings.CustomBrowser.OnDisplayNameChanged();
}
public string Language
diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml b/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml
index 81e15df69..07cc7b6a7 100644
--- a/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml
+++ b/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml
@@ -403,7 +403,7 @@
MaxWidth="250"
Margin="10 0 0 0"
Command="{Binding SelectFileManagerCommand}"
- Content="{Binding Settings.CustomExplorer.Name}" />
+ Content="{Binding Settings.CustomExplorer.DisplayName}" />
+ Content="{Binding Settings.CustomBrowser.DisplayName}" />
diff --git a/Flow.Launcher/ViewModel/PluginViewModel.cs b/Flow.Launcher/ViewModel/PluginViewModel.cs
index 41181f58d..bcc502f44 100644
--- a/Flow.Launcher/ViewModel/PluginViewModel.cs
+++ b/Flow.Launcher/ViewModel/PluginViewModel.cs
@@ -1,4 +1,5 @@
-using System.Threading.Tasks;
+using System;
+using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
@@ -14,8 +15,13 @@ namespace Flow.Launcher.ViewModel
{
public partial class PluginViewModel : BaseModel
{
+ private static readonly string ClassName = nameof(PluginViewModel);
+
private static readonly Settings Settings = Ioc.Default.GetRequiredService();
+ private static readonly Thickness SettingPanelMargin = (Thickness)Application.Current.FindResource("SettingPanelMargin");
+ private static readonly Thickness SettingPanelItemTopBottomMargin = (Thickness)Application.Current.FindResource("SettingPanelItemTopBottomMargin");
+
private readonly PluginPair _pluginPair;
public PluginPair PluginPair
{
@@ -135,11 +141,30 @@ namespace Flow.Launcher.ViewModel
=> IsExpanded
? _settingControl
??= HasSettingControl
- ? ((ISettingProvider)PluginPair.Plugin).CreateSettingPanel()
+ ? TryCreateSettingPanel(PluginPair)
: null
: null;
private ImageSource _image = ImageLoader.MissingImage;
+ private static Control TryCreateSettingPanel(PluginPair pair)
+ {
+ try
+ {
+ // We can safely cast here as we already check this in HasSettingControl
+ return ((ISettingProvider)pair.Plugin).CreateSettingPanel();
+ }
+ catch (Exception e)
+ {
+ // Log exception
+ App.API.LogException(ClassName, $"Failed to create setting panel for {pair.Metadata.Name}", e);
+
+ // Show error message in UI
+ var errorMsg = string.Format(App.API.GetTranslation("errorCreatingSettingPanel"),
+ pair.Metadata.Name, Environment.NewLine, e.Message);
+ return CreateErrorSettingPanel(errorMsg);
+ }
+ }
+
public Visibility ActionKeywordsVisibility => PluginPair.Metadata.HideActionKeywordPanel ?
Visibility.Collapsed : Visibility.Visible;
public string InitializeTime => PluginPair.Metadata.InitTime + "ms";
@@ -190,5 +215,28 @@ namespace Flow.Launcher.ViewModel
var changeKeywordsWindow = new ActionKeywords(this);
changeKeywordsWindow.ShowDialog();
}
+
+ private static UserControl CreateErrorSettingPanel(string text)
+ {
+ var grid = new Grid()
+ {
+ Margin = SettingPanelMargin
+ };
+ var textBox = new TextBox
+ {
+ Text = text,
+ IsReadOnly = true,
+ HorizontalAlignment = HorizontalAlignment.Stretch,
+ VerticalAlignment = VerticalAlignment.Top,
+ TextWrapping = TextWrapping.Wrap,
+ Margin = SettingPanelItemTopBottomMargin
+ };
+ textBox.SetResourceReference(TextBox.ForegroundProperty, "Color04B");
+ grid.Children.Add(textBox);
+ return new UserControl
+ {
+ Content = grid
+ };
+ }
}
}
diff --git a/Flow.Launcher/ViewModel/SelectBrowserViewModel.cs b/Flow.Launcher/ViewModel/SelectBrowserViewModel.cs
index 67bbbd930..e3a0e4e44 100644
--- a/Flow.Launcher/ViewModel/SelectBrowserViewModel.cs
+++ b/Flow.Launcher/ViewModel/SelectBrowserViewModel.cs
@@ -17,8 +17,13 @@ public partial class SelectBrowserViewModel : BaseModel
get => selectedCustomBrowserIndex;
set
{
- selectedCustomBrowserIndex = value;
- OnPropertyChanged(nameof(CustomBrowser));
+ // When one custom browser is selected and removed, the index will become -1, so we need to ignore this change
+ if (value < 0) return;
+ if (selectedCustomBrowserIndex != value)
+ {
+ selectedCustomBrowserIndex = value;
+ OnPropertyChanged(nameof(CustomBrowser));
+ }
}
}
@@ -40,22 +45,12 @@ public partial class SelectBrowserViewModel : BaseModel
return true;
}
- internal string SelectFile()
- {
- var dlg = new Microsoft.Win32.OpenFileDialog();
- var result = dlg.ShowDialog();
- if (result == true)
- return dlg.FileName;
-
- return string.Empty;
- }
-
[RelayCommand]
private void Add()
{
CustomBrowsers.Add(new()
{
- Name = "New Profile"
+ Name = App.API.GetTranslation("defaultBrowser_new_profile")
});
SelectedCustomBrowserIndex = CustomBrowsers.Count - 1;
}
diff --git a/Flow.Launcher/ViewModel/SelectFileManagerViewModel.cs b/Flow.Launcher/ViewModel/SelectFileManagerViewModel.cs
index 77f004980..f6a32e3fe 100644
--- a/Flow.Launcher/ViewModel/SelectFileManagerViewModel.cs
+++ b/Flow.Launcher/ViewModel/SelectFileManagerViewModel.cs
@@ -21,6 +21,8 @@ public partial class SelectFileManagerViewModel : BaseModel
get => selectedCustomExplorerIndex;
set
{
+ // When one custom file manager is selected and removed, the index will become -1, so we need to ignore this change
+ if (value < 0) return;
if (selectedCustomExplorerIndex != value)
{
selectedCustomExplorerIndex = value;
@@ -98,27 +100,12 @@ public partial class SelectFileManagerViewModel : BaseModel
}
}
- internal void OpenUrl(string absoluteUri)
- {
- App.API.OpenUrl(absoluteUri);
- }
-
- internal string SelectFile()
- {
- var dlg = new Microsoft.Win32.OpenFileDialog();
- var result = dlg.ShowDialog();
- if (result == true)
- return dlg.FileName;
-
- return string.Empty;
- }
-
[RelayCommand]
private void Add()
{
CustomExplorers.Add(new()
{
- Name = "New Profile"
+ Name = App.API.GetTranslation("defaultBrowser_new_profile")
});
SelectedCustomExplorerIndex = CustomExplorers.Count - 1;
}
diff --git a/Flow.Launcher/packages.lock.json b/Flow.Launcher/packages.lock.json
index 32b78c334..c90db6b0c 100644
--- a/Flow.Launcher/packages.lock.json
+++ b/Flow.Launcher/packages.lock.json
@@ -16,9 +16,9 @@
},
"Fody": {
"type": "Direct",
- "requested": "[6.9.2, )",
- "resolved": "6.9.2",
- "contentHash": "YBHobPGogb0vYhGYIxn/ndWqTjNWZveDi5jdjrcshL2vjwU3gQGyDeI7vGgye+2rAM5fGRvlLgNWLW3DpviS/w=="
+ "requested": "[6.9.3, )",
+ "resolved": "6.9.3",
+ "contentHash": "1CUGgFdyECDKgi5HaUBhdv6k+VG9Iy4OCforGfHyar3xQXAJypZkzymgKtWj/4SPd6nSG0Qi7NH71qHrDSZLaA=="
},
"MdXaml": {
"type": "Direct",
@@ -71,41 +71,41 @@
},
"Microsoft.Extensions.DependencyInjection": {
"type": "Direct",
- "requested": "[9.0.7, )",
- "resolved": "9.0.7",
- "contentHash": "i05AYA91vgq0as84ROVCyltD2gnxaba/f1Qw2rG7mUsS0gv8cPTr1Gm7jPQHq7JTr4MJoQUcanLVs16tIOUJaQ==",
+ "requested": "[9.0.9, )",
+ "resolved": "9.0.9",
+ "contentHash": "zQV2WOSP+3z1EuK91ULxfGgo2Y75bTRnmJHp08+w/YXAyekZutX/qCd88/HOMNh35MDW9mJJJxPpMPS+1Rww8A==",
"dependencies": {
- "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.7"
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9"
}
},
"Microsoft.Extensions.Hosting": {
"type": "Direct",
- "requested": "[9.0.7, )",
- "resolved": "9.0.7",
- "contentHash": "Dkv55VfitwJjPUk9mFHxT9MJAd8su7eJNaCHhBU/Y9xFqw3ZNHwrpeptXeaXiaPtfQq+alMmawIz1Impk5pHkQ==",
+ "requested": "[9.0.9, )",
+ "resolved": "9.0.9",
+ "contentHash": "DmRsWH3g8yZGho/pLQ79hxhM2ctE1eDTZ/HbAnrD/uw8m+P2pRRJOoBVxlrhbhMP3/y3oAJoy0yITasfmilbTg==",
"dependencies": {
- "Microsoft.Extensions.Configuration": "9.0.7",
- "Microsoft.Extensions.Configuration.Abstractions": "9.0.7",
- "Microsoft.Extensions.Configuration.Binder": "9.0.7",
- "Microsoft.Extensions.Configuration.CommandLine": "9.0.7",
- "Microsoft.Extensions.Configuration.EnvironmentVariables": "9.0.7",
- "Microsoft.Extensions.Configuration.FileExtensions": "9.0.7",
- "Microsoft.Extensions.Configuration.Json": "9.0.7",
- "Microsoft.Extensions.Configuration.UserSecrets": "9.0.7",
- "Microsoft.Extensions.DependencyInjection": "9.0.7",
- "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.7",
- "Microsoft.Extensions.Diagnostics": "9.0.7",
- "Microsoft.Extensions.FileProviders.Abstractions": "9.0.7",
- "Microsoft.Extensions.FileProviders.Physical": "9.0.7",
- "Microsoft.Extensions.Hosting.Abstractions": "9.0.7",
- "Microsoft.Extensions.Logging": "9.0.7",
- "Microsoft.Extensions.Logging.Abstractions": "9.0.7",
- "Microsoft.Extensions.Logging.Configuration": "9.0.7",
- "Microsoft.Extensions.Logging.Console": "9.0.7",
- "Microsoft.Extensions.Logging.Debug": "9.0.7",
- "Microsoft.Extensions.Logging.EventLog": "9.0.7",
- "Microsoft.Extensions.Logging.EventSource": "9.0.7",
- "Microsoft.Extensions.Options": "9.0.7"
+ "Microsoft.Extensions.Configuration": "9.0.9",
+ "Microsoft.Extensions.Configuration.Abstractions": "9.0.9",
+ "Microsoft.Extensions.Configuration.Binder": "9.0.9",
+ "Microsoft.Extensions.Configuration.CommandLine": "9.0.9",
+ "Microsoft.Extensions.Configuration.EnvironmentVariables": "9.0.9",
+ "Microsoft.Extensions.Configuration.FileExtensions": "9.0.9",
+ "Microsoft.Extensions.Configuration.Json": "9.0.9",
+ "Microsoft.Extensions.Configuration.UserSecrets": "9.0.9",
+ "Microsoft.Extensions.DependencyInjection": "9.0.9",
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9",
+ "Microsoft.Extensions.Diagnostics": "9.0.9",
+ "Microsoft.Extensions.FileProviders.Abstractions": "9.0.9",
+ "Microsoft.Extensions.FileProviders.Physical": "9.0.9",
+ "Microsoft.Extensions.Hosting.Abstractions": "9.0.9",
+ "Microsoft.Extensions.Logging": "9.0.9",
+ "Microsoft.Extensions.Logging.Abstractions": "9.0.9",
+ "Microsoft.Extensions.Logging.Configuration": "9.0.9",
+ "Microsoft.Extensions.Logging.Console": "9.0.9",
+ "Microsoft.Extensions.Logging.Debug": "9.0.9",
+ "Microsoft.Extensions.Logging.EventLog": "9.0.9",
+ "Microsoft.Extensions.Logging.EventSource": "9.0.9",
+ "Microsoft.Extensions.Options": "9.0.9"
}
},
"Microsoft.Toolkit.Uwp.Notifications": {
@@ -154,9 +154,9 @@
},
"VirtualizingWrapPanel": {
"type": "Direct",
- "requested": "[2.3.0, )",
- "resolved": "2.3.0",
- "contentHash": "Dpmtcpn2HqAWZR0NkN7Qd4YCjf+sdQcemIMKm2suZVbOIB9NsmKZnYaQDIpXWTh87a9+nArVto6Od1cM2ohzCQ=="
+ "requested": "[2.3.1, )",
+ "resolved": "2.3.1",
+ "contentHash": "imph3SJqFFgX8vc7XRBcftfgzIL7Q+uE0Tvk7dbY0KY0tcqUCs0ZmKV3Gt9QX2745v6bSw6ns8UHpXtiptHqdA=="
},
"AvalonEdit": {
"type": "Transitive",
@@ -196,8 +196,8 @@
},
"FSharp.Core": {
"type": "Transitive",
- "resolved": "9.0.300",
- "contentHash": "TVt2J7RCE1KCS2IaONF+p8/KIZ1eHNbW+7qmKF6hGoD4tXl+o07ja1mPtFjMqRa5uHMFaTrGTPn/m945WnDLiQ=="
+ "resolved": "9.0.303",
+ "contentHash": "6JlV8aD8qQvcmfoe/PMOxCHXc0uX4lR23u0fAyQtnVQxYULLoTZgwgZHSnRcuUHOvS3wULFWcwdnP1iwslH60g=="
},
"HtmlAgilityPack": {
"type": "Transitive",
@@ -211,8 +211,8 @@
},
"JetBrains.Annotations": {
"type": "Transitive",
- "resolved": "2024.3.0",
- "contentHash": "ox5pkeLQXjvJdyAB4b2sBYAlqZGLh3PjSnP1bQNVx72ONuTJ9+34/+Rq91Fc0dG29XG9RgZur9+NcP4riihTug=="
+ "resolved": "2025.2.2",
+ "contentHash": "0X56ZRizuHdrnPpgXjWV7f2tQO1FlQg5O1967OGKnI/4ZRNOK642J8L7brM1nYvrxTTU5TP1yRyXLRLaXLPQ8A=="
},
"MemoryPack": {
"type": "Transitive",
@@ -249,249 +249,249 @@
},
"Meziantou.Framework.Win32.Jobs": {
"type": "Transitive",
- "resolved": "3.4.3",
- "contentHash": "REjInKnQ0OrhjjtSMPQtLtdURctCroB4L8Sd2gjTOYDysklvsdnrStx1tHS7uLv+fSyFF3aazZmo5Ka0v1oz/w=="
+ "resolved": "3.4.4",
+ "contentHash": "AivBzH5wM1NHBLehclim+o37SmireP7JxCRUoTilsc/h7LH9+YCPjb6Ig6y0khnQhFcO1P8RHYw4oiR15TGHUg=="
},
"Microsoft.Extensions.Configuration": {
"type": "Transitive",
- "resolved": "9.0.7",
- "contentHash": "oxGR51+w5cXm5B9gU6XwpAB2sTiyPSmZm7hjvv0rzRnmL5o/KZzE103AuQj7sK26OBupjVzU/bZxDWvvU4nhEg==",
+ "resolved": "9.0.9",
+ "contentHash": "w87wF/90/VI0ZQBhf4rbMEeyEy0vi2WKjFmACsNAKNaorY+ZlVz7ddyXkbADvaWouMKffNmR0yQOGcrvSSvKGg==",
"dependencies": {
- "Microsoft.Extensions.Configuration.Abstractions": "9.0.7",
- "Microsoft.Extensions.Primitives": "9.0.7"
+ "Microsoft.Extensions.Configuration.Abstractions": "9.0.9",
+ "Microsoft.Extensions.Primitives": "9.0.9"
}
},
"Microsoft.Extensions.Configuration.Abstractions": {
"type": "Transitive",
- "resolved": "9.0.7",
- "contentHash": "lut/kiVvNsQ120VERMUYSFhpXPpKjjql+giy03LesASPBBcC0o6+aoFdzJH9GaYpFTQ3fGVhVjKjvJDoAW5/IQ==",
+ "resolved": "9.0.9",
+ "contentHash": "p5RKAY9POvs3axwA/AQRuJeM8AHuE8h4qbP1NxQeGm0ep46aXz1oCLAp/oOYxX1GsjStgdhHrN3XXLLXr0+b3w==",
"dependencies": {
- "Microsoft.Extensions.Primitives": "9.0.7"
+ "Microsoft.Extensions.Primitives": "9.0.9"
}
},
"Microsoft.Extensions.Configuration.Binder": {
"type": "Transitive",
- "resolved": "9.0.7",
- "contentHash": "ExY+zXHhU4o9KC2alp3ZdLWyVWVRSn5INqax5ABk+HEOHlAHzomhJ7ek9HHliyOMiVGoYWYaMFOGr9q59mSAGA==",
+ "resolved": "9.0.9",
+ "contentHash": "6SIp/6Bngk4jm2W36JekZbiIbFPdE/eMUtrJEqIqHGpd1zar3jvgnwxnpWQfzUiGrkyY8q8s6V82zkkEZozghA==",
"dependencies": {
- "Microsoft.Extensions.Configuration.Abstractions": "9.0.7"
+ "Microsoft.Extensions.Configuration.Abstractions": "9.0.9"
}
},
"Microsoft.Extensions.Configuration.CommandLine": {
"type": "Transitive",
- "resolved": "9.0.7",
- "contentHash": "LqwdkMNFeRyuqExewBSaWj8roEgZH8JQ9zEAmHl5ZFcnhCvjAdHICdYVRIiSEq9RWGB731LL8kZJM8tdTKEscA==",
+ "resolved": "9.0.9",
+ "contentHash": "9bzGOcHoTi8ijrj0MHh5qUY6n9CuittZUqEOj5iE0ZJoSCfG0BI9nhcpd8MC9bOOgjZW5OeizKO8rgta9lSVyA==",
"dependencies": {
- "Microsoft.Extensions.Configuration": "9.0.7",
- "Microsoft.Extensions.Configuration.Abstractions": "9.0.7"
+ "Microsoft.Extensions.Configuration": "9.0.9",
+ "Microsoft.Extensions.Configuration.Abstractions": "9.0.9"
}
},
"Microsoft.Extensions.Configuration.EnvironmentVariables": {
"type": "Transitive",
- "resolved": "9.0.7",
- "contentHash": "R8kgazVpDr4k1K7MeWPLAwsi5VpwrhE3ubXK38D9gpHEvf9XhZhJ8kWHKK00LDg5hJ7pMQLggdZ7XFdQ5182Ug==",
+ "resolved": "9.0.9",
+ "contentHash": "AB8suTh4STAMGDkPer5vL0YNp09eplvbkIbOfFJ1z8D1zOiFF8Hipk9FhCLU4Ea6TosWmGrK30ZIUO9KvAeFcg==",
"dependencies": {
- "Microsoft.Extensions.Configuration": "9.0.7",
- "Microsoft.Extensions.Configuration.Abstractions": "9.0.7"
+ "Microsoft.Extensions.Configuration": "9.0.9",
+ "Microsoft.Extensions.Configuration.Abstractions": "9.0.9"
}
},
"Microsoft.Extensions.Configuration.FileExtensions": {
"type": "Transitive",
- "resolved": "9.0.7",
- "contentHash": "3LVg32iMfR9ENeegXAo73L+877iOcQauLJsXlKZNVSsLA/HbPgClZdeMGdjLSkaidYw3l02XbXTlOdGYNgu91Q==",
+ "resolved": "9.0.9",
+ "contentHash": "fvgubCs++wTowHWuQ5TAyZV0S6ldA59U+tBVqFr4/WLd0oEf6ESbdBN2CFaVdn4sZqnarqMnl2O3++RG/Jrf/w==",
"dependencies": {
- "Microsoft.Extensions.Configuration": "9.0.7",
- "Microsoft.Extensions.Configuration.Abstractions": "9.0.7",
- "Microsoft.Extensions.FileProviders.Abstractions": "9.0.7",
- "Microsoft.Extensions.FileProviders.Physical": "9.0.7",
- "Microsoft.Extensions.Primitives": "9.0.7"
+ "Microsoft.Extensions.Configuration": "9.0.9",
+ "Microsoft.Extensions.Configuration.Abstractions": "9.0.9",
+ "Microsoft.Extensions.FileProviders.Abstractions": "9.0.9",
+ "Microsoft.Extensions.FileProviders.Physical": "9.0.9",
+ "Microsoft.Extensions.Primitives": "9.0.9"
}
},
"Microsoft.Extensions.Configuration.Json": {
"type": "Transitive",
- "resolved": "9.0.7",
- "contentHash": "3HQV326liEInT9UKEc+k73f1ECwNhvDS/DJAe5WvtMKDJTJqTH2ujrUC2ZlK/j6pXyPbV9f0Ku8JB20JveGImg==",
+ "resolved": "9.0.9",
+ "contentHash": "PiPYo1GTinR2ECM80zYdZUIFmde6jj5DryXUcOJg3yIjh+KQMQr42e+COD03QUsUiqNkJk511wVTnVpTm2AVZA==",
"dependencies": {
- "Microsoft.Extensions.Configuration": "9.0.7",
- "Microsoft.Extensions.Configuration.Abstractions": "9.0.7",
- "Microsoft.Extensions.Configuration.FileExtensions": "9.0.7",
- "Microsoft.Extensions.FileProviders.Abstractions": "9.0.7"
+ "Microsoft.Extensions.Configuration": "9.0.9",
+ "Microsoft.Extensions.Configuration.Abstractions": "9.0.9",
+ "Microsoft.Extensions.Configuration.FileExtensions": "9.0.9",
+ "Microsoft.Extensions.FileProviders.Abstractions": "9.0.9"
}
},
"Microsoft.Extensions.Configuration.UserSecrets": {
"type": "Transitive",
- "resolved": "9.0.7",
- "contentHash": "ouDuPgRdeF4TJXKUh+lbm6QwyWwnCy+ijiqfFM2cI5NmW83MwKg1WNp2nCdMVcwQW8wJXteF/L9lA6ZPS3bCIQ==",
+ "resolved": "9.0.9",
+ "contentHash": "bFaNxfU8gQJX3K/Dd6XT0YIJ5ZVihdAY6Z02p2nVTUHjUsaWflLIucZOgB/ecSNnN3zbbBEf1oFC7q5NHTZIHw==",
"dependencies": {
- "Microsoft.Extensions.Configuration.Abstractions": "9.0.7",
- "Microsoft.Extensions.Configuration.Json": "9.0.7",
- "Microsoft.Extensions.FileProviders.Abstractions": "9.0.7",
- "Microsoft.Extensions.FileProviders.Physical": "9.0.7"
+ "Microsoft.Extensions.Configuration.Abstractions": "9.0.9",
+ "Microsoft.Extensions.Configuration.Json": "9.0.9",
+ "Microsoft.Extensions.FileProviders.Abstractions": "9.0.9",
+ "Microsoft.Extensions.FileProviders.Physical": "9.0.9"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
- "resolved": "9.0.7",
- "contentHash": "iPK1FxbGFr2Xb+4Y+dTYI8Gupu9pOi8I3JPuPsrogUmEhe2hzZ9LpCmolMEBhVDo2ikcSr7G5zYiwaapHSQTew=="
+ "resolved": "9.0.9",
+ "contentHash": "/hymojfWbE9AlDOa0mczR44m00Jj+T3+HZO0ZnVTI032fVycI0ZbNOVFP6kqZMcXiLSYXzR2ilcwaRi6dzeGyA=="
},
"Microsoft.Extensions.Diagnostics": {
"type": "Transitive",
- "resolved": "9.0.7",
- "contentHash": "6ykfInm6yw7pPHJACgnrPUXxUWVslFnzad44K/siXk6Ovan6fNMnXxI5X9vphHJuZ4JbMOdPIgsfTmLD+Dyxug==",
+ "resolved": "9.0.9",
+ "contentHash": "gtzl9SD6CvFYOb92qEF41Z9rICzYniM342TWbbJwN3eLS6a5fCLFvO1pQGtpMSnP3h1zHXupMEeKSA9musWYCQ==",
"dependencies": {
- "Microsoft.Extensions.Configuration": "9.0.7",
- "Microsoft.Extensions.Diagnostics.Abstractions": "9.0.7",
- "Microsoft.Extensions.Options.ConfigurationExtensions": "9.0.7"
+ "Microsoft.Extensions.Configuration": "9.0.9",
+ "Microsoft.Extensions.Diagnostics.Abstractions": "9.0.9",
+ "Microsoft.Extensions.Options.ConfigurationExtensions": "9.0.9"
}
},
"Microsoft.Extensions.Diagnostics.Abstractions": {
"type": "Transitive",
- "resolved": "9.0.7",
- "contentHash": "d39Ov1JpeWCGLCOTinlaDkujhrSAQ0HFxb7Su1BjhCKBfmDcQ6Ia1i3JI6kd3NFgwi1dexTunu82daDNwt7E6w==",
+ "resolved": "9.0.9",
+ "contentHash": "YHGmxccrVZ2Ar3eI+/NdbOHkd1/HzrHvmQ5yBsp0Gl7jTyBe6qcXNYjUt9v9JIO+Z14la44+YYEe63JSqs1fYg==",
"dependencies": {
- "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.7",
- "Microsoft.Extensions.Options": "9.0.7"
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9",
+ "Microsoft.Extensions.Options": "9.0.9"
}
},
"Microsoft.Extensions.FileProviders.Abstractions": {
"type": "Transitive",
- "resolved": "9.0.7",
- "contentHash": "y9djCca1cz/oz/J8jTxtoecNiNvaiGBJeWd7XOPxonH+FnfHqcfslJMcSr5JMinmWFyS7eh3C9L6m6oURZ5lSA==",
+ "resolved": "9.0.9",
+ "contentHash": "M1ZhL9QkBQ/k6l/Wjgcli5zrV86HzytQ+gQiNtk9vs9Ge1fb17KKZil9T6jd15p2x/BGfXpup7Hg55CC0kkfig==",
"dependencies": {
- "Microsoft.Extensions.Primitives": "9.0.7"
+ "Microsoft.Extensions.Primitives": "9.0.9"
}
},
"Microsoft.Extensions.FileProviders.Physical": {
"type": "Transitive",
- "resolved": "9.0.7",
- "contentHash": "JYEPYrb+YBpFTCdmSBrk8cg3wAi1V4so7ccq04qbhg3FQHQqgJk28L3heEOKMXcZobOBUjTnGCFJD49Ez9kG5w==",
+ "resolved": "9.0.9",
+ "contentHash": "sRrPtEwbK23OCFOQ36Xn6ofiB0/nl54/BOdR7lJ/Vwg3XlyvUdmyXvFUS1EU5ltn+sQtbcPuy1l0hsysO8++SQ==",
"dependencies": {
- "Microsoft.Extensions.FileProviders.Abstractions": "9.0.7",
- "Microsoft.Extensions.FileSystemGlobbing": "9.0.7",
- "Microsoft.Extensions.Primitives": "9.0.7"
+ "Microsoft.Extensions.FileProviders.Abstractions": "9.0.9",
+ "Microsoft.Extensions.FileSystemGlobbing": "9.0.9",
+ "Microsoft.Extensions.Primitives": "9.0.9"
}
},
"Microsoft.Extensions.FileSystemGlobbing": {
"type": "Transitive",
- "resolved": "9.0.7",
- "contentHash": "5VKpTH2ME0SSs0lrtkpKgjCeHzXR5ka/H+qThPwuWi78wHubApZ/atD7w69FDt0OOM7UMV6LIbkqEQgoby4IXA=="
+ "resolved": "9.0.9",
+ "contentHash": "iQAgORaVIlkhcpxFnVEfjqNWfQCwBEEH7x2IanTwGafA6Tb4xiBoDWySTxUo3MV2NUV/PmwS/8OhT/elPnJCnw=="
},
"Microsoft.Extensions.Hosting.Abstractions": {
"type": "Transitive",
- "resolved": "9.0.7",
- "contentHash": "yG2JCXAR+VqI1mKqynLPNJlNlrUJeEISEpX4UznOp2uM4IEFz3pDDauzyMvTjICutEJtOigJ1yWBvxbaIlibBw==",
+ "resolved": "9.0.9",
+ "contentHash": "ORA4dICNz7cuwupPkjXpSuoiK6GMg0aygInBIQCCFEimwoHntRKdJqB59faxq2HHJuTPW3NsZm5EjN5P5Zh6nQ==",
"dependencies": {
- "Microsoft.Extensions.Configuration.Abstractions": "9.0.7",
- "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.7",
- "Microsoft.Extensions.Diagnostics.Abstractions": "9.0.7",
- "Microsoft.Extensions.FileProviders.Abstractions": "9.0.7",
- "Microsoft.Extensions.Logging.Abstractions": "9.0.7"
+ "Microsoft.Extensions.Configuration.Abstractions": "9.0.9",
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9",
+ "Microsoft.Extensions.Diagnostics.Abstractions": "9.0.9",
+ "Microsoft.Extensions.FileProviders.Abstractions": "9.0.9",
+ "Microsoft.Extensions.Logging.Abstractions": "9.0.9"
}
},
"Microsoft.Extensions.Logging": {
"type": "Transitive",
- "resolved": "9.0.7",
- "contentHash": "fdIeQpXYV8yxSWG03cCbU2Otdrq4NWuhnQLXokWLv3L9YcK055E7u8WFJvP+uuP4CFeCEoqZQL4yPcjuXhCZrg==",
+ "resolved": "9.0.9",
+ "contentHash": "MaCB0Y9hNDs4YLu3HCJbo199WnJT8xSgajG1JYGANz9FkseQ5f3v/llu3HxLI6mjDlu7pa7ps9BLPWjKzsAAzQ==",
"dependencies": {
- "Microsoft.Extensions.DependencyInjection": "9.0.7",
- "Microsoft.Extensions.Logging.Abstractions": "9.0.7",
- "Microsoft.Extensions.Options": "9.0.7"
+ "Microsoft.Extensions.DependencyInjection": "9.0.9",
+ "Microsoft.Extensions.Logging.Abstractions": "9.0.9",
+ "Microsoft.Extensions.Options": "9.0.9"
}
},
"Microsoft.Extensions.Logging.Abstractions": {
"type": "Transitive",
- "resolved": "9.0.7",
- "contentHash": "sMM6NEAdUTE/elJ2wqjOi0iBWqZmSyaTByLF9e8XHv6DRJFFnOe0N+s8Uc6C91E4SboQCfLswaBIZ+9ZXA98AA==",
+ "resolved": "9.0.9",
+ "contentHash": "FEgpSF+Z9StMvrsSViaybOBwR0f0ZZxDm8xV5cSOFiXN/t+ys+rwAlTd/6yG7Ld1gfppgvLcMasZry3GsI9lGA==",
"dependencies": {
- "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.7"
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9"
}
},
"Microsoft.Extensions.Logging.Configuration": {
"type": "Transitive",
- "resolved": "9.0.7",
- "contentHash": "AEBty9rvFGvdFRqgIDEhQmiCnIfQWyzVoOZrO244cfu+n9M+wI1QLDpuROVILlplIBtLVmOezAF7d1H3Qog6Xw==",
+ "resolved": "9.0.9",
+ "contentHash": "Abuo+S0Sg+Ke6vzSh5Ell+lwJJM+CEIqg1ImtWnnqF6a/ibJkQnmFJi4/ekEw/0uAcdFKJXtGV7w6cFN0nyXeg==",
"dependencies": {
- "Microsoft.Extensions.Configuration": "9.0.7",
- "Microsoft.Extensions.Configuration.Abstractions": "9.0.7",
- "Microsoft.Extensions.Configuration.Binder": "9.0.7",
- "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.7",
- "Microsoft.Extensions.Logging": "9.0.7",
- "Microsoft.Extensions.Logging.Abstractions": "9.0.7",
- "Microsoft.Extensions.Options": "9.0.7",
- "Microsoft.Extensions.Options.ConfigurationExtensions": "9.0.7"
+ "Microsoft.Extensions.Configuration": "9.0.9",
+ "Microsoft.Extensions.Configuration.Abstractions": "9.0.9",
+ "Microsoft.Extensions.Configuration.Binder": "9.0.9",
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9",
+ "Microsoft.Extensions.Logging": "9.0.9",
+ "Microsoft.Extensions.Logging.Abstractions": "9.0.9",
+ "Microsoft.Extensions.Options": "9.0.9",
+ "Microsoft.Extensions.Options.ConfigurationExtensions": "9.0.9"
}
},
"Microsoft.Extensions.Logging.Console": {
"type": "Transitive",
- "resolved": "9.0.7",
- "contentHash": "pEHlNa8iCfKsBFA3YVDn/8EicjSU/m8uDfyoR0i4svONDss4Yu9Kznw53E/TyI+TveTo7CwRid4kfd4pLYXBig==",
+ "resolved": "9.0.9",
+ "contentHash": "x3+W7IfW9Tg3sV+sU9N1039M4CqklaAecwhz9qNtjOCBdmg7h96JaL+NAvhYgZgweVJTJaxAvuO8I+ZZehE7Pg==",
"dependencies": {
- "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.7",
- "Microsoft.Extensions.Logging": "9.0.7",
- "Microsoft.Extensions.Logging.Abstractions": "9.0.7",
- "Microsoft.Extensions.Logging.Configuration": "9.0.7",
- "Microsoft.Extensions.Options": "9.0.7"
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9",
+ "Microsoft.Extensions.Logging": "9.0.9",
+ "Microsoft.Extensions.Logging.Abstractions": "9.0.9",
+ "Microsoft.Extensions.Logging.Configuration": "9.0.9",
+ "Microsoft.Extensions.Options": "9.0.9"
}
},
"Microsoft.Extensions.Logging.Debug": {
"type": "Transitive",
- "resolved": "9.0.7",
- "contentHash": "MxzZj7XbsYJwfjclVTjJym2/nVIkksu7l7tC/4HYy+YRdDmpE4B+hTzCXu3BNfLNhdLPZsWpyXuYe6UGgWDm3g==",
+ "resolved": "9.0.9",
+ "contentHash": "q8IbjIzTjfaGfuf9LAuG3X9BytAWj2hWhLU61rEkit847oaSSbcdx/yybY3yL9RgVG1u9ctk7kbCv18M+7Fi6Q==",
"dependencies": {
- "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.7",
- "Microsoft.Extensions.Logging": "9.0.7",
- "Microsoft.Extensions.Logging.Abstractions": "9.0.7"
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9",
+ "Microsoft.Extensions.Logging": "9.0.9",
+ "Microsoft.Extensions.Logging.Abstractions": "9.0.9"
}
},
"Microsoft.Extensions.Logging.EventLog": {
"type": "Transitive",
- "resolved": "9.0.7",
- "contentHash": "usrMVsY7c8M8fESt34Y3eEIQIlRlKXfPDlI+vYEb6xT7SUjhua2ey3NpHgQktiTgz8Uo5RiWqGD8ieiyo2WaDA==",
+ "resolved": "9.0.9",
+ "contentHash": "1SX5+mv16SBb5NrtLNxIvUt8PHbdvDloZazQdxz1CNM39jG7yeF6olH3sceQ4ONF0oVD5mVUsTag0iVX4xgyog==",
"dependencies": {
- "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.7",
- "Microsoft.Extensions.Logging": "9.0.7",
- "Microsoft.Extensions.Logging.Abstractions": "9.0.7",
- "Microsoft.Extensions.Options": "9.0.7",
- "System.Diagnostics.EventLog": "9.0.7"
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9",
+ "Microsoft.Extensions.Logging": "9.0.9",
+ "Microsoft.Extensions.Logging.Abstractions": "9.0.9",
+ "Microsoft.Extensions.Options": "9.0.9",
+ "System.Diagnostics.EventLog": "9.0.9"
}
},
"Microsoft.Extensions.Logging.EventSource": {
"type": "Transitive",
- "resolved": "9.0.7",
- "contentHash": "/wwi6ckTEegCExFV6gVToCO7CvysZnmE50fpdkYUsSMh0ue9vRkQ7uOqkHyHol93ASYTEahrp+guMtS/+fZKaA==",
+ "resolved": "9.0.9",
+ "contentHash": "rGQi5mImot7tTFxj1tQWknWjOBHX1+gsX1WLmQNl5WHr4Sx1kXUBGDuRUjfx4c8pe/hcYHdalAmgk7RdusW6Jw==",
"dependencies": {
- "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.7",
- "Microsoft.Extensions.Logging": "9.0.7",
- "Microsoft.Extensions.Logging.Abstractions": "9.0.7",
- "Microsoft.Extensions.Options": "9.0.7",
- "Microsoft.Extensions.Primitives": "9.0.7"
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9",
+ "Microsoft.Extensions.Logging": "9.0.9",
+ "Microsoft.Extensions.Logging.Abstractions": "9.0.9",
+ "Microsoft.Extensions.Options": "9.0.9",
+ "Microsoft.Extensions.Primitives": "9.0.9"
}
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
- "resolved": "9.0.7",
- "contentHash": "trJnF6cRWgR5uMmHpGoHmM1wOVFdIYlELlkO9zX+RfieK0321Y55zrcs4AaEymKup7dxgEN/uJU25CAcMNQRXw==",
+ "resolved": "9.0.9",
+ "contentHash": "loxGGHE1FC2AefwPHzrjPq7X92LQm64qnU/whKfo6oWaceewPUVYQJBJs3S3E2qlWwnCpeZ+dGCPTX+5dgVAuQ==",
"dependencies": {
- "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.7",
- "Microsoft.Extensions.Primitives": "9.0.7"
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9",
+ "Microsoft.Extensions.Primitives": "9.0.9"
}
},
"Microsoft.Extensions.Options.ConfigurationExtensions": {
"type": "Transitive",
- "resolved": "9.0.7",
- "contentHash": "pE/jeAWHEIy/8HsqYA+I1+toTsdvsv+WywAcRoNSvPoFwjOREa8Fqn7D0/i0PbiXsDLFupltTTctliePx8ib4w==",
+ "resolved": "9.0.9",
+ "contentHash": "n4DCdnn2qs6V5U06Sx62FySEAZsJiJJgOzrPHDh9hPK7c2W8hEabC76F3Re3tGPjpiKa02RvB6FxZyxo8iICzg==",
"dependencies": {
- "Microsoft.Extensions.Configuration.Abstractions": "9.0.7",
- "Microsoft.Extensions.Configuration.Binder": "9.0.7",
- "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.7",
- "Microsoft.Extensions.Options": "9.0.7",
- "Microsoft.Extensions.Primitives": "9.0.7"
+ "Microsoft.Extensions.Configuration.Abstractions": "9.0.9",
+ "Microsoft.Extensions.Configuration.Binder": "9.0.9",
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9",
+ "Microsoft.Extensions.Options": "9.0.9",
+ "Microsoft.Extensions.Primitives": "9.0.9"
}
},
"Microsoft.Extensions.Primitives": {
"type": "Transitive",
- "resolved": "9.0.7",
- "contentHash": "ti/zD9BuuO50IqlvhWQs9GHxkCmoph5BHjGiWKdg2t6Or8XoyAfRJiKag+uvd/fpASnNklfsB01WpZ4fhAe0VQ=="
+ "resolved": "9.0.9",
+ "contentHash": "z4pyMePOrl733ltTowbN565PxBw1oAr8IHmIXNDiDqd22nFpYltX9KhrNC/qBWAG1/Zx5MHX+cOYhWJQYCO/iw=="
},
"Microsoft.IO.RecyclableMemoryStream": {
"type": "Transitive",
@@ -590,15 +590,15 @@
},
"NLog": {
"type": "Transitive",
- "resolved": "6.0.1",
- "contentHash": "qDWiqy8/xdpZKtHna/645KbalwP86N2NFJEzfqhcv+Si4V2iNaEfR/dCneuF/4+Dcwl3f7jHMXj3ndWYftV3Ug=="
+ "resolved": "6.0.4",
+ "contentHash": "Xr+lIk1ZlTTFXEqnxQVLxrDqZlt2tm5X+/AhJbaY2emb/dVtGDiU5QuEtj3gHtwV/SWlP/rJ922I/BPuOJXlRw=="
},
"NLog.OutputDebugString": {
"type": "Transitive",
- "resolved": "6.0.1",
- "contentHash": "wwJCQLaHVzuRf8TsXB+EEdrzVvE3dnzCSMQMDgwkw3AXp8VSp3JSVF/Q/H0oEqggKgKhPs13hh3a7svyQr4s3A==",
+ "resolved": "6.0.4",
+ "contentHash": "TOP2Ap9BbE98B/l/TglnguowOD0rXo8B/20xAgvj9shO/kf6IJ5M4QMhVxq72mrneJ/ANhHY7Jcd+xJbzuI5PA==",
"dependencies": {
- "NLog": "6.0.1"
+ "NLog": "6.0.4"
}
},
"runtime.osx.10.10-x64.CoreCompat.System.Drawing": {
@@ -608,8 +608,8 @@
},
"SharpVectors.Wpf": {
"type": "Transitive",
- "resolved": "1.8.4.2",
- "contentHash": "PNxLkMBJnV8A+6yH9OqOlhLJegvWP/dvh0rAJp2l0kcrR+rB4R2tQ9vhUqka+UilH4atN8T6zvjDOizVyfz2Ng=="
+ "resolved": "1.8.5",
+ "contentHash": "WURdBDq5AE8RjKV9pFS7lNkJe81gxja9SaMGE4URq9GJUZ6M+5DGUL0Lm3B0iYW2/Meyowaz4ffGsyW+RBSTtg=="
},
"Splat": {
"type": "Transitive",
@@ -672,8 +672,8 @@
},
"System.Diagnostics.EventLog": {
"type": "Transitive",
- "resolved": "9.0.7",
- "contentHash": "AJ+9fyCtQUImntxAJ9l4PZiCd4iepuk4pm7Qcno7PBIWQnfXlvwKuFsGk2H+QyY69GUVzDP2heELW6ho5BCXUg=="
+ "resolved": "9.0.9",
+ "contentHash": "wpsUfnyv8E5K4WQaok6weewvAbQhcLwXFcHBm5U0gdEaBs85N//ssuYvRPFWwz2rO/9/DFP3A1sGMzUFBj8y3w=="
},
"System.Drawing.Common": {
"type": "Transitive",
@@ -838,10 +838,10 @@
"type": "Project",
"dependencies": {
"Droplex": "[1.7.0, )",
- "FSharp.Core": "[9.0.300, )",
+ "FSharp.Core": "[9.0.303, )",
"Flow.Launcher.Infrastructure": "[1.0.0, )",
- "Flow.Launcher.Plugin": "[4.7.0, )",
- "Meziantou.Framework.Win32.Jobs": "[3.4.3, )",
+ "Flow.Launcher.Plugin": "[5.0.0, )",
+ "Meziantou.Framework.Win32.Jobs": "[3.4.4, )",
"Microsoft.IO.RecyclableMemoryStream": "[3.0.1, )",
"SemanticVersioning": "[3.0.0, )",
"StreamJsonRpc": "[2.22.11, )",
@@ -854,14 +854,14 @@
"Ben.Demystifier": "[0.4.1, )",
"BitFaster.Caching": "[2.5.4, )",
"CommunityToolkit.Mvvm": "[8.4.0, )",
- "Flow.Launcher.Plugin": "[4.7.0, )",
+ "Flow.Launcher.Plugin": "[5.0.0, )",
"InputSimulator": "[1.0.4, )",
"MemoryPack": "[1.21.4, )",
"Microsoft.VisualStudio.Threading": "[17.14.15, )",
"NHotkey.Wpf": "[3.0.0, )",
- "NLog": "[6.0.1, )",
- "NLog.OutputDebugString": "[6.0.1, )",
- "SharpVectors.Wpf": "[1.8.4.2, )",
+ "NLog": "[6.0.4, )",
+ "NLog.OutputDebugString": "[6.0.4, )",
+ "SharpVectors.Wpf": "[1.8.5, )",
"System.Drawing.Common": "[7.0.0, )",
"ToolGood.Words.Pinyin": "[3.1.0.3, )"
}
@@ -869,7 +869,7 @@
"flow.launcher.plugin": {
"type": "Project",
"dependencies": {
- "JetBrains.Annotations": "[2024.3.0, )"
+ "JetBrains.Annotations": "[2025.2.2, )"
}
}
}
diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj
index 901dc2a37..e3233f73d 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj
@@ -34,6 +34,7 @@
prompt
4
false
+ $(NoWarn);FLSG0007
@@ -103,9 +104,9 @@
-
-
-
+
+
+
diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Helper/FaviconHelper.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Helper/FaviconHelper.cs
index 1820a7836..82b089033 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Helper/FaviconHelper.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Helper/FaviconHelper.cs
@@ -106,12 +106,13 @@ public static class FaviconHelper
{
try
{
- using (var image = SKImage.FromBitmap(bitmap))
- using (var webp = image.Encode(SKEncodedImageFormat.Webp, 65))
- {
- if (webp != null)
- return webp.ToArray();
- }
+ using var image = SKImage.FromBitmap(bitmap);
+ if (image is null)
+ return null;
+
+ using var webp = image.Encode(SKEncodedImageFormat.Webp, 65);
+ if (webp != null)
+ return webp.ToArray();
}
finally
{
diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj b/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj
index 43a2c2f3c..20a0ec4f0 100644
--- a/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj
+++ b/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj
@@ -33,6 +33,7 @@
prompt
4
false
+ $(NoWarn);FLSG0007
@@ -62,7 +63,7 @@
-
+
diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/en.xaml
index b71e5d8a0..b12972b1b 100644
--- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/en.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/en.xaml
@@ -4,7 +4,7 @@
xmlns:system="clr-namespace:System;assembly=mscorlib">
Calculator
- Perform mathematical calculations (including hexadecimal values). Use ',' or '.' as thousand separator or decimal place.
+ Perform mathematical calculations, including hex values and advanced functions such as 'min(1,2,3)', 'sqrt(123)' and 'cos(123)'.
Not a number (NaN)
Expression wrong or incomplete (Did you forget some parentheses?)
Copy this number to the clipboard
@@ -15,4 +15,5 @@
Dot (.)
Max. decimal places
Copy failed, please try later
+ Show error message when calculation fails
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs b/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs
index 6878c54b4..9d5e4700f 100644
--- a/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs
+++ b/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs
@@ -13,30 +13,24 @@ namespace Flow.Launcher.Plugin.Calculator
{
public class Main : IPlugin, IPluginI18n, ISettingProvider
{
- private static readonly Regex RegValidExpressChar = MainRegexHelper.GetRegValidExpressChar();
- private static readonly Regex RegBrackets = MainRegexHelper.GetRegBrackets();
private static readonly Regex ThousandGroupRegex = MainRegexHelper.GetThousandGroupRegex();
private static readonly Regex NumberRegex = MainRegexHelper.GetNumberRegex();
+ private static readonly Regex PowRegex = MainRegexHelper.GetPowRegex();
+ private static readonly Regex LogRegex = MainRegexHelper.GetLogRegex();
+ private static readonly Regex LnRegex = MainRegexHelper.GetLnRegex();
+ private static readonly Regex FunctionRegex = MainRegexHelper.GetFunctionRegex();
private static Engine MagesEngine;
private const string Comma = ",";
private const string Dot = ".";
+ private const string IcoPath = "Images/calculator.png";
+ private static readonly List EmptyResults = [];
internal static PluginInitContext Context { get; set; } = null!;
private Settings _settings;
private SettingsViewModel _viewModel;
- ///
- /// Holds the formatting information for a single query.
- /// This is used to ensure thread safety by keeping query state local.
- ///
- private class ParsingContext
- {
- public string InputDecimalSeparator { get; set; }
- public bool InputUsesGroupSeparators { get; set; }
- }
-
public void Init(PluginInitContext context)
{
Context = context;
@@ -54,38 +48,98 @@ namespace Flow.Launcher.Plugin.Calculator
public List Query(Query query)
{
- if (!CanCalculate(query))
+ if (string.IsNullOrWhiteSpace(query.Search))
{
- return new List();
+ return EmptyResults;
}
- var context = new ParsingContext();
-
try
{
- var expression = NumberRegex.Replace(query.Search, m => NormalizeNumber(m.Value, context));
+ var search = query.Search;
+ bool isFunctionPresent = FunctionRegex.IsMatch(search);
+
+ // Mages is case sensitive, so we need to convert all function names to lower case.
+ search = FunctionRegex.Replace(search, m => m.Value.ToLowerInvariant());
+
+ var decimalSep = GetDecimalSeparator();
+ var groupSep = GetGroupSeparator(decimalSep);
+ var expression = NumberRegex.Replace(search, m => NormalizeNumber(m.Value, isFunctionPresent, decimalSep, groupSep));
+
+ // WORKAROUND START: The 'pow' function in Mages v3.0.0 is broken.
+ // https://github.com/FlorianRappl/Mages/issues/132
+ // We bypass it by rewriting any pow(x,y) expression to the equivalent (x^y) expression
+ // before the engine sees it. This loop handles nested calls.
+ {
+ string previous;
+ do
+ {
+ previous = expression;
+ expression = PowRegex.Replace(previous, PowMatchEvaluator);
+ } while (previous != expression);
+ }
+ // WORKAROUND END
+
+ // WORKAROUND START: The 'log' & 'ln' function in Mages v3.0.0 are broken.
+ // https://github.com/FlorianRappl/Mages/issues/137
+ // We bypass it by rewriting any log & ln expression to the equivalent (log10 & log) expression
+ // before the engine sees it. This loop handles nested calls.
+ {
+ string previous;
+ do
+ {
+ previous = expression;
+ expression = LogRegex.Replace(previous, LogMatchEvaluator);
+ } while (previous != expression);
+ }
+ {
+ string previous;
+ do
+ {
+ previous = expression;
+ expression = LnRegex.Replace(previous, LnMatchEvaluator);
+ } while (previous != expression);
+ }
+ // WORKAROUND END
var result = MagesEngine.Interpret(expression);
- if (result?.ToString() == "NaN")
+ if (result == null || string.IsNullOrEmpty(result.ToString()))
+ {
+ if (!_settings.ShowErrorMessage) return EmptyResults;
+ return
+ [
+ new Result
+ {
+ Title = Localize.flowlauncher_plugin_calculator_expression_not_complete(),
+ IcoPath = IcoPath
+ }
+ ];
+ }
+
+ if (result.ToString() == "NaN")
+ {
result = Localize.flowlauncher_plugin_calculator_not_a_number();
+ }
if (result is Function)
+ {
result = Localize.flowlauncher_plugin_calculator_expression_not_complete();
+ }
- if (!string.IsNullOrEmpty(result?.ToString()))
+ if (!string.IsNullOrEmpty(result.ToString()))
{
decimal roundedResult = Math.Round(Convert.ToDecimal(result), _settings.MaxDecimalPlaces, MidpointRounding.AwayFromZero);
- string newResult = FormatResult(roundedResult, context);
+ string newResult = FormatResult(roundedResult);
- return new List
- {
+ return
+ [
new Result
{
Title = newResult,
- IcoPath = "Images/calculator.png",
+ IcoPath = IcoPath,
Score = 300,
- SubTitle = Localize.flowlauncher_plugin_calculator_copy_number_to_clipboard(),
+ // Check context nullability for unit testing
+ SubTitle = Context == null ? string.Empty : Localize.flowlauncher_plugin_calculator_copy_number_to_clipboard(),
CopyText = newResult,
Action = c =>
{
@@ -101,118 +155,206 @@ namespace Flow.Launcher.Plugin.Calculator
}
}
}
- };
+ ];
}
}
catch (Exception)
{
- // ignored
+ // Mages engine can throw various exceptions, for simplicity we catch them all and show a generic message.
+ if (!_settings.ShowErrorMessage) return EmptyResults;
+ return
+ [
+ new Result
+ {
+ Title = Localize.flowlauncher_plugin_calculator_expression_not_complete(),
+ IcoPath = IcoPath
+ }
+ ];
}
- return new List();
+ return EmptyResults;
}
- ///
- /// Parses a string representation of a number, detecting its format. It uses structural analysis
- /// and falls back to system culture for truly ambiguous cases (e.g., "1,234").
- /// It populates the provided ParsingContext with the detected format for later use.
- ///
- /// A normalized number string with '.' as the decimal separator for the Mages engine.
- private string NormalizeNumber(string numberStr, ParsingContext context)
+ private static string PowMatchEvaluator(Match m)
{
- var systemGroupSep = CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator;
- int dotCount = numberStr.Count(f => f == '.');
- int commaCount = numberStr.Count(f => f == ',');
+ // m.Groups[1].Value will be `(...)` with parens
+ var contentWithParen = m.Groups[1].Value;
+ // remove outer parens. `(min(2,3), 4)` becomes `min(2,3), 4`
+ var argsContent = contentWithParen[1..^1];
- // Case 1: Unambiguous mixed separators (e.g., "1.234,56")
- if (dotCount > 0 && commaCount > 0)
+ var bracketCount = 0;
+ var splitIndex = -1;
+
+ // Find the top-level comma that separates the two arguments of pow.
+ for (var i = 0; i < argsContent.Length; i++)
{
- context.InputUsesGroupSeparators = true;
- if (numberStr.LastIndexOf('.') > numberStr.LastIndexOf(','))
+ switch (argsContent[i])
{
- context.InputDecimalSeparator = Dot;
- return numberStr.Replace(Comma, string.Empty);
+ case '(':
+ case '[':
+ bracketCount++;
+ break;
+ case ')':
+ case ']':
+ bracketCount--;
+ break;
+ case ',' when bracketCount == 0:
+ splitIndex = i;
+ break;
+ }
+
+ if (splitIndex != -1)
+ break;
+ }
+
+ if (splitIndex == -1)
+ {
+ // This indicates malformed arguments for pow, e.g., pow(5) or pow().
+ // Return original string to let Mages handle the error.
+ return m.Value;
+ }
+
+ var arg1 = argsContent[..splitIndex].Trim();
+ var arg2 = argsContent[(splitIndex + 1)..].Trim();
+
+ // Check for empty arguments which can happen with stray commas, e.g., pow(,5)
+ if (string.IsNullOrEmpty(arg1) || string.IsNullOrEmpty(arg2))
+ {
+ return m.Value;
+ }
+
+ return $"({arg1}^{arg2})";
+ }
+
+ private static string LogMatchEvaluator(Match m)
+ {
+ // m.Groups[1].Value will be `(...)` with parens
+ var contentWithParen = m.Groups[1].Value;
+ var argsContent = contentWithParen[1..^1];
+
+ // log is unary — if malformed, return original to let Mages handle it
+ var arg = argsContent.Trim();
+ if (string.IsNullOrEmpty(arg)) return m.Value;
+
+ // log(x) -> log10(x) (natural log)
+ return $"(log10({arg}))";
+ }
+
+ private static string LnMatchEvaluator(Match m)
+ {
+ // m.Groups[1].Value will be `(...)` with parens
+ var contentWithParen = m.Groups[1].Value;
+ var argsContent = contentWithParen[1..^1];
+
+ // ln is unary — if malformed, return original to let Mages handle it
+ var arg = argsContent.Trim();
+ if (string.IsNullOrEmpty(arg)) return m.Value;
+
+ // ln(x) -> log(x) (natural log)
+ return $"(log({arg}))";
+ }
+ private static string NormalizeNumber(string numberStr, bool isFunctionPresent, string decimalSep, string groupSep)
+ {
+ if (isFunctionPresent)
+ {
+ // STRICT MODE: When functions are present, ',' is ALWAYS an argument separator.
+ if (numberStr.Contains(','))
+ {
+ return numberStr;
+ }
+
+ string processedStr = numberStr;
+
+ // Handle group separator, with special care for ambiguous dot.
+ if (!string.IsNullOrEmpty(groupSep))
+ {
+ if (groupSep == ".")
+ {
+ var parts = processedStr.Split('.');
+ if (parts.Length > 1)
+ {
+ var culture = CultureInfo.CurrentCulture;
+ if (IsValidGrouping(parts, culture.NumberFormat.NumberGroupSizes))
+ {
+ processedStr = processedStr.Replace(groupSep, "");
+ }
+ // If not grouped, it's likely a decimal number, so we don't strip dots.
+ }
+ }
+ else
+ {
+ processedStr = processedStr.Replace(groupSep, "");
+ }
+ }
+
+ // Handle decimal separator.
+ if (decimalSep != ".")
+ {
+ processedStr = processedStr.Replace(decimalSep, ".");
+ }
+
+ return processedStr;
+ }
+ else
+ {
+ // LENIENT MODE: No functions are present, so we can be flexible.
+ string processedStr = numberStr;
+ if (!string.IsNullOrEmpty(groupSep))
+ {
+ processedStr = processedStr.Replace(groupSep, "");
+ }
+ if (decimalSep != ".")
+ {
+ processedStr = processedStr.Replace(decimalSep, ".");
+ }
+ return processedStr;
+ }
+ }
+
+ private static bool IsValidGrouping(string[] parts, int[] groupSizes)
+ {
+ if (parts.Length <= 1) return true;
+
+ if (groupSizes is null || groupSizes.Length == 0 || groupSizes[0] == 0)
+ return false; // has groups, but culture defines none.
+
+ var firstPart = parts[0];
+ if (firstPart.StartsWith('-')) firstPart = firstPart[1..];
+ if (firstPart.Length == 0) return false; // e.g. ",123"
+
+ if (firstPart.Length > groupSizes[0]) return false;
+
+ var lastGroupSize = groupSizes.Last();
+ var canRepeatLastGroup = lastGroupSize != 0;
+
+ int groupIndex = 0;
+ for (int i = parts.Length - 1; i > 0; i--)
+ {
+ int expectedSize;
+ if (groupIndex < groupSizes.Length)
+ {
+ expectedSize = groupSizes[groupIndex];
+ }
+ else if(canRepeatLastGroup)
+ {
+ expectedSize = lastGroupSize;
}
else
{
- context.InputDecimalSeparator = Comma;
- return numberStr.Replace(Dot, string.Empty).Replace(Comma, Dot);
+ return false;
}
+
+ if (parts[i].Length != expectedSize) return false;
+
+ groupIndex++;
}
- // Case 2: Only dots
- if (dotCount > 0)
- {
- if (dotCount > 1)
- {
- context.InputUsesGroupSeparators = true;
- return numberStr.Replace(Dot, string.Empty);
- }
- // A number is ambiguous if it has a single Dot in the thousands position,
- // and does not start with a "0." or "."
- bool isAmbiguous = numberStr.Length - numberStr.LastIndexOf('.') == 4
- && !numberStr.StartsWith("0.")
- && !numberStr.StartsWith(".");
- if (isAmbiguous)
- {
- if (systemGroupSep == Dot)
- {
- context.InputUsesGroupSeparators = true;
- return numberStr.Replace(Dot, string.Empty);
- }
- else
- {
- context.InputDecimalSeparator = Dot;
- return numberStr;
- }
- }
- else // Unambiguous decimal (e.g., "12.34" or "0.123" or ".123")
- {
- context.InputDecimalSeparator = Dot;
- return numberStr;
- }
- }
-
- // Case 3: Only commas
- if (commaCount > 0)
- {
- if (commaCount > 1)
- {
- context.InputUsesGroupSeparators = true;
- return numberStr.Replace(Comma, string.Empty);
- }
- // A number is ambiguous if it has a single Comma in the thousands position,
- // and does not start with a "0," or ","
- bool isAmbiguous = numberStr.Length - numberStr.LastIndexOf(',') == 4
- && !numberStr.StartsWith("0,")
- && !numberStr.StartsWith(",");
- if (isAmbiguous)
- {
- if (systemGroupSep == Comma)
- {
- context.InputUsesGroupSeparators = true;
- return numberStr.Replace(Comma, string.Empty);
- }
- else
- {
- context.InputDecimalSeparator = Comma;
- return numberStr.Replace(Comma, Dot);
- }
- }
- else // Unambiguous decimal (e.g., "12,34" or "0,123" or ",123")
- {
- context.InputDecimalSeparator = Comma;
- return numberStr.Replace(Comma, Dot);
- }
- }
-
- // Case 4: No separators
- return numberStr;
+ return true;
}
- private string FormatResult(decimal roundedResult, ParsingContext context)
+ private string FormatResult(decimal roundedResult)
{
- string decimalSeparator = context.InputDecimalSeparator ?? GetDecimalSeparator();
+ string decimalSeparator = GetDecimalSeparator();
string groupSeparator = GetGroupSeparator(decimalSeparator);
string resultStr = roundedResult.ToString(CultureInfo.InvariantCulture);
@@ -221,7 +363,7 @@ namespace Flow.Launcher.Plugin.Calculator
string integerPart = parts[0];
string fractionalPart = parts.Length > 1 ? parts[1] : string.Empty;
- if (context.InputUsesGroupSeparators && integerPart.Length > 3)
+ if (integerPart.Length > 3)
{
integerPart = ThousandGroupRegex.Replace(integerPart, groupSeparator);
}
@@ -236,29 +378,23 @@ namespace Flow.Launcher.Plugin.Calculator
private string GetGroupSeparator(string decimalSeparator)
{
- // This logic is now independent of the system's group separator
- // to ensure consistent output for unit testing.
- return decimalSeparator == Dot ? Comma : Dot;
- }
+ var culture = CultureInfo.CurrentCulture;
+ var systemGroupSeparator = culture.NumberFormat.NumberGroupSeparator;
- private bool CanCalculate(Query query)
- {
- if (query.Search.Length < 2)
+ if (_settings.DecimalSeparator == DecimalSeparator.UseSystemLocale)
{
- return false;
+ return systemGroupSeparator;
}
- if (!RegValidExpressChar.IsMatch(query.Search))
+ // When a custom decimal separator is used,
+ // use the system's group separator unless it conflicts with the custom decimal separator.
+ if (decimalSeparator == systemGroupSeparator)
{
- return false;
+ // Conflict: use the opposite of the decimal separator as a fallback.
+ return decimalSeparator == Dot ? Comma : Dot;
}
- if (!IsBracketComplete(query.Search))
- {
- return false;
- }
-
- return true;
+ return systemGroupSeparator;
}
private string GetDecimalSeparator()
@@ -273,25 +409,6 @@ namespace Flow.Launcher.Plugin.Calculator
};
}
- private static bool IsBracketComplete(string query)
- {
- var matchs = RegBrackets.Matches(query);
- var leftBracketCount = 0;
- foreach (Match match in matchs)
- {
- if (match.Value == "(" || match.Value == "[")
- {
- leftBracketCount++;
- }
- else
- {
- leftBracketCount--;
- }
- }
-
- return leftBracketCount == 0;
- }
-
public string GetTranslatedPluginTitle()
{
return Localize.flowlauncher_plugin_calculator_plugin_name();
diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/MainRegexHelper.cs b/Plugins/Flow.Launcher.Plugin.Calculator/MainRegexHelper.cs
index f4e2090e7..a8b582ccc 100644
--- a/Plugins/Flow.Launcher.Plugin.Calculator/MainRegexHelper.cs
+++ b/Plugins/Flow.Launcher.Plugin.Calculator/MainRegexHelper.cs
@@ -4,16 +4,21 @@ namespace Flow.Launcher.Plugin.Calculator;
internal static partial class MainRegexHelper
{
-
- [GeneratedRegex(@"[\(\)\[\]]", RegexOptions.Compiled)]
- public static partial Regex GetRegBrackets();
-
- [GeneratedRegex(@"^(ceil|floor|exp|pi|e|max|min|det|abs|log|ln|sqrt|sin|cos|tan|arcsin|arccos|arctan|eigval|eigvec|eig|sum|polar|plot|round|sort|real|zeta|bin2dec|hex2dec|oct2dec|factorial|sign|isprime|isinfty|==|~=|&&|\|\||(?:\<|\>)=?|[ei]|[0-9]|0x[\da-fA-F]+|[\+\%\-\*\/\^\., ""]|[\(\)\|\!\[\]])+$", RegexOptions.Compiled)]
- public static partial Regex GetRegValidExpressChar();
-
- [GeneratedRegex(@"[\d\.,]+", RegexOptions.Compiled)]
+ [GeneratedRegex(@"-?[\d\.,'\u00A0\u202F]+", RegexOptions.Compiled | RegexOptions.CultureInvariant)]
public static partial Regex GetNumberRegex();
[GeneratedRegex(@"\B(?=(\d{3})+(?!\d))", RegexOptions.Compiled)]
public static partial Regex GetThousandGroupRegex();
+
+ [GeneratedRegex(@"\bpow(\((?:[^()\[\]]|\((?)|\)(?<-Depth>)|\[(?)|\](?<-Depth>))*(?(Depth)(?!))\))", RegexOptions.Compiled | RegexOptions.RightToLeft | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
+ public static partial Regex GetPowRegex();
+
+ [GeneratedRegex(@"\blog(\((?:[^()\[\]]|\((?)|\)(?<-Depth>)|\[(?)|\](?<-Depth>))*(?(Depth)(?!))\))", RegexOptions.Compiled | RegexOptions.RightToLeft | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
+ public static partial Regex GetLogRegex();
+
+ [GeneratedRegex(@"\bln(\((?:[^()\[\]]|\((?)|\)(?<-Depth>)|\[(?)|\](?<-Depth>))*(?(Depth)(?!))\))", RegexOptions.Compiled | RegexOptions.RightToLeft | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
+ public static partial Regex GetLnRegex();
+
+ [GeneratedRegex(@"\b(sqrt|pow|factorial|abs|sign|ceil|floor|round|exp|log|log2|log10|min|max|lt|eq|gt|sin|cos|tan|arcsin|arccos|arctan|isnan|isint|isprime|isinfty|rand|randi|type|is|as|length|throw|catch|eval|map|clamp|lerp|regex|shuffle)\s*\(", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
+ public static partial Regex GetFunctionRegex();
}
diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Settings.cs b/Plugins/Flow.Launcher.Plugin.Calculator/Settings.cs
index 8354863b8..cac0f3080 100644
--- a/Plugins/Flow.Launcher.Plugin.Calculator/Settings.cs
+++ b/Plugins/Flow.Launcher.Plugin.Calculator/Settings.cs
@@ -1,9 +1,11 @@
-namespace Flow.Launcher.Plugin.Calculator
+namespace Flow.Launcher.Plugin.Calculator;
+
+public class Settings
{
- public class Settings
- {
- public DecimalSeparator DecimalSeparator { get; set; } = DecimalSeparator.UseSystemLocale;
- public int MaxDecimalPlaces { get; set; } = 10;
- }
+ public DecimalSeparator DecimalSeparator { get; set; } = DecimalSeparator.UseSystemLocale;
+
+ public int MaxDecimalPlaces { get; set; } = 10;
+
+ public bool ShowErrorMessage { get; set; } = false;
}
diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/ViewModels/SettingsViewModel.cs b/Plugins/Flow.Launcher.Plugin.Calculator/ViewModels/SettingsViewModel.cs
index 87ae72fb6..79236bdf8 100644
--- a/Plugins/Flow.Launcher.Plugin.Calculator/ViewModels/SettingsViewModel.cs
+++ b/Plugins/Flow.Launcher.Plugin.Calculator/ViewModels/SettingsViewModel.cs
@@ -1,31 +1,25 @@
using System.Collections.Generic;
using System.Linq;
-namespace Flow.Launcher.Plugin.Calculator.ViewModels
+namespace Flow.Launcher.Plugin.Calculator.ViewModels;
+
+public class SettingsViewModel(Settings settings) : BaseModel
{
- public class SettingsViewModel : BaseModel
+ public Settings Settings { get; } = settings;
+
+ public static IEnumerable MaxDecimalPlacesRange => Enumerable.Range(1, 20);
+
+ public List AllDecimalSeparator { get; } = DecimalSeparatorLocalized.GetValues();
+
+ public DecimalSeparator SelectedDecimalSeparator
{
- public SettingsViewModel(Settings settings)
+ get => Settings.DecimalSeparator;
+ set
{
- Settings = settings;
- }
-
- public Settings Settings { get; init; }
-
- public static IEnumerable MaxDecimalPlacesRange => Enumerable.Range(1, 20);
-
- public List AllDecimalSeparator { get; } = DecimalSeparatorLocalized.GetValues();
-
- public DecimalSeparator SelectedDecimalSeparator
- {
- get => Settings.DecimalSeparator;
- set
+ if (Settings.DecimalSeparator != value)
{
- if (Settings.DecimalSeparator != value)
- {
- Settings.DecimalSeparator = value;
- OnPropertyChanged();
- }
+ Settings.DecimalSeparator = value;
+ OnPropertyChanged();
}
}
}
diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml
index 8d240ef39..9e7549b2d 100644
--- a/Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml
@@ -15,6 +15,7 @@
+
@@ -58,5 +59,14 @@
ItemsSource="{Binding MaxDecimalPlacesRange}"
SelectedItem="{Binding Settings.MaxDecimalPlaces}" />
+
diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml.cs b/Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml.cs
index 7bc307d11..9e75e7bfb 100644
--- a/Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml.cs
+++ b/Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml.cs
@@ -1,22 +1,16 @@
using System.Windows.Controls;
using Flow.Launcher.Plugin.Calculator.ViewModels;
-namespace Flow.Launcher.Plugin.Calculator.Views
-{
- ///
- /// Interaction logic for CalculatorSettings.xaml
- ///
- public partial class CalculatorSettings : UserControl
- {
- private readonly SettingsViewModel _viewModel;
- private readonly Settings _settings;
+namespace Flow.Launcher.Plugin.Calculator.Views;
- public CalculatorSettings(Settings settings)
- {
- _viewModel = new SettingsViewModel(settings);
- _settings = _viewModel.Settings;
- DataContext = _viewModel;
- InitializeComponent();
- }
+public partial class CalculatorSettings : UserControl
+{
+ private readonly SettingsViewModel _viewModel;
+
+ public CalculatorSettings(Settings settings)
+ {
+ _viewModel = new SettingsViewModel(settings);
+ DataContext = _viewModel;
+ InitializeComponent();
}
}
diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json b/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json
index c9435e043..93df9ec72 100644
--- a/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json
+++ b/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json
@@ -2,7 +2,7 @@
"ID": "CEA0FDFC6D3B4085823D60DC76F28855",
"ActionKeyword": "*",
"Name": "Calculator",
- "Description": "Perform mathematical calculations (including hexadecimal values). Use ',' or '.' as thousand separator or decimal place.",
+ "Description": "Perform mathematical calculations, including hex values and advanced functions such as 'min(1,2,3)', 'sqrt(123)' and 'cos(123)'.",
"Author": "cxfksword, dcog989",
"Version": "1.0.0",
"Language": "csharp",
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs
index 3802c701b..90db87966 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs
@@ -66,8 +66,8 @@ namespace Flow.Launcher.Plugin.Explorer
{
contextMenus.Add(new Result
{
- Title = Context.API.GetTranslation("plugin_explorer_add_to_quickaccess_title"),
- SubTitle = Context.API.GetTranslation("plugin_explorer_add_to_quickaccess_subtitle"),
+ Title = Localize.plugin_explorer_add_to_quickaccess_title(),
+ SubTitle = Localize.plugin_explorer_add_to_quickaccess_subtitle(),
Action = (context) =>
{
Settings.QuickAccessLinks.Add(new AccessLink
@@ -77,16 +77,14 @@ namespace Flow.Launcher.Plugin.Explorer
Type = record.Type
});
- Context.API.ShowMsg(Context.API.GetTranslation("plugin_explorer_addfilefoldersuccess"),
- Context.API.GetTranslation("plugin_explorer_addfilefoldersuccess_detail"),
- Constants.ExplorerIconImageFullPath);
-
-
+ Context.API.ShowMsg(Localize.plugin_explorer_addfilefoldersuccess(),
+ Localize.plugin_explorer_addfilefoldersuccess_detail(),
+ Constants.ExplorerIconImageFullPath);
return true;
},
- SubTitleToolTip = Context.API.GetTranslation("plugin_explorer_contextmenu_titletooltip"),
- TitleToolTip = Context.API.GetTranslation("plugin_explorer_contextmenu_titletooltip"),
+ SubTitleToolTip = Localize.plugin_explorer_contextmenu_titletooltip(),
+ TitleToolTip = Localize.plugin_explorer_contextmenu_titletooltip(),
IcoPath = Constants.QuickAccessImagePath,
Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\ue718"),
});
@@ -95,22 +93,20 @@ namespace Flow.Launcher.Plugin.Explorer
{
contextMenus.Add(new Result
{
- Title = Context.API.GetTranslation("plugin_explorer_remove_from_quickaccess_title"),
- SubTitle = Context.API.GetTranslation("plugin_explorer_remove_from_quickaccess_subtitle"),
+ Title = Localize.plugin_explorer_remove_from_quickaccess_title(),
+ SubTitle = Localize.plugin_explorer_remove_from_quickaccess_subtitle(),
Action = (context) =>
{
Settings.QuickAccessLinks.Remove(Settings.QuickAccessLinks.FirstOrDefault(x => string.Equals(x.Path, record.FullPath, StringComparison.OrdinalIgnoreCase)));
- Context.API.ShowMsg(Context.API.GetTranslation("plugin_explorer_removefilefoldersuccess"),
- Context.API.GetTranslation("plugin_explorer_removefilefoldersuccess_detail"),
- Constants.ExplorerIconImageFullPath);
-
-
+ Context.API.ShowMsg(Localize.plugin_explorer_removefilefoldersuccess(),
+ Localize.plugin_explorer_removefilefoldersuccess_detail(),
+ Constants.ExplorerIconImageFullPath);
return true;
},
- SubTitleToolTip = Context.API.GetTranslation("plugin_explorer_contextmenu_remove_titletooltip"),
- TitleToolTip = Context.API.GetTranslation("plugin_explorer_contextmenu_remove_titletooltip"),
+ SubTitleToolTip = Localize.plugin_explorer_contextmenu_remove_titletooltip(),
+ TitleToolTip = Localize.plugin_explorer_contextmenu_remove_titletooltip(),
IcoPath = Constants.RemoveQuickAccessImagePath,
Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\uecc9")
});
@@ -118,8 +114,8 @@ namespace Flow.Launcher.Plugin.Explorer
contextMenus.Add(new Result
{
- Title = Context.API.GetTranslation("plugin_explorer_copypath"),
- SubTitle = Context.API.GetTranslation("plugin_explorer_copypath_subtitle"),
+ Title = Localize.plugin_explorer_copypath(),
+ SubTitle = Localize.plugin_explorer_copypath_subtitle(),
Action = _ =>
{
try
@@ -130,7 +126,7 @@ namespace Flow.Launcher.Plugin.Explorer
catch (Exception e)
{
LogException("Fail to set text in clipboard", e);
- Context.API.ShowMsgError(Context.API.GetTranslation("plugin_explorer_fail_to_set_text"));
+ Context.API.ShowMsgError(Localize.plugin_explorer_fail_to_set_text());
return false;
}
},
@@ -140,8 +136,8 @@ namespace Flow.Launcher.Plugin.Explorer
contextMenus.Add(new Result
{
- Title = Context.API.GetTranslation("plugin_explorer_copyname"),
- SubTitle = Context.API.GetTranslation("plugin_explorer_copyname_subtitle"),
+ Title = Localize.plugin_explorer_copyname(),
+ SubTitle = Localize.plugin_explorer_copyname_subtitle(),
Action = _ =>
{
try
@@ -152,7 +148,7 @@ namespace Flow.Launcher.Plugin.Explorer
catch (Exception e)
{
LogException("Fail to set text in clipboard", e);
- Context.API.ShowMsgError(Context.API.GetTranslation("plugin_explorer_fail_to_set_text"));
+ Context.API.ShowMsgError(Localize.plugin_explorer_fail_to_set_text());
return false;
}
},
@@ -162,8 +158,8 @@ namespace Flow.Launcher.Plugin.Explorer
contextMenus.Add(new Result
{
- Title = Context.API.GetTranslation("plugin_explorer_copyfilefolder"),
- SubTitle = isFile ? Context.API.GetTranslation("plugin_explorer_copyfile_subtitle") : Context.API.GetTranslation("plugin_explorer_copyfolder_subtitle"),
+ Title = Localize.plugin_explorer_copyfilefolder(),
+ SubTitle = isFile ? Localize.plugin_explorer_copyfile_subtitle(): Localize.plugin_explorer_copyfolder_subtitle(),
Action = _ =>
{
try
@@ -174,28 +170,26 @@ namespace Flow.Launcher.Plugin.Explorer
catch (Exception e)
{
LogException($"Fail to set file/folder in clipboard", e);
- Context.API.ShowMsgError(Context.API.GetTranslation("plugin_explorer_fail_to_set_files"));
+ Context.API.ShowMsgError(Localize.plugin_explorer_fail_to_set_files());
return false;
}
-
},
IcoPath = icoPath,
Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\uf12b")
});
-
if (record.Type is ResultType.File or ResultType.Folder)
contextMenus.Add(new Result
{
- Title = Context.API.GetTranslation("plugin_explorer_deletefilefolder"),
- SubTitle = isFile ? Context.API.GetTranslation("plugin_explorer_deletefile_subtitle") : Context.API.GetTranslation("plugin_explorer_deletefolder_subtitle"),
+ Title = Localize.plugin_explorer_deletefilefolder(),
+ SubTitle = isFile ? Localize.plugin_explorer_deletefile_subtitle(): Localize.plugin_explorer_deletefolder_subtitle(),
Action = (context) =>
{
try
{
if (Context.API.ShowMsgBox(
- string.Format(Context.API.GetTranslation("plugin_explorer_delete_folder_link"), record.FullPath),
- Context.API.GetTranslation("plugin_explorer_deletefilefolder"),
+ Localize.plugin_explorer_delete_folder_link(record.FullPath),
+ Localize.plugin_explorer_deletefilefolder(),
MessageBoxButton.OKCancel,
MessageBoxImage.Warning)
== MessageBoxResult.Cancel)
@@ -208,15 +202,15 @@ namespace Flow.Launcher.Plugin.Explorer
_ = Task.Run(() =>
{
- Context.API.ShowMsg(Context.API.GetTranslation("plugin_explorer_deletefilefoldersuccess"),
- string.Format(Context.API.GetTranslation("plugin_explorer_deletefilefoldersuccess_detail"), record.FullPath),
+ Context.API.ShowMsg(Localize.plugin_explorer_deletefilefoldersuccess(),
+ Localize.plugin_explorer_deletefilefoldersuccess_detail(record.FullPath),
Constants.ExplorerIconImageFullPath);
});
}
catch (Exception e)
{
LogException($"Fail to delete {record.FullPath}", e);
- Context.API.ShowMsgError(string.Format(Context.API.GetTranslation("plugin_explorer_fail_to_delete"), record.FullPath));
+ Context.API.ShowMsgError(Localize.plugin_explorer_fail_to_delete(record.FullPath));
return false;
}
@@ -230,7 +224,7 @@ namespace Flow.Launcher.Plugin.Explorer
{
contextMenus.Add(new Result()
{
- Title = Context.API.GetTranslation("plugin_explorer_show_contextmenu_title"),
+ Title = Localize.plugin_explorer_show_contextmenu_title(),
IcoPath = Constants.ShowContextMenuImagePath,
Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\ue700"),
Action = _ =>
@@ -248,8 +242,8 @@ namespace Flow.Launcher.Plugin.Explorer
if (record.Type == ResultType.File && CanRunAsDifferentUser(record.FullPath))
contextMenus.Add(new Result
{
- Title = Context.API.GetTranslation("plugin_explorer_runasdifferentuser"),
- SubTitle = Context.API.GetTranslation("plugin_explorer_runasdifferentuser_subtitle"),
+ Title = Localize.plugin_explorer_runasdifferentuser(),
+ SubTitle = Localize.plugin_explorer_runasdifferentuser_subtitle(),
Action = (context) =>
{
try
@@ -259,8 +253,8 @@ namespace Flow.Launcher.Plugin.Explorer
catch (FileNotFoundException e)
{
Context.API.ShowMsgError(
- Context.API.GetTranslation("plugin_explorer_plugin_name"),
- string.Format(Context.API.GetTranslation("plugin_explorer_file_not_found"), e.Message));
+ Localize.plugin_explorer_plugin_name(),
+ Localize.plugin_explorer_file_not_found(e.Message));
return false;
}
@@ -317,8 +311,8 @@ namespace Flow.Launcher.Plugin.Explorer
{
return new Result
{
- Title = Context.API.GetTranslation("plugin_explorer_opencontainingfolder"),
- SubTitle = Context.API.GetTranslation("plugin_explorer_opencontainingfolder_subtitle"),
+ Title = Localize.plugin_explorer_opencontainingfolder(),
+ SubTitle = Localize.plugin_explorer_opencontainingfolder_subtitle(),
Action = _ =>
{
try
@@ -328,7 +322,7 @@ namespace Flow.Launcher.Plugin.Explorer
catch (Exception e)
{
LogException($"Fail to open file at {record.FullPath}", e);
- Context.API.ShowMsgError(string.Format(Context.API.GetTranslation("plugin_explorer_fail_to_open"), record.FullPath));
+ Context.API.ShowMsgError(Localize.plugin_explorer_fail_to_open(record.FullPath));
return false;
}
@@ -339,11 +333,9 @@ namespace Flow.Launcher.Plugin.Explorer
};
}
-
-
private Result CreateOpenWithEditorResult(SearchResult record, string editorPath)
{
- var name = $"{Context.API.GetTranslation("plugin_explorer_openwitheditor")} {Path.GetFileNameWithoutExtension(editorPath)}";
+ var name = $"{Localize.plugin_explorer_openwitheditor()} {Path.GetFileNameWithoutExtension(editorPath)}";
return new Result
{
@@ -361,8 +353,7 @@ namespace Flow.Launcher.Plugin.Explorer
}
catch (Exception e)
{
- var raw_message = Context.API.GetTranslation("plugin_explorer_openwitheditor_error");
- var message = string.Format(raw_message, record.FullPath, Path.GetFileNameWithoutExtension(editorPath), editorPath);
+ var message = Localize.plugin_explorer_openwitheditor_error(record.FullPath, Path.GetFileNameWithoutExtension(editorPath), editorPath);
LogException(message, e);
Context.API.ShowMsgError(message);
return false;
@@ -377,7 +368,7 @@ namespace Flow.Launcher.Plugin.Explorer
{
string shellPath = Settings.ShellPath;
- var name = $"{Context.API.GetTranslation("plugin_explorer_openwithshell")} {Path.GetFileNameWithoutExtension(shellPath)}";
+ var name = $"{Localize.plugin_explorer_openwithshell()} {Path.GetFileNameWithoutExtension(shellPath)}";
return new Result
{
@@ -394,8 +385,7 @@ namespace Flow.Launcher.Plugin.Explorer
}
catch (Exception e)
{
- var raw_message = Context.API.GetTranslation("plugin_explorer_openwithshell_error");
- var message = string.Format(raw_message, record.FullPath, Path.GetFileNameWithoutExtension(shellPath), shellPath);
+ var message = Localize.plugin_explorer_openwithshell_error(record.FullPath, Path.GetFileNameWithoutExtension(shellPath), shellPath);
LogException(message, e);
Context.API.ShowMsgError(message);
return false;
@@ -410,8 +400,8 @@ namespace Flow.Launcher.Plugin.Explorer
{
return new Result
{
- Title = Context.API.GetTranslation("plugin_explorer_excludefromindexsearch"),
- SubTitle = Context.API.GetTranslation("plugin_explorer_path") + " " + record.FullPath,
+ Title = Localize.plugin_explorer_excludefromindexsearch(),
+ SubTitle = Localize.plugin_explorer_path()+ " " + record.FullPath,
Action = c_ =>
{
if (!Settings.IndexSearchExcludedSubdirectoryPaths.Any(x => string.Equals(x.Path, record.FullPath, StringComparison.OrdinalIgnoreCase)))
@@ -422,8 +412,8 @@ namespace Flow.Launcher.Plugin.Explorer
_ = Task.Run(() =>
{
- Context.API.ShowMsg(Context.API.GetTranslation("plugin_explorer_excludedfromindexsearch_msg"),
- Context.API.GetTranslation("plugin_explorer_path") +
+ Context.API.ShowMsg(Localize.plugin_explorer_excludedfromindexsearch_msg(),
+ Localize.plugin_explorer_path()+
" " + record.FullPath, Constants.ExplorerIconImageFullPath);
// so the new path can be persisted to storage and not wait till next ViewModel save.
@@ -441,8 +431,8 @@ namespace Flow.Launcher.Plugin.Explorer
{
return new Result
{
- Title = Context.API.GetTranslation("plugin_explorer_openindexingoptions"),
- SubTitle = Context.API.GetTranslation("plugin_explorer_openindexingoptions_subtitle"),
+ Title = Localize.plugin_explorer_openindexingoptions(),
+ SubTitle = Localize.plugin_explorer_openindexingoptions_subtitle(),
Action = _ =>
{
try
@@ -459,7 +449,7 @@ namespace Flow.Launcher.Plugin.Explorer
}
catch (Exception e)
{
- var message = Context.API.GetTranslation("plugin_explorer_openindexingoptions_errormsg");
+ var message = Localize.plugin_explorer_openindexingoptions_errormsg();
LogException(message, e);
Context.API.ShowMsgError(message);
return false;
@@ -470,12 +460,12 @@ namespace Flow.Launcher.Plugin.Explorer
};
}
- private Result CreateOpenWithMenu(SearchResult record)
+ private static Result CreateOpenWithMenu(SearchResult record)
{
return new Result
{
- Title = Context.API.GetTranslation("plugin_explorer_openwith"),
- SubTitle = Context.API.GetTranslation("plugin_explorer_openwith_subtitle"),
+ Title = Localize.plugin_explorer_openwith(),
+ SubTitle = Localize.plugin_explorer_openwith_subtitle(),
Action = _ =>
{
Process.Start("rundll32.exe", $"{Path.Combine(Environment.SystemDirectory, "shell32.dll")},OpenAs_RunDLL {record.FullPath}");
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj
index af33f4da2..a837a49b4 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj
@@ -19,6 +19,7 @@
..\..\Output\Release\Plugins\Flow.Launcher.Plugin.Explorer
+ $(NoWarn);FLSG0007
@@ -47,8 +48,8 @@
-
-
+
+
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml
index 16ef037cc..c40040df5 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml
@@ -24,6 +24,7 @@
Error occurred during search: {0}
Could not open folder
Could not open file
+ This new action keyword is already assigned to another plugin, please choose a different one
Delete
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs
index d93c6c77b..f5b8b9325 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs
@@ -90,12 +90,12 @@ namespace Flow.Launcher.Plugin.Explorer
public string GetTranslatedPluginTitle()
{
- return Context.API.GetTranslation("plugin_explorer_plugin_name");
+ return Localize.plugin_explorer_plugin_name();
}
public string GetTranslatedPluginDescription()
{
- return Context.API.GetTranslation("plugin_explorer_plugin_description");
+ return Localize.plugin_explorer_plugin_description();
}
public void OnCultureInfoChanged(CultureInfo newCulture)
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingDownloadHelper.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingDownloadHelper.cs
index c8bd68279..13d988f1a 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingDownloadHelper.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingDownloadHelper.cs
@@ -21,9 +21,9 @@ public static class EverythingDownloadHelper
if (string.IsNullOrEmpty(installedLocation))
{
if (api.ShowMsgBox(
- string.Format(api.GetTranslation("flowlauncher_plugin_everything_installing_select"), Environment.NewLine),
- api.GetTranslation("flowlauncher_plugin_everything_installing_title"),
- MessageBoxButton.YesNo) == MessageBoxResult.Yes)
+ Localize.flowlauncher_plugin_everything_installing_select(Environment.NewLine),
+ Localize.flowlauncher_plugin_everything_installing_title(),
+ MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
var dlg = new System.Windows.Forms.OpenFileDialog
{
@@ -41,13 +41,13 @@ public static class EverythingDownloadHelper
return installedLocation;
}
- api.ShowMsg(api.GetTranslation("flowlauncher_plugin_everything_installing_title"),
- api.GetTranslation("flowlauncher_plugin_everything_installing_subtitle"), "", useMainWindowAsOwner: false);
+ api.ShowMsg(Localize.flowlauncher_plugin_everything_installing_title(),
+ Localize.flowlauncher_plugin_everything_installing_subtitle(), "", useMainWindowAsOwner: false);
await DroplexPackage.Drop(App.Everything1_4_1_1009).ConfigureAwait(false);
- api.ShowMsg(api.GetTranslation("flowlauncher_plugin_everything_installing_title"),
- api.GetTranslation("flowlauncher_plugin_everything_installationsuccess_subtitle"), "", useMainWindowAsOwner: false);
+ api.ShowMsg(Localize.flowlauncher_plugin_everything_installing_title(),
+ Localize.flowlauncher_plugin_everything_installationsuccess_subtitle(), "", useMainWindowAsOwner: false);
installedLocation = "C:\\Program Files\\Everything\\Everything.exe";
@@ -83,6 +83,5 @@ public static class EverythingDownloadHelper
var scoopInstalledPath = Environment.ExpandEnvironmentVariables(@"%userprofile%\scoop\apps\everything\current\Everything.exe");
return File.Exists(scoopInstalledPath) ? scoopInstalledPath : string.Empty;
-
}
}
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingSearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingSearchManager.cs
index ce71c94ba..eb994a6f9 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingSearchManager.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingSearchManager.cs
@@ -27,8 +27,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
if (!await EverythingApi.IsEverythingRunningAsync(token))
throw new EngineNotAvailableException(
Enum.GetName(Settings.IndexSearchEngineOption.Everything)!,
- Main.Context.API.GetTranslation("flowlauncher_plugin_everything_click_to_launch_or_install"),
- Main.Context.API.GetTranslation("flowlauncher_plugin_everything_is_not_running"),
+ Localize.flowlauncher_plugin_everything_click_to_launch_or_install(),
+ Localize.flowlauncher_plugin_everything_is_not_running(),
Constants.EverythingErrorImagePath,
ClickToInstallEverythingAsync);
}
@@ -38,7 +38,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
Enum.GetName(Settings.IndexSearchEngineOption.Everything)!,
"Please check whether your system is x86 or x64",
Constants.GeneralSearchErrorImagePath,
- Main.Context.API.GetTranslation("flowlauncher_plugin_everything_sdk_issue"));
+ Localize.flowlauncher_plugin_everything_sdk_issue());
}
}
@@ -50,7 +50,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
if (installedPath == null)
{
- Main.Context.API.ShowMsgError(Main.Context.API.GetTranslation("flowlauncher_plugin_everything_not_found"));
+ Main.Context.API.ShowMsgError(Localize.flowlauncher_plugin_everything_not_found());
Main.Context.API.LogError(ClassName, "Unable to find Everything.exe");
return false;
@@ -65,7 +65,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
// Just let the user know that Everything is not installed properly and ask them to install it manually
catch (Exception e)
{
- Main.Context.API.ShowMsgError(Main.Context.API.GetTranslation("flowlauncher_plugin_everything_install_issue"));
+ Main.Context.API.ShowMsgError(Localize.flowlauncher_plugin_everything_install_issue());
Main.Context.API.LogException(ClassName, "Failed to install Everything", e);
return false;
@@ -97,8 +97,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
if (!Settings.EnableEverythingContentSearch)
{
throw new EngineNotAvailableException(Enum.GetName(Settings.IndexSearchEngineOption.Everything)!,
- Main.Context.API.GetTranslation("flowlauncher_plugin_everything_enable_content_search"),
- Main.Context.API.GetTranslation("flowlauncher_plugin_everything_enable_content_search_tips"),
+ Localize.flowlauncher_plugin_everything_enable_content_search(),
+ Localize.flowlauncher_plugin_everything_enable_content_search_tips(),
Constants.EverythingErrorImagePath,
_ =>
{
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs
index dfa2c8d43..18eb168b9 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
@@ -124,7 +124,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
}
catch (Exception ex)
{
- Context.API.ShowMsgBox(ex.Message, Context.API.GetTranslation("plugin_explorer_opendir_error"));
+ Context.API.ShowMsgBox(ex.Message, Localize.plugin_explorer_opendir_error());
return false;
}
}
@@ -138,7 +138,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
}
catch (Exception ex)
{
- Context.API.ShowMsgBox(ex.Message, Context.API.GetTranslation("plugin_explorer_opendir_error"));
+ Context.API.ShowMsgBox(ex.Message, Localize.plugin_explorer_opendir_error());
return false;
}
}
@@ -153,7 +153,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
}
catch (Exception ex)
{
- Context.API.ShowMsgBox(ex.Message, Context.API.GetTranslation("plugin_explorer_opendir_error"));
+ Context.API.ShowMsgBox(ex.Message, Localize.plugin_explorer_opendir_error());
return false;
}
}
@@ -166,7 +166,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
return false;
},
Score = score,
- TitleToolTip = Main.Context.API.GetTranslation("plugin_explorer_plugin_ToolTipOpenDirectory"),
+ TitleToolTip = Localize.plugin_explorer_plugin_ToolTipOpenDirectory(),
SubTitleToolTip = Settings.DisplayMoreInformationInToolTip ? GetFolderMoreInfoTooltip(path) : path,
ContextData = new SearchResult { Type = ResultType.Folder, FullPath = path, WindowsIndexed = windowsIndexed }
};
@@ -190,7 +190,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
DriveInfo drv = new DriveInfo(driveLetter);
var freespace = ToReadableSize(drv.AvailableFreeSpace, 2);
var totalspace = ToReadableSize(drv.TotalSize, 2);
- var subtitle = string.Format(Context.API.GetTranslation("plugin_explorer_diskfreespace"), freespace, totalspace);
+ var subtitle = Localize.plugin_explorer_diskfreespace(freespace, totalspace);
double usingSize = (Convert.ToDouble(drv.TotalSize) - Convert.ToDouble(drv.AvailableFreeSpace)) / Convert.ToDouble(drv.TotalSize) * 100;
int? progressValue = Convert.ToInt32(usingSize);
@@ -262,8 +262,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search
return new Result
{
- Title = Context.API.GetTranslation("plugin_explorer_openresultfolder"),
- SubTitle = Context.API.GetTranslation("plugin_explorer_openresultfolder_subtitle"),
+ Title = Localize.plugin_explorer_openresultfolder(),
+ SubTitle = Localize.plugin_explorer_openresultfolder_subtitle(),
AutoCompleteText = GetPathWithActionKeyword(folderPath, ResultType.Folder, actionKeyword),
IcoPath = folderPath,
Score = 500,
@@ -330,12 +330,12 @@ namespace Flow.Launcher.Plugin.Explorer.Search
}
catch (Exception ex)
{
- Context.API.ShowMsgBox(ex.Message, Context.API.GetTranslation("plugin_explorer_openfile_error"));
+ Context.API.ShowMsgBox(ex.Message, Localize.plugin_explorer_openfile_error());
}
return true;
},
- TitleToolTip = Main.Context.API.GetTranslation("plugin_explorer_plugin_ToolTipOpenContainingFolder"),
+ TitleToolTip = Localize.plugin_explorer_plugin_ToolTipOpenContainingFolder(),
SubTitleToolTip = Settings.DisplayMoreInformationInToolTip ? GetFileMoreInfoTooltip(filePath) : filePath,
ContextData = new SearchResult { Type = ResultType.File, FullPath = filePath, WindowsIndexed = windowsIndexed }
};
@@ -374,8 +374,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
var fileSize = PreviewPanel.GetFileSize(filePath);
var fileCreatedAt = PreviewPanel.GetFileCreatedAt(filePath, Settings.PreviewPanelDateFormat, Settings.PreviewPanelTimeFormat, Settings.ShowFileAgeInPreviewPanel);
var fileModifiedAt = PreviewPanel.GetFileLastModifiedAt(filePath, Settings.PreviewPanelDateFormat, Settings.PreviewPanelTimeFormat, Settings.ShowFileAgeInPreviewPanel);
- return string.Format(Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info"),
- filePath, fileSize, fileCreatedAt, fileModifiedAt, Environment.NewLine);
+ return Localize.plugin_explorer_plugin_tooltip_more_info(filePath, fileSize, fileCreatedAt, fileModifiedAt, Environment.NewLine);
}
catch (Exception e)
{
@@ -391,8 +390,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
var folderSize = PreviewPanel.GetFolderSize(folderPath);
var folderCreatedAt = PreviewPanel.GetFolderCreatedAt(folderPath, Settings.PreviewPanelDateFormat, Settings.PreviewPanelTimeFormat, Settings.ShowFileAgeInPreviewPanel);
var folderModifiedAt = PreviewPanel.GetFolderLastModifiedAt(folderPath, Settings.PreviewPanelDateFormat, Settings.PreviewPanelTimeFormat, Settings.ShowFileAgeInPreviewPanel);
- return string.Format(Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info"),
- folderPath, folderSize, folderCreatedAt, folderModifiedAt, Environment.NewLine);
+ return Localize.plugin_explorer_plugin_tooltip_more_info(folderPath, folderSize, folderCreatedAt, folderModifiedAt, Environment.NewLine);
}
catch (Exception e)
{
@@ -403,8 +401,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
private static string GetVolumeMoreInfoTooltip(string volumePath, string freespace, string totalspace)
{
- return string.Format(Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_volume"),
- volumePath, freespace, totalspace, Environment.NewLine);
+ return Localize.plugin_explorer_plugin_tooltip_more_info_volume(volumePath, freespace, totalspace, Environment.NewLine);
}
private static readonly string[] MediaExtensions =
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs
index f4f87d4d4..f9d8963e6 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs
@@ -161,8 +161,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search
{
new()
{
- Title = Context.API.GetTranslation("flowlauncher_plugin_everything_enable_content_search"),
- SubTitle = Context.API.GetTranslation("flowlauncher_plugin_everything_enable_content_search_tips"),
+ Title = Localize.flowlauncher_plugin_everything_enable_content_search(),
+ SubTitle = Localize.flowlauncher_plugin_everything_enable_content_search_tips(),
IcoPath = "Images/index_error.png",
Action = c =>
{
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndexSearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndexSearchManager.cs
index 3d69a1ee6..eeb5c2c4a 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndexSearchManager.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndexSearchManager.cs
@@ -105,8 +105,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
throw new EngineNotAvailableException(
"Windows Index",
- Main.Context.API.GetTranslation("plugin_explorer_windowsSearchServiceFix"),
- Main.Context.API.GetTranslation("plugin_explorer_windowsSearchServiceNotRunning"),
+ Localize.plugin_explorer_windowsSearchServiceFix(),
+ Localize.plugin_explorer_windowsSearchServiceNotRunning(),
Constants.WindowsIndexErrorImagePath,
c =>
{
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs
index 672e81d03..8d62531cd 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs
@@ -14,9 +14,9 @@ namespace Flow.Launcher.Plugin.Explorer
{
public int MaxResult { get; set; } = 100;
- public ObservableCollection QuickAccessLinks { get; set; } = new();
+ public ObservableCollection QuickAccessLinks { get; set; } = [];
- public ObservableCollection IndexSearchExcludedSubdirectoryPaths { get; set; } = new ObservableCollection();
+ public ObservableCollection IndexSearchExcludedSubdirectoryPaths { get; set; } = [];
public string EditorPath { get; set; } = "";
@@ -58,7 +58,6 @@ namespace Flow.Launcher.Plugin.Explorer
public bool QuickAccessKeywordEnabled { get; set; }
-
public bool WarnWindowsSearchServiceOff { get; set; } = true;
public bool ShowFileSizeInPreviewPanel { get; set; } = true;
@@ -69,7 +68,6 @@ namespace Flow.Launcher.Plugin.Explorer
public bool ShowFileAgeInPreviewPanel { get; set; } = false;
-
public string PreviewPanelDateFormat { get; set; } = "yyyy-MM-dd";
public string PreviewPanelTimeFormat { get; set; } = "HH:mm";
@@ -82,8 +80,8 @@ namespace Flow.Launcher.Plugin.Explorer
private EverythingSearchManager EverythingManagerInstance => _everythingManagerInstance ??= new EverythingSearchManager(this);
private WindowsIndexSearchManager WindowsIndexSearchManager => _windowsIndexSearchManager ??= new WindowsIndexSearchManager(this);
-
public IndexSearchEngineOption IndexSearchEngine { get; set; } = IndexSearchEngineOption.WindowsIndex;
+
[JsonIgnore]
public IIndexProvider IndexProvider => IndexSearchEngine switch
{
@@ -139,7 +137,6 @@ namespace Flow.Launcher.Plugin.Explorer
#endregion
-
#region Everything Settings
public string EverythingInstalledPath { get; set; }
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs
index 7292697ce..2d46c6307 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs
@@ -296,7 +296,7 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
return;
}
- var actionKeywordWindow = new ActionKeywordSetting(actionKeyword, Context.API);
+ var actionKeywordWindow = new ActionKeywordSetting(actionKeyword);
if (!(actionKeywordWindow.ShowDialog() ?? false))
{
@@ -432,8 +432,8 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
case "QuickAccessLink":
if (SelectedQuickAccessLink == null) return;
if (Context.API.ShowMsgBox(
- Context.API.GetTranslation("plugin_explorer_delete_quick_access_link"),
- Context.API.GetTranslation("plugin_explorer_delete"),
+ Localize.plugin_explorer_delete_quick_access_link(),
+ Localize.plugin_explorer_delete(),
MessageBoxButton.OKCancel,
MessageBoxImage.Warning)
== MessageBoxResult.Cancel)
@@ -443,8 +443,8 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
case "IndexSearchExcludedPaths":
if (SelectedIndexSearchExcludedPath == null) return;
if (Context.API.ShowMsgBox(
- Context.API.GetTranslation("plugin_explorer_delete_index_search_excluded_path"),
- Context.API.GetTranslation("plugin_explorer_delete"),
+ Localize.plugin_explorer_delete_index_search_excluded_path(),
+ Localize.plugin_explorer_delete(),
MessageBoxButton.OKCancel,
MessageBoxImage.Warning)
== MessageBoxResult.Cancel)
@@ -457,7 +457,7 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
private void ShowUnselectedMessage()
{
- var warning = Context.API.GetTranslation("plugin_explorer_make_selection_warning");
+ var warning = Localize.plugin_explorer_make_selection_warning();
Context.API.ShowMsgBox(warning);
}
@@ -577,8 +577,8 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
}
}
- public int MaxResultLowerLimit => 1;
- public int MaxResultUpperLimit => 100000;
+ public int MaxResultLowerLimit { get; } = 1;
+ public int MaxResultUpperLimit { get; } = 100000;
public int MaxResult
{
@@ -592,7 +592,7 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
#region Everything FastSortWarning
- public List AllEverythingSortOptions = EverythingSortOptionLocalized.GetValues();
+ public List AllEverythingSortOptions { get; } = EverythingSortOptionLocalized.GetValues();
public EverythingSortOption SelectedEverythingSortOption
{
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs
index 829a2feed..562170062 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs
@@ -29,13 +29,11 @@ namespace Flow.Launcher.Plugin.Explorer.Views
}
private string actionKeyword;
- private readonly IPublicAPI _api;
private bool _keywordEnabled;
- public ActionKeywordSetting(ActionKeywordModel selectedActionKeyword, IPublicAPI api)
+ public ActionKeywordSetting(ActionKeywordModel selectedActionKeyword)
{
CurrentActionKeyword = selectedActionKeyword;
- _api = api;
ActionKeyword = selectedActionKeyword.Keyword;
KeywordEnabled = selectedActionKeyword.Enabled;
@@ -60,14 +58,14 @@ namespace Flow.Launcher.Plugin.Explorer.Views
switch (CurrentActionKeyword.KeywordProperty, KeywordEnabled)
{
case (Settings.ActionKeyword.FileContentSearchActionKeyword, true):
- _api.ShowMsgBox(_api.GetTranslation("plugin_explorer_globalActionKeywordInvalid"));
+ Main.Context.API.ShowMsgBox(Localize.plugin_explorer_globalActionKeywordInvalid());
return;
case (Settings.ActionKeyword.QuickAccessActionKeyword, true):
- _api.ShowMsgBox(_api.GetTranslation("plugin_explorer_quickaccess_globalActionKeywordInvalid"));
+ Main.Context.API.ShowMsgBox(Localize.plugin_explorer_quickaccess_globalActionKeywordInvalid());
return;
}
- if (!KeywordEnabled || !_api.ActionKeywordAssigned(ActionKeyword))
+ if (!KeywordEnabled || !Main.Context.API.ActionKeywordAssigned(ActionKeyword))
{
DialogResult = true;
Close();
@@ -75,7 +73,7 @@ namespace Flow.Launcher.Plugin.Explorer.Views
}
// The keyword is not valid, so show message
- _api.ShowMsgBox(_api.GetTranslation("newActionKeywordsHasBeenAssigned"));
+ Main.Context.API.ShowMsgBox(Localize.plugin_explorer_new_action_keyword_assigned());
}
private void BtnCancel_OnClick(object sender, RoutedEventArgs e)
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/PreviewPanel.xaml.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Views/PreviewPanel.xaml.cs
index 4dd0588ee..3c627cc06 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/PreviewPanel.xaml.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/PreviewPanel.xaml.cs
@@ -25,7 +25,7 @@ public partial class PreviewPanel : UserControl
public string FileName { get; }
[ObservableProperty]
- private string _fileSize = Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown");
+ private string _fileSize = Localize.plugin_explorer_plugin_tooltip_more_info_unknown();
[ObservableProperty]
private string _createdAt = "";
@@ -111,17 +111,17 @@ public partial class PreviewPanel : UserControl
catch (FileNotFoundException)
{
Main.Context.API.LogError(ClassName, $"File not found: {filePath}");
- return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown");
+ return Localize.plugin_explorer_plugin_tooltip_more_info_unknown();
}
catch (UnauthorizedAccessException)
{
Main.Context.API.LogError(ClassName, $"Access denied to file: {filePath}");
- return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown");
+ return Localize.plugin_explorer_plugin_tooltip_more_info_unknown();
}
catch (Exception e)
{
Main.Context.API.LogException(ClassName, $"Failed to get file size for {filePath}", e);
- return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown");
+ return Localize.plugin_explorer_plugin_tooltip_more_info_unknown();
}
}
@@ -142,17 +142,17 @@ public partial class PreviewPanel : UserControl
catch (FileNotFoundException)
{
Main.Context.API.LogError(ClassName, $"File not found: {filePath}");
- return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown");
+ return Localize.plugin_explorer_plugin_tooltip_more_info_unknown();
}
catch (UnauthorizedAccessException)
{
Main.Context.API.LogError(ClassName, $"Access denied to file: {filePath}");
- return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown");
+ return Localize.plugin_explorer_plugin_tooltip_more_info_unknown();
}
catch (Exception e)
{
Main.Context.API.LogException(ClassName, $"Failed to get file created date for {filePath}", e);
- return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown");
+ return Localize.plugin_explorer_plugin_tooltip_more_info_unknown();
}
}
@@ -173,17 +173,17 @@ public partial class PreviewPanel : UserControl
catch (FileNotFoundException)
{
Main.Context.API.LogError(ClassName, $"File not found: {filePath}");
- return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown");
+ return Localize.plugin_explorer_plugin_tooltip_more_info_unknown();
}
catch (UnauthorizedAccessException)
{
Main.Context.API.LogError(ClassName, $"Access denied to file: {filePath}");
- return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown");
+ return Localize.plugin_explorer_plugin_tooltip_more_info_unknown();
}
catch (Exception e)
{
Main.Context.API.LogException(ClassName, $"Failed to get file modified date for {filePath}", e);
- return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown");
+ return Localize.plugin_explorer_plugin_tooltip_more_info_unknown();
}
}
@@ -205,17 +205,17 @@ public partial class PreviewPanel : UserControl
catch (FileNotFoundException)
{
Main.Context.API.LogError(ClassName, $"Folder not found: {folderPath}");
- return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown");
+ return Localize.plugin_explorer_plugin_tooltip_more_info_unknown();
}
catch (UnauthorizedAccessException)
{
Main.Context.API.LogError(ClassName, $"Access denied to folder: {folderPath}");
- return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown");
+ return Localize.plugin_explorer_plugin_tooltip_more_info_unknown();
}
catch (OperationCanceledException)
{
Main.Context.API.LogError(ClassName, $"Operation timed out while calculating folder size for {folderPath}");
- return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown");
+ return Localize.plugin_explorer_plugin_tooltip_more_info_unknown();
}
// For parallel operations, AggregateException may be thrown if any of the tasks fail
catch (AggregateException ae)
@@ -224,22 +224,22 @@ public partial class PreviewPanel : UserControl
{
case FileNotFoundException:
Main.Context.API.LogError(ClassName, $"Folder not found: {folderPath}");
- return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown");
+ return Localize.plugin_explorer_plugin_tooltip_more_info_unknown();
case UnauthorizedAccessException:
Main.Context.API.LogError(ClassName, $"Access denied to folder: {folderPath}");
- return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown");
+ return Localize.plugin_explorer_plugin_tooltip_more_info_unknown();
case OperationCanceledException:
Main.Context.API.LogError(ClassName, $"Operation timed out while calculating folder size for {folderPath}");
- return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown");
+ return Localize.plugin_explorer_plugin_tooltip_more_info_unknown();
default:
Main.Context.API.LogException(ClassName, $"Failed to get folder size for {folderPath}", ae);
- return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown");
+ return Localize.plugin_explorer_plugin_tooltip_more_info_unknown();
}
}
catch (Exception e)
{
Main.Context.API.LogException(ClassName, $"Failed to get folder size for {folderPath}", e);
- return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown");
+ return Localize.plugin_explorer_plugin_tooltip_more_info_unknown();
}
}
@@ -260,17 +260,17 @@ public partial class PreviewPanel : UserControl
catch (FileNotFoundException)
{
Main.Context.API.LogError(ClassName, $"Folder not found: {folderPath}");
- return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown");
+ return Localize.plugin_explorer_plugin_tooltip_more_info_unknown();
}
catch (UnauthorizedAccessException)
{
Main.Context.API.LogError(ClassName, $"Access denied to folder: {folderPath}");
- return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown");
+ return Localize.plugin_explorer_plugin_tooltip_more_info_unknown();
}
catch (Exception e)
{
Main.Context.API.LogException(ClassName, $"Failed to get folder created date for {folderPath}", e);
- return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown");
+ return Localize.plugin_explorer_plugin_tooltip_more_info_unknown();
}
}
@@ -291,17 +291,17 @@ public partial class PreviewPanel : UserControl
catch (FileNotFoundException)
{
Main.Context.API.LogError(ClassName, $"Folder not found: {folderPath}");
- return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown");
+ return Localize.plugin_explorer_plugin_tooltip_more_info_unknown();
}
catch (UnauthorizedAccessException)
{
Main.Context.API.LogError(ClassName, $"Access denied to folder: {folderPath}");
- return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown");
+ return Localize.plugin_explorer_plugin_tooltip_more_info_unknown();
}
catch (Exception e)
{
Main.Context.API.LogException(ClassName, $"Failed to get folder modified date for {folderPath}", e);
- return Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown");
+ return Localize.plugin_explorer_plugin_tooltip_more_info_unknown();
}
}
@@ -311,21 +311,20 @@ public partial class PreviewPanel : UserControl
var difference = now - fileDateTime;
if (difference.TotalDays < 1)
- return Main.Context.API.GetTranslation("Today");
+ return Localize.Today();
if (difference.TotalDays < 30)
- return string.Format(Main.Context.API.GetTranslation("DaysAgo"), (int)difference.TotalDays);
+ return Localize.DaysAgo((int)difference.TotalDays);
var monthsDiff = (now.Year - fileDateTime.Year) * 12 + now.Month - fileDateTime.Month;
if (monthsDiff == 1)
- return Main.Context.API.GetTranslation("OneMonthAgo");
+ return Localize.OneMonthAgo();
if (monthsDiff < 12)
- return string.Format(Main.Context.API.GetTranslation("MonthsAgo"), monthsDiff);
+ return Localize.MonthsAgo(monthsDiff);
var yearsDiff = now.Year - fileDateTime.Year;
if (now.Month < fileDateTime.Month || (now.Month == fileDateTime.Month && now.Day < fileDateTime.Day))
yearsDiff--;
- return yearsDiff == 1 ? Main.Context.API.GetTranslation("OneYearAgo") :
- string.Format(Main.Context.API.GetTranslation("YearsAgo"), yearsDiff);
+ return yearsDiff == 1 ? Localize.OneYearAgo(): Localize.YearsAgo(yearsDiff);
}
}
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/QuickAccessLinkSettings.xaml.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Views/QuickAccessLinkSettings.xaml.cs
index e6294b98b..f8929549b 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/QuickAccessLinkSettings.xaml.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/QuickAccessLinkSettings.xaml.cs
@@ -97,7 +97,7 @@ public partial class QuickAccessLinkSettings
// Validate the input before proceeding
if (string.IsNullOrEmpty(SelectedName) || string.IsNullOrEmpty(SelectedPath))
{
- var warning = Main.Context.API.GetTranslation("plugin_explorer_quick_access_link_no_folder_selected");
+ var warning = Localize.plugin_explorer_quick_access_link_no_folder_selected();
Main.Context.API.ShowMsgBox(warning);
return;
}
@@ -107,7 +107,7 @@ public partial class QuickAccessLinkSettings
x.Path.Equals(SelectedPath, StringComparison.OrdinalIgnoreCase) &&
x.Name.Equals(SelectedName, StringComparison.OrdinalIgnoreCase)))
{
- var warning = Main.Context.API.GetTranslation("plugin_explorer_quick_access_link_path_already_exists");
+ var warning = Localize.plugin_explorer_quick_access_link_path_already_exists();
Main.Context.API.ShowMsgBox(warning);
return;
}
diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj
index 2da97ebbd..0a7a02a45 100644
--- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj
+++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj
@@ -52,7 +52,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj b/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj
index da2b19d7c..e9515fab4 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj
+++ b/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj
@@ -64,12 +64,12 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs
index 7c30c0c96..0258a10d2 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs
+++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs
@@ -31,7 +31,7 @@ namespace Flow.Launcher.Plugin.Program
internal static PluginInitContext Context { get; private set; }
- private static readonly List emptyResults = new();
+ private static readonly List emptyResults = [];
private static readonly MemoryCacheOptions cacheOptions = new() { SizeLimit = 1560 };
private static MemoryCache cache = new(cacheOptions);
@@ -84,7 +84,6 @@ namespace Flow.Launcher.Plugin.Program
{
await _win32sLock.WaitAsync(token);
await _uwpsLock.WaitAsync(token);
-
try
{
// Collect all UWP Windows app directories
@@ -117,7 +116,7 @@ namespace Flow.Launcher.Plugin.Program
}
}, token);
- resultList = resultList.Any() ? resultList : emptyResults;
+ resultList = resultList.Count != 0 ? resultList : emptyResults;
entry.SetSize(resultList.Count);
entry.SetSlidingExpiration(TimeSpan.FromHours(8));
@@ -250,14 +249,26 @@ namespace Flow.Launcher.Plugin.Program
}
await _win32sLock.WaitAsync();
- _win32s = await context.API.LoadCacheBinaryStorageAsync(Win32CacheName, pluginCacheDirectory, new List());
- _win32sCount = _win32s.Count;
- _win32sLock.Release();
+ try
+ {
+ _win32s = await context.API.LoadCacheBinaryStorageAsync(Win32CacheName, pluginCacheDirectory, new List());
+ _win32sCount = _win32s.Count;
+ }
+ finally
+ {
+ _win32sLock.Release();
+ }
await _uwpsLock.WaitAsync();
- _uwps = await context.API.LoadCacheBinaryStorageAsync(UwpCacheName, pluginCacheDirectory, new List());
- _uwpsCount = _uwps.Count;
- _uwpsLock.Release();
+ try
+ {
+ _uwps = await context.API.LoadCacheBinaryStorageAsync(UwpCacheName, pluginCacheDirectory, new List());
+ _uwpsCount = _uwps.Count;
+ }
+ finally
+ {
+ _uwpsLock.Release();
+ }
});
Context.API.LogInfo(ClassName, $"Number of preload win32 programs <{_win32sCount}>");
Context.API.LogInfo(ClassName, $"Number of preload uwps <{_uwpsCount}>");
@@ -408,38 +419,46 @@ namespace Flow.Launcher.Plugin.Program
return;
await _uwpsLock.WaitAsync();
- if (_uwps.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier))
+ var reindexUwps = true;
+ try
{
+ reindexUwps = _uwps.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier);
var program = _uwps.First(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier);
program.Enabled = false;
_settings.DisabledProgramSources.Add(new ProgramSource(program));
+ }
+ finally
+ {
_uwpsLock.Release();
+ }
- // Reindex UWP programs
+ // Reindex UWP programs
+ if (reindexUwps)
+ {
_ = Task.Run(IndexUwpProgramsAsync);
return;
}
- else
- {
- _uwpsLock.Release();
- }
await _win32sLock.WaitAsync();
- if (_win32s.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier))
+ var reindexWin32s = true;
+ try
{
+ reindexWin32s = _win32s.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier);
var program = _win32s.First(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier);
program.Enabled = false;
_settings.DisabledProgramSources.Add(new ProgramSource(program));
- _win32sLock.Release();
-
- // Reindex Win32 programs
- _ = Task.Run(IndexWin32ProgramsAsync);
- return;
}
- else
+ finally
{
_win32sLock.Release();
}
+
+ // Reindex Win32 programs
+ if (reindexWin32s)
+ {
+ _ = Task.Run(IndexWin32ProgramsAsync);
+ return;
+ }
}
public static void StartProcess(Func runProcess, ProcessStartInfo info)
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Views/Commands/ProgramSettingDisplay.cs b/Plugins/Flow.Launcher.Plugin.Program/Views/Commands/ProgramSettingDisplay.cs
index b89a2a6ba..2a6a3e987 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Views/Commands/ProgramSettingDisplay.cs
+++ b/Plugins/Flow.Launcher.Plugin.Program/Views/Commands/ProgramSettingDisplay.cs
@@ -19,18 +19,30 @@ namespace Flow.Launcher.Plugin.Program.Views.Commands
internal static async Task DisplayAllProgramsAsync()
{
await Main._win32sLock.WaitAsync();
- var win32 = Main._win32s
+ try
+ {
+ var win32 = Main._win32s
.Where(t1 => !ProgramSetting.ProgramSettingDisplayList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier))
.Select(x => new ProgramSource(x));
- ProgramSetting.ProgramSettingDisplayList.AddRange(win32);
- Main._win32sLock.Release();
+ ProgramSetting.ProgramSettingDisplayList.AddRange(win32);
+ }
+ finally
+ {
+ Main._win32sLock.Release();
+ }
await Main._uwpsLock.WaitAsync();
- var uwp = Main._uwps
+ try
+ {
+ var uwp = Main._uwps
.Where(t1 => !ProgramSetting.ProgramSettingDisplayList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier))
.Select(x => new ProgramSource(x));
- ProgramSetting.ProgramSettingDisplayList.AddRange(uwp);
- Main._uwpsLock.Release();
+ ProgramSetting.ProgramSettingDisplayList.AddRange(uwp);
+ }
+ finally
+ {
+ Main._uwpsLock.Release();
+ }
}
internal static async Task SetProgramSourcesStatusAsync(List selectedProgramSourcesToDisable, bool status)
@@ -44,24 +56,36 @@ namespace Flow.Launcher.Plugin.Program.Views.Commands
}
await Main._win32sLock.WaitAsync();
- foreach (var program in Main._win32s)
+ try
{
- if (selectedProgramSourcesToDisable.Any(x => x.UniqueIdentifier == program.UniqueIdentifier && program.Enabled != status))
+ foreach (var program in Main._win32s)
{
- program.Enabled = status;
+ if (selectedProgramSourcesToDisable.Any(x => x.UniqueIdentifier == program.UniqueIdentifier && program.Enabled != status))
+ {
+ program.Enabled = status;
+ }
}
}
- Main._win32sLock.Release();
+ finally
+ {
+ Main._win32sLock.Release();
+ }
await Main._uwpsLock.WaitAsync();
- foreach (var program in Main._uwps)
+ try
{
- if (selectedProgramSourcesToDisable.Any(x => x.UniqueIdentifier == program.UniqueIdentifier && program.Enabled != status))
+ foreach (var program in Main._uwps)
{
- program.Enabled = status;
+ if (selectedProgramSourcesToDisable.Any(x => x.UniqueIdentifier == program.UniqueIdentifier && program.Enabled != status))
+ {
+ program.Enabled = status;
+ }
}
}
- Main._uwpsLock.Release();
+ finally
+ {
+ Main._uwpsLock.Release();
+ }
}
internal static void StoreDisabledInSettings()
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj b/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj
index 8e54e1894..44fc9a8cf 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj
+++ b/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj
@@ -58,7 +58,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive