diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs
index 1a324a993..2c5f5502f 100644
--- a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs
+++ b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs
@@ -40,6 +40,12 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
PluginSettings = pluginSettings;
}
+ ///
+ /// 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);
+
internal IEnumerable Setup()
{
// If no plugin is using the language, return empty list
@@ -48,13 +54,16 @@ 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);
+ // Ensure the path is updated in settings in case environment was updated
+ resolvedPath = ResolvedPluginsSettingsFilePath;
+ return SetPathForPluginPairs(resolvedPath, Language);
}
var noRuntimeMessage = Localize.runtimePluginInstalledChooseRuntimePrompt(Language, EnvName, Environment.NewLine);
@@ -103,9 +112,11 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
InstallEnvironment();
}
- if (FilesFolders.FileExists(PluginsSettingsFilePath))
+ // Ensure the path is updated when user has chosen to install or select environment executable
+ resolvedPath = ResolvedPluginsSettingsFilePath;
+ if (FilesFolders.FileExists(resolvedPath))
{
- return SetPathForPluginPairs(PluginsSettingsFilePath, Language);
+ return SetPathForPluginPairs(resolvedPath, Language);
}
else
{
diff --git a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs
index 82f83b11a..94dda11df 100644
--- a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs
+++ b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs
@@ -42,5 +42,34 @@ 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 not rooted (as determined by ), 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.IsPathFullyQualified(path))
+ return path;
+
+ // Resolve relative to ProgramDirectory, handling invalid path formats gracefully
+ try
+ {
+ return Path.GetFullPath(Path.Combine(Constant.ProgramDirectory, path));
+ }
+ catch (System.Exception)
+ {
+ // If the path cannot be resolved (invalid characters, format, or too long),
+ // return the original path to avoid crashing the application.
+ return path;
+ }
+ }
}
}