diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs index 1a324a9930a..91f3c09c4ee 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs @@ -40,6 +40,12 @@ internal AbstractPluginEnvironment(List pluginMetadataList, Plug 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 => FilesFolders.ResolveAbsolutePath(PluginsSettingsFilePath); + internal IEnumerable Setup() { // If no plugin is using the language, return empty list @@ -48,13 +54,16 @@ internal IEnumerable Setup() 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 @@ internal IEnumerable Setup() 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.Plugin/SharedCommands/FilesFolders.cs b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs index cd1ddf983a0..4075b89ace4 100644 --- a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs +++ b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs @@ -2,6 +2,8 @@ using System.Diagnostics; using System.IO; using System.Linq; +using System.Reflection; + #pragma warning disable IDE0005 using System.Windows; #pragma warning restore IDE0005 @@ -487,5 +489,34 @@ public static void ValidateDataDirectory(string bundledDataDirectory, string dat } } } + + /// + /// 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(Directory.GetParent(Assembly.GetExecutingAssembly().Location).ToString(), path)); + } + catch (Exception) + { + // If the path cannot be resolved (invalid characters, format, or too long), + // return the original path to avoid crashing the application. + return path; + } + } } }