Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
438bb8a
Initial plan
Copilot Jan 24, 2026
d61ec18
Add relative path resolution support for Python and Node.js executables
Copilot Jan 24, 2026
864eedd
Add UI descriptions for relative path support
Copilot Jan 24, 2026
3de9e53
Address code review feedback: convert absolute to relative paths for …
Copilot Jan 24, 2026
ab5a0b4
Revert "Add UI descriptions for relative path support"
Jack251970 Jan 24, 2026
c59249d
Save Python/Node paths as absolute, not relative
Jack251970 Jan 24, 2026
9070ff4
Remove PathResolutionTest.cs and related path tests
Jack251970 Jan 24, 2026
d16e43d
Refactor path utilities to DataLocation from Constant
Jack251970 Jan 24, 2026
d49e5b4
Remove unused Flow.Launcher.Infrastructure using directive
Jack251970 Jan 24, 2026
9fa4c37
Remove relative path conversion for plugin settings files
Jack251970 Jan 24, 2026
001101b
Improve error handling
Jack251970 Feb 21, 2026
34a984c
Improve code comments
Jack251970 Feb 21, 2026
b923c40
Merge branch 'dev' into copilot/support-relative-paths
Jack251970 Feb 21, 2026
9b0a503
Clarify exception type in path resolution catch block
Jack251970 Feb 21, 2026
d62fd05
Ensure the path is updated in settings in case user has moved Flow to…
Jack251970 Feb 21, 2026
04a56b5
Use Path.IsPathFullyQualified for absolute path check
Jack251970 Feb 21, 2026
57fde0a
Improve code comments
Jack251970 Feb 21, 2026
bfed516
Improve code comments
Jack251970 Feb 21, 2026
f756b71
Catch all exceptions
Jack251970 Feb 21, 2026
8e6d718
Merge branch 'dev' into copilot/support-relative-paths
Jack251970 Feb 26, 2026
c9c306f
Merge branch 'dev' into copilot/support-relative-paths
Jack251970 Apr 6, 2026
8b6f9f4
Move function to FilesFolders
Jack251970 Apr 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ internal AbstractPluginEnvironment(List<PluginMetadata> pluginMetadataList, Plug
PluginSettings = pluginSettings;
}

/// <summary>
/// Resolves the configured runtime executable path to an absolute path.
/// Supports both absolute paths and relative paths (relative to ProgramDirectory).
/// </summary>
private string ResolvedPluginsSettingsFilePath => FilesFolders.ResolveAbsolutePath(PluginsSettingsFilePath);

internal IEnumerable<PluginPair> Setup()
{
// If no plugin is using the language, return empty list
Expand All @@ -48,13 +54,16 @@ internal IEnumerable<PluginPair> Setup()
return new List<PluginPair>();
}

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);
Expand Down Expand Up @@ -103,9 +112,11 @@ internal IEnumerable<PluginPair> 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
{
Expand Down
31 changes: 31 additions & 0 deletions Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -17,7 +19,7 @@

/// <summary>
/// Copies the folder and all of its files and folders
/// including subfolders to the target location

Check warning on line 22 in Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`subfolders` is not a recognized word. (unrecognized-spelling)
/// </summary>
/// <param name="sourcePath"></param>
/// <param name="targetPath"></param>
Expand Down Expand Up @@ -48,14 +50,14 @@
foreach (FileInfo file in files)
{
string temppath = Path.Combine(targetPath, file.Name);
file.CopyTo(temppath, false);

Check warning on line 53 in Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`temppath` is not a recognized word. (unrecognized-spelling)
}

// Recursively copy subdirectories by calling itself on each subdirectory until there are no more to copy
foreach (DirectoryInfo subdir in dirs)
{
string temppath = Path.Combine(targetPath, subdir.Name);

Check warning on line 59 in Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`temppath` is not a recognized word. (unrecognized-spelling)
CopyAll(subdir.FullName, temppath, messageBoxExShow);

Check warning on line 60 in Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`temppath` is not a recognized word. (unrecognized-spelling)
}
}
catch (Exception)
Expand Down Expand Up @@ -487,5 +489,34 @@
}
}
}

/// <summary>
/// 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 <see cref="Path.IsPathRooted(string)"/>), resolves it relative to ProgramDirectory.
/// </summary>
/// <param name="path">The path to resolve</param>
/// <returns>An absolute path</returns>
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;
}
}
}
}
Loading