diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs index 1a324a9930a..2c5f5502fa5 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 => DataLocation.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.Infrastructure/UserSettings/DataLocation.cs b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs index 82f83b11ac2..94dda11df75 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs @@ -42,5 +42,34 @@ public static bool PortableDataLocationInUse() 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; + } + } } }