From 438bb8a2fcdb6fd96258da5b9b0c5bca8905e47e Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 24 Jan 2026 07:43:35 +0000
Subject: [PATCH 01/18] Initial plan
From d61ec1828f89e78c2f55cd2475650179cc789688 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 24 Jan 2026 07:47:57 +0000
Subject: [PATCH 02/18] Add relative path resolution support for Python and
Node.js executables
Co-authored-by: Jack251970 <53996452+Jack251970@users.noreply.github.com>
---
.../Environments/AbstractPluginEnvironment.cs | 21 +++-
Flow.Launcher.Infrastructure/Constant.cs | 20 ++++
Flow.Launcher.Test/PathResolutionTest.cs | 104 ++++++++++++++++++
3 files changed, 139 insertions(+), 6 deletions(-)
create mode 100644 Flow.Launcher.Test/PathResolutionTest.cs
diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs
index 1a324a993..c37030812 100644
--- a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs
+++ b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs
@@ -4,6 +4,7 @@ using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Forms;
+using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.SharedCommands;
@@ -40,6 +41,12 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
PluginSettings = pluginSettings;
}
+ ///
+ /// Resolves the configured executable path to an absolute path.
+ /// Supports both absolute paths and relative paths (relative to ProgramDirectory).
+ ///
+ private string ResolvedPluginsSettingsFilePath => Constant.ResolveAbsolutePath(PluginsSettingsFilePath);
+
internal IEnumerable Setup()
{
// If no plugin is using the language, return empty list
@@ -48,13 +55,14 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
return new List();
}
- if (!string.IsNullOrEmpty(PluginsSettingsFilePath) && FilesFolders.FileExists(PluginsSettingsFilePath))
+ var resolvedPath = ResolvedPluginsSettingsFilePath;
+ if (!string.IsNullOrEmpty(resolvedPath) && FilesFolders.FileExists(resolvedPath))
{
// Ensure latest only if user is using Flow's environment setup.
- if (PluginsSettingsFilePath.StartsWith(EnvPath, StringComparison.OrdinalIgnoreCase))
- EnsureLatestInstalled(ExecutablePath, PluginsSettingsFilePath, EnvPath);
+ if (resolvedPath.StartsWith(EnvPath, StringComparison.OrdinalIgnoreCase))
+ EnsureLatestInstalled(ExecutablePath, resolvedPath, EnvPath);
- return SetPathForPluginPairs(PluginsSettingsFilePath, Language);
+ return SetPathForPluginPairs(resolvedPath, Language);
}
var noRuntimeMessage = Localize.runtimePluginInstalledChooseRuntimePrompt(Language, EnvName, Environment.NewLine);
@@ -103,9 +111,10 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
InstallEnvironment();
}
- if (FilesFolders.FileExists(PluginsSettingsFilePath))
+ resolvedPath = ResolvedPluginsSettingsFilePath;
+ if (FilesFolders.FileExists(resolvedPath))
{
- return SetPathForPluginPairs(PluginsSettingsFilePath, Language);
+ return SetPathForPluginPairs(resolvedPath, Language);
}
else
{
diff --git a/Flow.Launcher.Infrastructure/Constant.cs b/Flow.Launcher.Infrastructure/Constant.cs
index 13da9f79f..57175930f 100644
--- a/Flow.Launcher.Infrastructure/Constant.cs
+++ b/Flow.Launcher.Infrastructure/Constant.cs
@@ -56,5 +56,25 @@ namespace Flow.Launcher.Infrastructure
public const string Docs = "https://flowlauncher.com/docs";
public const string SystemLanguageCode = "system";
+
+ ///
+ /// Resolves a path that may be relative to an absolute path.
+ /// If the path is already absolute, returns it as-is.
+ /// If the path is relative (starts with . or doesn't contain a drive), resolves it relative to ProgramDirectory.
+ ///
+ /// The path to resolve
+ /// An absolute path
+ public static string ResolveAbsolutePath(string path)
+ {
+ if (string.IsNullOrEmpty(path))
+ return path;
+
+ // If already absolute, return as-is
+ if (Path.IsPathRooted(path))
+ return path;
+
+ // Resolve relative to ProgramDirectory
+ return Path.GetFullPath(Path.Combine(ProgramDirectory, path));
+ }
}
}
diff --git a/Flow.Launcher.Test/PathResolutionTest.cs b/Flow.Launcher.Test/PathResolutionTest.cs
new file mode 100644
index 000000000..4e03a2d6f
--- /dev/null
+++ b/Flow.Launcher.Test/PathResolutionTest.cs
@@ -0,0 +1,104 @@
+using System;
+using System.IO;
+using Xunit;
+using Flow.Launcher.Infrastructure;
+
+namespace Flow.Launcher.Test
+{
+ public class PathResolutionTest
+ {
+ [Fact]
+ public void ResolveAbsolutePath_WithAbsolutePath_ReturnsOriginalPath()
+ {
+ // Arrange
+ var absolutePath = @"C:\Program Files\Python\python.exe";
+
+ // Act
+ var result = Constant.ResolveAbsolutePath(absolutePath);
+
+ // Assert
+ Assert.Equal(absolutePath, result);
+ }
+
+ [Fact]
+ public void ResolveAbsolutePath_WithRelativePath_ResolvesToProgramDirectory()
+ {
+ // Arrange
+ var relativePath = @".\runtimes\python\pythonw.exe";
+
+ // Act
+ var result = Constant.ResolveAbsolutePath(relativePath);
+
+ // Assert
+ Assert.True(Path.IsPathRooted(result), "Result should be an absolute path");
+ Assert.Contains(Constant.ProgramDirectory, result);
+ Assert.EndsWith(@"runtimes\python\pythonw.exe", result);
+ }
+
+ [Fact]
+ public void ResolveAbsolutePath_WithDotDotPath_ResolvesCorrectly()
+ {
+ // Arrange
+ var relativePath = @"..\runtimes\node\node.exe";
+
+ // Act
+ var result = Constant.ResolveAbsolutePath(relativePath);
+
+ // Assert
+ Assert.True(Path.IsPathRooted(result), "Result should be an absolute path");
+ }
+
+ [Fact]
+ public void ResolveAbsolutePath_WithNullPath_ReturnsNull()
+ {
+ // Arrange
+ string nullPath = null;
+
+ // Act
+ var result = Constant.ResolveAbsolutePath(nullPath);
+
+ // Assert
+ Assert.Null(result);
+ }
+
+ [Fact]
+ public void ResolveAbsolutePath_WithEmptyPath_ReturnsEmpty()
+ {
+ // Arrange
+ var emptyPath = string.Empty;
+
+ // Act
+ var result = Constant.ResolveAbsolutePath(emptyPath);
+
+ // Assert
+ Assert.Equal(string.Empty, result);
+ }
+
+ [Fact]
+ public void ResolveAbsolutePath_WithForwardSlashes_ResolvesCorrectly()
+ {
+ // Arrange
+ var relativePath = @"./runtimes/python/pythonw.exe";
+
+ // Act
+ var result = Constant.ResolveAbsolutePath(relativePath);
+
+ // Assert
+ Assert.True(Path.IsPathRooted(result), "Result should be an absolute path");
+ Assert.Contains(Constant.ProgramDirectory, result);
+ }
+
+ [Fact]
+ public void ResolveAbsolutePath_WithUNCPath_ReturnsOriginalPath()
+ {
+ // Arrange
+ var uncPath = @"\\server\share\python\pythonw.exe";
+
+ // Act
+ var result = Constant.ResolveAbsolutePath(uncPath);
+
+ // Assert
+ Assert.Equal(uncPath, result);
+ }
+ }
+}
From 864eedd255410baaa8bb3f405d010f9804ee766d Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 24 Jan 2026 07:49:13 +0000
Subject: [PATCH 03/18] Add UI descriptions for relative path support
Co-authored-by: Jack251970 <53996452+Jack251970@users.noreply.github.com>
---
.../SettingPages/Views/SettingsPaneGeneral.xaml | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml b/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml
index b4c94cb35..8900a16ce 100644
--- a/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml
+++ b/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml
@@ -476,7 +476,10 @@
Content="{Binding Settings.CustomBrowser.DisplayName}" />
-
+
-
+
Date: Sat, 24 Jan 2026 07:51:22 +0000
Subject: [PATCH 04/18] Address code review feedback: convert absolute to
relative paths for portability
Co-authored-by: Jack251970 <53996452+Jack251970@users.noreply.github.com>
---
.../Environments/AbstractPluginEnvironment.cs | 8 +-
Flow.Launcher.Infrastructure/Constant.cs | 41 +++++++++
Flow.Launcher.Test/PathResolutionTest.cs | 84 +++++++++++++++++++
.../SettingsPaneGeneralViewModel.cs | 6 +-
4 files changed, 134 insertions(+), 5 deletions(-)
diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs
index c37030812..3a9732aa3 100644
--- a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs
+++ b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs
@@ -42,7 +42,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
}
///
- /// Resolves the configured executable path to an absolute path.
+ /// Resolves the configured plugin settings file path to an absolute path.
/// Supports both absolute paths and relative paths (relative to ProgramDirectory).
///
private string ResolvedPluginsSettingsFilePath => Constant.ResolveAbsolutePath(PluginsSettingsFilePath);
@@ -74,7 +74,8 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
if (!string.IsNullOrEmpty(selectedFile))
{
- PluginsSettingsFilePath = selectedFile;
+ // Convert to relative path if within ProgramDirectory for portability
+ PluginsSettingsFilePath = Constant.ConvertToRelativePathIfPossible(selectedFile);
}
// Nothing selected because user pressed cancel from the file dialog window
else
@@ -98,7 +99,8 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
if (!string.IsNullOrEmpty(selectedFile))
{
- PluginsSettingsFilePath = selectedFile;
+ // Convert to relative path if within ProgramDirectory for portability
+ PluginsSettingsFilePath = Constant.ConvertToRelativePathIfPossible(selectedFile);
}
else
{
diff --git a/Flow.Launcher.Infrastructure/Constant.cs b/Flow.Launcher.Infrastructure/Constant.cs
index 57175930f..fd1538646 100644
--- a/Flow.Launcher.Infrastructure/Constant.cs
+++ b/Flow.Launcher.Infrastructure/Constant.cs
@@ -76,5 +76,46 @@ namespace Flow.Launcher.Infrastructure
// Resolve relative to ProgramDirectory
return Path.GetFullPath(Path.Combine(ProgramDirectory, path));
}
+
+ ///
+ /// Converts an absolute path to a relative path if it's within ProgramDirectory.
+ /// This enables portability by storing paths relative to the program directory when possible.
+ ///
+ /// The absolute path to convert
+ /// A relative path if the path is within ProgramDirectory, otherwise the original absolute path
+ public static string ConvertToRelativePathIfPossible(string absolutePath)
+ {
+ if (string.IsNullOrEmpty(absolutePath))
+ return absolutePath;
+
+ if (!Path.IsPathRooted(absolutePath))
+ return absolutePath;
+
+ try
+ {
+ // Get the full absolute paths for comparison
+ var fullAbsolutePath = Path.GetFullPath(absolutePath);
+ var fullProgramDir = Path.GetFullPath(ProgramDirectory);
+
+ // Check if the absolute path is within ProgramDirectory
+ if (fullAbsolutePath.StartsWith(fullProgramDir, StringComparison.OrdinalIgnoreCase))
+ {
+ // Convert to relative path
+ var relativePath = Path.GetRelativePath(fullProgramDir, fullAbsolutePath);
+
+ // Prefix with .\ for clarity
+ if (!relativePath.StartsWith("."))
+ relativePath = ".\\" + relativePath;
+
+ return relativePath;
+ }
+ }
+ catch
+ {
+ // If conversion fails, return the original path
+ }
+
+ return absolutePath;
+ }
}
}
diff --git a/Flow.Launcher.Test/PathResolutionTest.cs b/Flow.Launcher.Test/PathResolutionTest.cs
index 4e03a2d6f..3888b41a4 100644
--- a/Flow.Launcher.Test/PathResolutionTest.cs
+++ b/Flow.Launcher.Test/PathResolutionTest.cs
@@ -100,5 +100,89 @@ namespace Flow.Launcher.Test
// Assert
Assert.Equal(uncPath, result);
}
+
+ [Fact]
+ public void ConvertToRelativePathIfPossible_WithPathInProgramDirectory_ReturnsRelativePath()
+ {
+ // Arrange
+ var absolutePath = Path.Combine(Constant.ProgramDirectory, "runtimes", "python", "pythonw.exe");
+
+ // Act
+ var result = Constant.ConvertToRelativePathIfPossible(absolutePath);
+
+ // Assert
+ Assert.True(result.StartsWith(".\\"), "Result should start with .\\");
+ Assert.Contains("runtimes", result);
+ Assert.Contains("python", result);
+ }
+
+ [Fact]
+ public void ConvertToRelativePathIfPossible_WithPathOutsideProgramDirectory_ReturnsAbsolutePath()
+ {
+ // Arrange
+ var absolutePath = @"C:\Python\python.exe";
+
+ // Act
+ var result = Constant.ConvertToRelativePathIfPossible(absolutePath);
+
+ // Assert
+ Assert.Equal(absolutePath, result);
+ }
+
+ [Fact]
+ public void ConvertToRelativePathIfPossible_WithRelativePath_ReturnsOriginalPath()
+ {
+ // Arrange
+ var relativePath = @".\runtimes\python\pythonw.exe";
+
+ // Act
+ var result = Constant.ConvertToRelativePathIfPossible(relativePath);
+
+ // Assert
+ Assert.Equal(relativePath, result);
+ }
+
+ [Fact]
+ public void ConvertToRelativePathIfPossible_WithNullPath_ReturnsNull()
+ {
+ // Arrange
+ string nullPath = null;
+
+ // Act
+ var result = Constant.ConvertToRelativePathIfPossible(nullPath);
+
+ // Assert
+ Assert.Null(result);
+ }
+
+ [Fact]
+ public void ConvertToRelativePathIfPossible_WithEmptyPath_ReturnsEmpty()
+ {
+ // Arrange
+ var emptyPath = string.Empty;
+
+ // Act
+ var result = Constant.ConvertToRelativePathIfPossible(emptyPath);
+
+ // Assert
+ Assert.Equal(string.Empty, result);
+ }
+
+ [Fact]
+ public void RoundTripTest_RelativePathResolutionAndConversion()
+ {
+ // Arrange
+ var originalRelative = @".\runtimes\python\pythonw.exe";
+
+ // Act - Resolve to absolute
+ var absolute = Constant.ResolveAbsolutePath(originalRelative);
+ // Convert back to relative
+ var backToRelative = Constant.ConvertToRelativePathIfPossible(absolute);
+
+ // Assert
+ Assert.True(Path.IsPathRooted(absolute), "Resolved path should be absolute");
+ Assert.True(backToRelative.StartsWith(".\\"), "Converted path should be relative");
+ Assert.Contains("runtimes\\python\\pythonw.exe", backToRelative);
+ }
}
}
diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs
index aa78849ba..eb00ae3ea 100644
--- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs
+++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs
@@ -377,7 +377,8 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
);
if (!string.IsNullOrEmpty(selectedFile))
- Settings.PluginSettings.PythonExecutablePath = selectedFile;
+ // Convert to relative path if within ProgramDirectory for portability
+ Settings.PluginSettings.PythonExecutablePath = Constant.ConvertToRelativePathIfPossible(selectedFile);
}
[RelayCommand]
@@ -389,7 +390,8 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
);
if (!string.IsNullOrEmpty(selectedFile))
- Settings.PluginSettings.NodeExecutablePath = selectedFile;
+ // Convert to relative path if within ProgramDirectory for portability
+ Settings.PluginSettings.NodeExecutablePath = Constant.ConvertToRelativePathIfPossible(selectedFile);
}
[RelayCommand]
From ab5a0b493ddda6facc6b8b21dfc8d95490615e3c Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Sat, 24 Jan 2026 15:55:49 +0800
Subject: [PATCH 05/18] Revert "Add UI descriptions for relative path support"
This reverts commit 864eedd255410baaa8bb3f405d010f9804ee766d.
---
.../SettingPages/Views/SettingsPaneGeneral.xaml | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)
diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml b/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml
index 8900a16ce..b4c94cb35 100644
--- a/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml
+++ b/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml
@@ -476,10 +476,7 @@
Content="{Binding Settings.CustomBrowser.DisplayName}" />
-
+
-
+
Date: Sat, 24 Jan 2026 15:57:52 +0800
Subject: [PATCH 06/18] Save Python/Node paths as absolute, not relative
Removed logic that converted selected Python and Node executable
paths to relative if within the program directory. Now, the
selected file paths are stored as absolute paths without
conversion. This simplifies path handling and improves clarity.
---
.../SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs
index eb00ae3ea..aa78849ba 100644
--- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs
+++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs
@@ -377,8 +377,7 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
);
if (!string.IsNullOrEmpty(selectedFile))
- // Convert to relative path if within ProgramDirectory for portability
- Settings.PluginSettings.PythonExecutablePath = Constant.ConvertToRelativePathIfPossible(selectedFile);
+ Settings.PluginSettings.PythonExecutablePath = selectedFile;
}
[RelayCommand]
@@ -390,8 +389,7 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
);
if (!string.IsNullOrEmpty(selectedFile))
- // Convert to relative path if within ProgramDirectory for portability
- Settings.PluginSettings.NodeExecutablePath = Constant.ConvertToRelativePathIfPossible(selectedFile);
+ Settings.PluginSettings.NodeExecutablePath = selectedFile;
}
[RelayCommand]
From 9070ff4c7bcce6f1daf2b413b8351324727b0ffd Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Sat, 24 Jan 2026 15:58:37 +0800
Subject: [PATCH 07/18] Remove PathResolutionTest.cs and related path tests
Removed the PathResolutionTest.cs file, which contained unit tests for the Constant class's path resolution methods. These tests covered absolute, relative, UNC, null, and empty path scenarios, as well as round-trip conversions.
---
Flow.Launcher.Test/PathResolutionTest.cs | 188 -----------------------
1 file changed, 188 deletions(-)
delete mode 100644 Flow.Launcher.Test/PathResolutionTest.cs
diff --git a/Flow.Launcher.Test/PathResolutionTest.cs b/Flow.Launcher.Test/PathResolutionTest.cs
deleted file mode 100644
index 3888b41a4..000000000
--- a/Flow.Launcher.Test/PathResolutionTest.cs
+++ /dev/null
@@ -1,188 +0,0 @@
-using System;
-using System.IO;
-using Xunit;
-using Flow.Launcher.Infrastructure;
-
-namespace Flow.Launcher.Test
-{
- public class PathResolutionTest
- {
- [Fact]
- public void ResolveAbsolutePath_WithAbsolutePath_ReturnsOriginalPath()
- {
- // Arrange
- var absolutePath = @"C:\Program Files\Python\python.exe";
-
- // Act
- var result = Constant.ResolveAbsolutePath(absolutePath);
-
- // Assert
- Assert.Equal(absolutePath, result);
- }
-
- [Fact]
- public void ResolveAbsolutePath_WithRelativePath_ResolvesToProgramDirectory()
- {
- // Arrange
- var relativePath = @".\runtimes\python\pythonw.exe";
-
- // Act
- var result = Constant.ResolveAbsolutePath(relativePath);
-
- // Assert
- Assert.True(Path.IsPathRooted(result), "Result should be an absolute path");
- Assert.Contains(Constant.ProgramDirectory, result);
- Assert.EndsWith(@"runtimes\python\pythonw.exe", result);
- }
-
- [Fact]
- public void ResolveAbsolutePath_WithDotDotPath_ResolvesCorrectly()
- {
- // Arrange
- var relativePath = @"..\runtimes\node\node.exe";
-
- // Act
- var result = Constant.ResolveAbsolutePath(relativePath);
-
- // Assert
- Assert.True(Path.IsPathRooted(result), "Result should be an absolute path");
- }
-
- [Fact]
- public void ResolveAbsolutePath_WithNullPath_ReturnsNull()
- {
- // Arrange
- string nullPath = null;
-
- // Act
- var result = Constant.ResolveAbsolutePath(nullPath);
-
- // Assert
- Assert.Null(result);
- }
-
- [Fact]
- public void ResolveAbsolutePath_WithEmptyPath_ReturnsEmpty()
- {
- // Arrange
- var emptyPath = string.Empty;
-
- // Act
- var result = Constant.ResolveAbsolutePath(emptyPath);
-
- // Assert
- Assert.Equal(string.Empty, result);
- }
-
- [Fact]
- public void ResolveAbsolutePath_WithForwardSlashes_ResolvesCorrectly()
- {
- // Arrange
- var relativePath = @"./runtimes/python/pythonw.exe";
-
- // Act
- var result = Constant.ResolveAbsolutePath(relativePath);
-
- // Assert
- Assert.True(Path.IsPathRooted(result), "Result should be an absolute path");
- Assert.Contains(Constant.ProgramDirectory, result);
- }
-
- [Fact]
- public void ResolveAbsolutePath_WithUNCPath_ReturnsOriginalPath()
- {
- // Arrange
- var uncPath = @"\\server\share\python\pythonw.exe";
-
- // Act
- var result = Constant.ResolveAbsolutePath(uncPath);
-
- // Assert
- Assert.Equal(uncPath, result);
- }
-
- [Fact]
- public void ConvertToRelativePathIfPossible_WithPathInProgramDirectory_ReturnsRelativePath()
- {
- // Arrange
- var absolutePath = Path.Combine(Constant.ProgramDirectory, "runtimes", "python", "pythonw.exe");
-
- // Act
- var result = Constant.ConvertToRelativePathIfPossible(absolutePath);
-
- // Assert
- Assert.True(result.StartsWith(".\\"), "Result should start with .\\");
- Assert.Contains("runtimes", result);
- Assert.Contains("python", result);
- }
-
- [Fact]
- public void ConvertToRelativePathIfPossible_WithPathOutsideProgramDirectory_ReturnsAbsolutePath()
- {
- // Arrange
- var absolutePath = @"C:\Python\python.exe";
-
- // Act
- var result = Constant.ConvertToRelativePathIfPossible(absolutePath);
-
- // Assert
- Assert.Equal(absolutePath, result);
- }
-
- [Fact]
- public void ConvertToRelativePathIfPossible_WithRelativePath_ReturnsOriginalPath()
- {
- // Arrange
- var relativePath = @".\runtimes\python\pythonw.exe";
-
- // Act
- var result = Constant.ConvertToRelativePathIfPossible(relativePath);
-
- // Assert
- Assert.Equal(relativePath, result);
- }
-
- [Fact]
- public void ConvertToRelativePathIfPossible_WithNullPath_ReturnsNull()
- {
- // Arrange
- string nullPath = null;
-
- // Act
- var result = Constant.ConvertToRelativePathIfPossible(nullPath);
-
- // Assert
- Assert.Null(result);
- }
-
- [Fact]
- public void ConvertToRelativePathIfPossible_WithEmptyPath_ReturnsEmpty()
- {
- // Arrange
- var emptyPath = string.Empty;
-
- // Act
- var result = Constant.ConvertToRelativePathIfPossible(emptyPath);
-
- // Assert
- Assert.Equal(string.Empty, result);
- }
-
- [Fact]
- public void RoundTripTest_RelativePathResolutionAndConversion()
- {
- // Arrange
- var originalRelative = @".\runtimes\python\pythonw.exe";
-
- // Act - Resolve to absolute
- var absolute = Constant.ResolveAbsolutePath(originalRelative);
- // Convert back to relative
- var backToRelative = Constant.ConvertToRelativePathIfPossible(absolute);
-
- // Assert
- Assert.True(Path.IsPathRooted(absolute), "Resolved path should be absolute");
- Assert.True(backToRelative.StartsWith(".\\"), "Converted path should be relative");
- Assert.Contains("runtimes\\python\\pythonw.exe", backToRelative);
- }
- }
-}
From d16e43de8a0717429085015008542e53f7e4307d Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Sat, 24 Jan 2026 16:03:26 +0800
Subject: [PATCH 08/18] Refactor path utilities to DataLocation from Constant
Move ResolveAbsolutePath and ConvertToRelativePathIfPossible from Constant to DataLocation for better organization. Update all references accordingly; implementations remain unchanged. This improves code clarity around file path management.
---
.../Environments/AbstractPluginEnvironment.cs | 6 +-
Flow.Launcher.Infrastructure/Constant.cs | 61 -------------------
.../UserSettings/DataLocation.cs | 61 +++++++++++++++++++
3 files changed, 64 insertions(+), 64 deletions(-)
diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs
index 3a9732aa3..e08a5815e 100644
--- a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs
+++ b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs
@@ -45,7 +45,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
/// Resolves the configured plugin settings file path to an absolute path.
/// Supports both absolute paths and relative paths (relative to ProgramDirectory).
///
- private string ResolvedPluginsSettingsFilePath => Constant.ResolveAbsolutePath(PluginsSettingsFilePath);
+ private string ResolvedPluginsSettingsFilePath => DataLocation.ResolveAbsolutePath(PluginsSettingsFilePath);
internal IEnumerable Setup()
{
@@ -75,7 +75,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
if (!string.IsNullOrEmpty(selectedFile))
{
// Convert to relative path if within ProgramDirectory for portability
- PluginsSettingsFilePath = Constant.ConvertToRelativePathIfPossible(selectedFile);
+ PluginsSettingsFilePath = DataLocation.ConvertToRelativePathIfPossible(selectedFile);
}
// Nothing selected because user pressed cancel from the file dialog window
else
@@ -100,7 +100,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
if (!string.IsNullOrEmpty(selectedFile))
{
// Convert to relative path if within ProgramDirectory for portability
- PluginsSettingsFilePath = Constant.ConvertToRelativePathIfPossible(selectedFile);
+ PluginsSettingsFilePath = DataLocation.ConvertToRelativePathIfPossible(selectedFile);
}
else
{
diff --git a/Flow.Launcher.Infrastructure/Constant.cs b/Flow.Launcher.Infrastructure/Constant.cs
index fd1538646..13da9f79f 100644
--- a/Flow.Launcher.Infrastructure/Constant.cs
+++ b/Flow.Launcher.Infrastructure/Constant.cs
@@ -56,66 +56,5 @@ namespace Flow.Launcher.Infrastructure
public const string Docs = "https://flowlauncher.com/docs";
public const string SystemLanguageCode = "system";
-
- ///
- /// Resolves a path that may be relative to an absolute path.
- /// If the path is already absolute, returns it as-is.
- /// If the path is relative (starts with . or doesn't contain a drive), resolves it relative to ProgramDirectory.
- ///
- /// The path to resolve
- /// An absolute path
- public static string ResolveAbsolutePath(string path)
- {
- if (string.IsNullOrEmpty(path))
- return path;
-
- // If already absolute, return as-is
- if (Path.IsPathRooted(path))
- return path;
-
- // Resolve relative to ProgramDirectory
- return Path.GetFullPath(Path.Combine(ProgramDirectory, path));
- }
-
- ///
- /// Converts an absolute path to a relative path if it's within ProgramDirectory.
- /// This enables portability by storing paths relative to the program directory when possible.
- ///
- /// The absolute path to convert
- /// A relative path if the path is within ProgramDirectory, otherwise the original absolute path
- public static string ConvertToRelativePathIfPossible(string absolutePath)
- {
- if (string.IsNullOrEmpty(absolutePath))
- return absolutePath;
-
- if (!Path.IsPathRooted(absolutePath))
- return absolutePath;
-
- try
- {
- // Get the full absolute paths for comparison
- var fullAbsolutePath = Path.GetFullPath(absolutePath);
- var fullProgramDir = Path.GetFullPath(ProgramDirectory);
-
- // Check if the absolute path is within ProgramDirectory
- if (fullAbsolutePath.StartsWith(fullProgramDir, StringComparison.OrdinalIgnoreCase))
- {
- // Convert to relative path
- var relativePath = Path.GetRelativePath(fullProgramDir, fullAbsolutePath);
-
- // Prefix with .\ for clarity
- if (!relativePath.StartsWith("."))
- relativePath = ".\\" + relativePath;
-
- return relativePath;
- }
- }
- catch
- {
- // If conversion fails, return the original path
- }
-
- return absolutePath;
- }
}
}
diff --git a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs
index 82f83b11a..4588a9e6a 100644
--- a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs
+++ b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs
@@ -42,5 +42,66 @@ namespace Flow.Launcher.Infrastructure.UserSettings
public const string PluginEnvironments = "Environments";
public const string PluginDeleteFile = "NeedDelete.txt";
public static readonly string PluginEnvironmentsPath = Path.Combine(DataDirectory(), PluginEnvironments);
+
+ ///
+ /// Resolves a path that may be relative to an absolute path.
+ /// If the path is already absolute, returns it as-is.
+ /// If the path is relative (starts with . or doesn't contain a drive), resolves it relative to ProgramDirectory.
+ ///
+ /// The path to resolve
+ /// An absolute path
+ public static string ResolveAbsolutePath(string path)
+ {
+ if (string.IsNullOrEmpty(path))
+ return path;
+
+ // If already absolute, return as-is
+ if (Path.IsPathRooted(path))
+ return path;
+
+ // Resolve relative to ProgramDirectory
+ return Path.GetFullPath(Path.Combine(Constant.ProgramDirectory, path));
+ }
+
+ ///
+ /// Converts an absolute path to a relative path if it's within ProgramDirectory.
+ /// This enables portability by storing paths relative to the program directory when possible.
+ ///
+ /// The absolute path to convert
+ /// A relative path if the path is within ProgramDirectory, otherwise the original absolute path
+ public static string ConvertToRelativePathIfPossible(string absolutePath)
+ {
+ if (string.IsNullOrEmpty(absolutePath))
+ return absolutePath;
+
+ if (!Path.IsPathRooted(absolutePath))
+ return absolutePath;
+
+ try
+ {
+ // Get the full absolute paths for comparison
+ var fullAbsolutePath = Path.GetFullPath(absolutePath);
+ var fullProgramDir = Path.GetFullPath(Constant.ProgramDirectory);
+
+ // Check if the absolute path is within ProgramDirectory
+ if (fullAbsolutePath.StartsWith(fullProgramDir, StringComparison.OrdinalIgnoreCase))
+ {
+ // Convert to relative path
+ var relativePath = Path.GetRelativePath(fullProgramDir, fullAbsolutePath);
+
+ // Prefix with .\ for clarity
+ if (!relativePath.StartsWith('.'))
+ relativePath = ".\\" + relativePath;
+
+ return relativePath;
+ }
+ }
+ catch
+ {
+ // If conversion fails, return the original path
+ }
+
+ return absolutePath;
+ }
}
}
From d49e5b4c1f2078412e747d6e93f4d57fab8aa860 Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Sat, 24 Jan 2026 16:04:32 +0800
Subject: [PATCH 09/18] Remove unused Flow.Launcher.Infrastructure using
directive
Eliminated the unnecessary using statement for Flow.Launcher.Infrastructure in AbstractPluginEnvironment.cs, as its types or members are no longer referenced in this file. This helps clean up the code and avoid redundant dependencies.
---
.../ExternalPlugins/Environments/AbstractPluginEnvironment.cs | 1 -
1 file changed, 1 deletion(-)
diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs
index e08a5815e..dead5f293 100644
--- a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs
+++ b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs
@@ -4,7 +4,6 @@ using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Forms;
-using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.SharedCommands;
From 9fa4c37109276b3c745f4932ac3d515da4667435 Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Sat, 24 Jan 2026 16:07:04 +0800
Subject: [PATCH 10/18] Remove relative path conversion for plugin settings
files
Removed logic that converted absolute paths to relative paths within the ProgramDirectory. Now, plugin settings file paths are always stored as absolute paths. Deleted the ConvertToRelativePathIfPossible method and updated usages accordingly.
---
.../Environments/AbstractPluginEnvironment.cs | 6 +--
.../UserSettings/DataLocation.cs | 41 -------------------
2 files changed, 2 insertions(+), 45 deletions(-)
diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs
index dead5f293..37a1449c1 100644
--- a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs
+++ b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs
@@ -73,8 +73,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
if (!string.IsNullOrEmpty(selectedFile))
{
- // Convert to relative path if within ProgramDirectory for portability
- PluginsSettingsFilePath = DataLocation.ConvertToRelativePathIfPossible(selectedFile);
+ PluginsSettingsFilePath = selectedFile;
}
// Nothing selected because user pressed cancel from the file dialog window
else
@@ -98,8 +97,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
if (!string.IsNullOrEmpty(selectedFile))
{
- // Convert to relative path if within ProgramDirectory for portability
- PluginsSettingsFilePath = DataLocation.ConvertToRelativePathIfPossible(selectedFile);
+ PluginsSettingsFilePath = selectedFile;
}
else
{
diff --git a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs
index 4588a9e6a..2e23734d7 100644
--- a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs
+++ b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs
@@ -62,46 +62,5 @@ namespace Flow.Launcher.Infrastructure.UserSettings
// Resolve relative to ProgramDirectory
return Path.GetFullPath(Path.Combine(Constant.ProgramDirectory, path));
}
-
- ///
- /// Converts an absolute path to a relative path if it's within ProgramDirectory.
- /// This enables portability by storing paths relative to the program directory when possible.
- ///
- /// The absolute path to convert
- /// A relative path if the path is within ProgramDirectory, otherwise the original absolute path
- public static string ConvertToRelativePathIfPossible(string absolutePath)
- {
- if (string.IsNullOrEmpty(absolutePath))
- return absolutePath;
-
- if (!Path.IsPathRooted(absolutePath))
- return absolutePath;
-
- try
- {
- // Get the full absolute paths for comparison
- var fullAbsolutePath = Path.GetFullPath(absolutePath);
- var fullProgramDir = Path.GetFullPath(Constant.ProgramDirectory);
-
- // Check if the absolute path is within ProgramDirectory
- if (fullAbsolutePath.StartsWith(fullProgramDir, StringComparison.OrdinalIgnoreCase))
- {
- // Convert to relative path
- var relativePath = Path.GetRelativePath(fullProgramDir, fullAbsolutePath);
-
- // Prefix with .\ for clarity
- if (!relativePath.StartsWith('.'))
- relativePath = ".\\" + relativePath;
-
- return relativePath;
- }
- }
- catch
- {
- // If conversion fails, return the original path
- }
-
- return absolutePath;
- }
}
}
From 001101bb813f96658630b88a00e247b3e510d11f Mon Sep 17 00:00:00 2001
From: Jack Ye
Date: Sat, 21 Feb 2026 20:11:49 +0800
Subject: [PATCH 11/18] Improve error handling
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
.../UserSettings/DataLocation.cs | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs
index 2e23734d7..7b610d31a 100644
--- a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs
+++ b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs
@@ -59,8 +59,19 @@ namespace Flow.Launcher.Infrastructure.UserSettings
if (Path.IsPathRooted(path))
return path;
- // Resolve relative to ProgramDirectory
- return Path.GetFullPath(Path.Combine(Constant.ProgramDirectory, path));
+ // Resolve relative to ProgramDirectory, handling invalid path formats gracefully
+ try
+ {
+ return Path.GetFullPath(Path.Combine(Constant.ProgramDirectory, path));
+ }
+ catch (Exception ex) when (ex is ArgumentException ||
+ ex is NotSupportedException ||
+ ex is PathTooLongException)
+ {
+ // If the path cannot be resolved (invalid characters, format, or too long),
+ // return the original path to avoid crashing the application.
+ return path;
+ }
}
}
}
From 34a984cd86ee28313c176c79cc2c62d0e0d5a69f Mon Sep 17 00:00:00 2001
From: Jack Ye
Date: Sat, 21 Feb 2026 20:12:49 +0800
Subject: [PATCH 12/18] Improve code comments
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs
index 7b610d31a..9764a7ab2 100644
--- a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs
+++ b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs
@@ -46,7 +46,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
///
/// Resolves a path that may be relative to an absolute path.
/// If the path is already absolute, returns it as-is.
- /// If the path is relative (starts with . or doesn't contain a drive), resolves it relative to ProgramDirectory.
+ /// If the path is not rooted (as determined by ), resolves it relative to ProgramDirectory.
///
/// The path to resolve
/// An absolute path
From 9b0a50376c5a9804f52206d593e5510df88a3659 Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Sat, 21 Feb 2026 20:18:09 +0800
Subject: [PATCH 13/18] Clarify exception type in path resolution catch block
Updated the catch block in DataLocation.cs to explicitly use System.Exception instead of Exception when handling path resolution errors. This improves code clarity while maintaining the same error handling logic for ArgumentException, NotSupportedException, and PathTooLongException.
---
Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs
index 9764a7ab2..802f09492 100644
--- a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs
+++ b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs
@@ -64,7 +64,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
{
return Path.GetFullPath(Path.Combine(Constant.ProgramDirectory, path));
}
- catch (Exception ex) when (ex is ArgumentException ||
+ catch (System.Exception ex) when (ex is ArgumentException ||
ex is NotSupportedException ||
ex is PathTooLongException)
{
From d62fd05599e43ddce07264faff4812ab6b8ee2a1 Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Sat, 21 Feb 2026 20:22:04 +0800
Subject: [PATCH 14/18] Ensure the path is updated in settings in case user has
moved Flow to a different location
---
.../ExternalPlugins/Environments/AbstractPluginEnvironment.cs | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs
index 37a1449c1..6f190bf10 100644
--- a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs
+++ b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs
@@ -61,6 +61,8 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
if (resolvedPath.StartsWith(EnvPath, StringComparison.OrdinalIgnoreCase))
EnsureLatestInstalled(ExecutablePath, resolvedPath, EnvPath);
+ // Ensure the path is updated in settings in case user has moved Flow to a different location
+ resolvedPath = ResolvedPluginsSettingsFilePath;
return SetPathForPluginPairs(resolvedPath, Language);
}
From 04a56b556b4748861d41695e229a12913aef76d5 Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Sat, 21 Feb 2026 20:25:10 +0800
Subject: [PATCH 15/18] Use Path.IsPathFullyQualified for absolute path check
Replaced Path.IsPathRooted with Path.IsPathFullyQualified to more accurately determine if a path is truly absolute, preventing misclassification of certain relative paths.
---
Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs
index 802f09492..4c3a7b417 100644
--- a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs
+++ b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs
@@ -56,7 +56,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
return path;
// If already absolute, return as-is
- if (Path.IsPathRooted(path))
+ if (Path.IsPathFullyQualified(path))
return path;
// Resolve relative to ProgramDirectory, handling invalid path formats gracefully
From 57fde0a2f31157e6b967d3b6ee1f12c658f19e4f Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Sat, 21 Feb 2026 20:30:54 +0800
Subject: [PATCH 16/18] Improve code comments
---
.../ExternalPlugins/Environments/AbstractPluginEnvironment.cs | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs
index 6f190bf10..07e4c8dcb 100644
--- a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs
+++ b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs
@@ -61,7 +61,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
if (resolvedPath.StartsWith(EnvPath, StringComparison.OrdinalIgnoreCase))
EnsureLatestInstalled(ExecutablePath, resolvedPath, EnvPath);
- // Ensure the path is updated in settings in case user has moved Flow to a different location
+ // Ensure the path is updated in settings in case environment was updated
resolvedPath = ResolvedPluginsSettingsFilePath;
return SetPathForPluginPairs(resolvedPath, Language);
}
@@ -112,6 +112,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
InstallEnvironment();
}
+ // Ensure the path is updated when user has chosen to install or select environment executable
resolvedPath = ResolvedPluginsSettingsFilePath;
if (FilesFolders.FileExists(resolvedPath))
{
From bfed5168f07bb9617f8dfbcfb193b6d398b1c671 Mon Sep 17 00:00:00 2001
From: Jack Ye
Date: Sat, 21 Feb 2026 20:32:41 +0800
Subject: [PATCH 17/18] Improve code comments
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
.../ExternalPlugins/Environments/AbstractPluginEnvironment.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs
index 07e4c8dcb..2c5f5502f 100644
--- a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs
+++ b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs
@@ -41,7 +41,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
}
///
- /// Resolves the configured plugin settings file path to an absolute path.
+ /// Resolves the configured runtime executable path to an absolute path.
/// Supports both absolute paths and relative paths (relative to ProgramDirectory).
///
private string ResolvedPluginsSettingsFilePath => DataLocation.ResolveAbsolutePath(PluginsSettingsFilePath);
From f756b71207f49df654808606d5721ea74d98c17f Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Sat, 21 Feb 2026 20:34:57 +0800
Subject: [PATCH 18/18] Catch all exceptions
---
Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs
index 4c3a7b417..94dda11df 100644
--- a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs
+++ b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs
@@ -64,9 +64,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
{
return Path.GetFullPath(Path.Combine(Constant.ProgramDirectory, path));
}
- catch (System.Exception ex) when (ex is ArgumentException ||
- ex is NotSupportedException ||
- ex is PathTooLongException)
+ catch (System.Exception)
{
// If the path cannot be resolved (invalid characters, format, or too long),
// return the original path to avoid crashing the application.