Merge branch 'dev' into fix_win32_loading

This commit is contained in:
Jeremy Wu 2021-02-15 06:51:58 +11:00
commit f78ba8e79e
27 changed files with 294 additions and 108 deletions

View file

@ -53,6 +53,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Droplex" Version="1.2.0" />
<PackageReference Include="FSharp.Core" Version="4.7.1" />
<PackageReference Include="squirrel.windows" Version="1.5.2" />
</ItemGroup>

View file

@ -3,13 +3,14 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Threading.Tasks;
using System.Windows.Forms;
using Droplex;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.SharedCommands;
namespace Flow.Launcher.Core.Plugin
{
@ -22,7 +23,7 @@ namespace Flow.Launcher.Core.Plugin
public static List<PluginPair> Plugins(List<PluginMetadata> metadatas, PluginsSettings settings)
{
var dotnetPlugins = DotNetPlugins(metadatas);
var pythonPlugins = PythonPlugins(metadatas, settings.PythonDirectory);
var pythonPlugins = PythonPlugins(metadatas, settings);
var executablePlugins = ExecutablePlugins(metadatas);
var plugins = dotnetPlugins.Concat(pythonPlugins).Concat(executablePlugins).ToList();
return plugins;
@ -113,11 +114,14 @@ namespace Flow.Launcher.Core.Plugin
return plugins;
}
public static IEnumerable<PluginPair> PythonPlugins(List<PluginMetadata> source, string pythonDirectory)
public static IEnumerable<PluginPair> PythonPlugins(List<PluginMetadata> source, PluginsSettings settings)
{
// try to set Constant.PythonPath, either from
if (!source.Any(o => o.Language.ToUpper() == AllowedLanguage.Python))
return new List<PluginPair>();
// Try setting Constant.PythonPath first, either from
// PATH or from the given pythonDirectory
if (string.IsNullOrEmpty(pythonDirectory))
if (string.IsNullOrEmpty(settings.PythonDirectory))
{
var paths = Environment.GetEnvironmentVariable(PATH);
if (paths != null)
@ -129,47 +133,103 @@ namespace Flow.Launcher.Core.Plugin
if (pythonInPath)
{
Constant.PythonPath = PythonExecutable;
Constant.PythonPath =
Path.Combine(paths.Split(';').Where(p => p.ToLower().Contains(Python)).FirstOrDefault(), PythonExecutable);
settings.PythonDirectory = FilesFolders.GetPreviousExistingDirectory(FilesFolders.LocationExists, Constant.PythonPath);
}
else
{
Log.Error("|PluginsLoader.PythonPlugins|Python can't be found in PATH.");
Log.Error("PluginsLoader","Failed to set Python path despite the environment variable PATH is found", "PythonPlugins");
}
}
else
{
Log.Error("|PluginsLoader.PythonPlugins|PATH environment variable is not set.");
}
}
else
{
var path = Path.Combine(pythonDirectory, PythonExecutable);
var path = Path.Combine(settings.PythonDirectory, PythonExecutable);
if (File.Exists(path))
{
Constant.PythonPath = path;
}
else
{
Log.Error($"|PluginsLoader.PythonPlugins|Can't find python executable in {path}");
Log.Error("PluginsLoader",$"Tried to automatically set from Settings.PythonDirectory " +
$"but can't find python executable in {path}", "PythonPlugins");
}
}
// if we have a path to the python executable,
// load every python plugin pair.
if (String.IsNullOrEmpty(Constant.PythonPath))
if (string.IsNullOrEmpty(settings.PythonDirectory))
{
if (MessageBox.Show("Flow detected you have installed Python plugins, " +
"would you like to install Python to run them? " +
Environment.NewLine + Environment.NewLine +
"Click no if it's already installed, " +
"and you will be prompted to select the folder that contains the Python executable",
string.Empty, MessageBoxButtons.YesNo) == DialogResult.No
&& string.IsNullOrEmpty(settings.PythonDirectory))
{
var dlg = new FolderBrowserDialog
{
SelectedPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles)
};
var result = dlg.ShowDialog();
if (result == DialogResult.OK)
{
string pythonDirectory = dlg.SelectedPath;
if (!string.IsNullOrEmpty(pythonDirectory))
{
var pythonPath = Path.Combine(pythonDirectory, PythonExecutable);
if (File.Exists(pythonPath))
{
settings.PythonDirectory = pythonDirectory;
Constant.PythonPath = pythonPath;
}
else
{
MessageBox.Show("Can't find python in given directory");
}
}
}
}
else
{
DroplexPackage.Drop(App.python3_9_1).Wait();
var installedPythonDirectory =
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Programs\Python\Python39");
var pythonPath = Path.Combine(installedPythonDirectory, PythonExecutable);
if (FilesFolders.FileExists(pythonPath))
{
settings.PythonDirectory = installedPythonDirectory;
Constant.PythonPath = pythonPath;
}
else
{
Log.Error("PluginsLoader",
$"Failed to set Python path after Droplex install, {pythonPath} does not exist",
"PythonPlugins");
}
}
}
if (string.IsNullOrEmpty(settings.PythonDirectory))
{
MessageBox.Show("Unable to set Python executable path, please try from Flow's settings (scroll down to the bottom).");
Log.Error("PluginsLoader",
$"Not able to successfully set Python path, the PythonDirectory variable is still an empty string.",
"PythonPlugins");
return new List<PluginPair>();
}
else
{
return source
.Where(o => o.Language.ToUpper() == AllowedLanguage.Python)
.Select(metadata => new PluginPair
{
Plugin = new PythonPlugin(Constant.PythonPath),
Metadata = metadata
});
}
return source
.Where(o => o.Language.ToUpper() == AllowedLanguage.Python)
.Select(metadata => new PluginPair
{
Plugin = new PythonPlugin(Constant.PythonPath),
Metadata = metadata
})
.ToList();
}
public static IEnumerable<PluginPair> ExecutablePlugins(IEnumerable<PluginMetadata> source)

View file

@ -75,7 +75,7 @@ namespace Flow.Launcher.Infrastructure.Http
{
try
{
using var response = await client.GetAsync(url, token);
using var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token);
if (response.StatusCode == HttpStatusCode.OK)
{
await using var fileStream = new FileStream(filePath, FileMode.CreateNew);
@ -135,7 +135,7 @@ namespace Flow.Launcher.Infrastructure.Http
public static async Task<Stream> GetStreamAsync([NotNull] string url, CancellationToken token = default)
{
Log.Debug($"|Http.Get|Url <{url}>");
var response = await client.GetAsync(url, token);
var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token);
return await response.Content.ReadAsStreamAsync();
}
}

View file

@ -12,7 +12,7 @@ namespace Flow.Launcher.Infrastructure.Storage
public class JsonStrorage<T> where T : new()
{
private readonly JsonSerializerOptions _serializerSettings;
private T _data;
protected T _data;
// need a new directory name
public const string DirectoryName = "Settings";
public const string FileSuffix = ".json";

View file

@ -15,5 +15,10 @@ namespace Flow.Launcher.Infrastructure.Storage
FilePath = Path.Combine(DirectoryPath, $"{dataType.Name}{FileSuffix}");
}
public PluginJsonStorage(T data) : this()
{
_data = data;
}
}
}

View file

@ -3,6 +3,7 @@ using JetBrains.Annotations;
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
@ -88,17 +89,86 @@ namespace Flow.Launcher.Plugin
/// </summary>
event FlowLauncherGlobalKeyboardEventHandler GlobalKeyboardEvent;
/// <summary>
/// Fuzzy Search the string with the given query. This is the core search mechanism Flow uses
/// </summary>
/// <param name="query">Query string</param>
/// <param name="stringToCompare">The string that will be compared against the query</param>
/// <returns>Match results</returns>
MatchResult FuzzySearch(string query, string stringToCompare);
/// <summary>
/// Http download the spefic url and return as string
/// </summary>
/// <param name="url">URL to call Http Get</param>
/// <param name="token">Cancellation Token</param>
/// <returns>Task to get string result</returns>
Task<string> HttpGetStringAsync(string url, CancellationToken token = default);
/// <summary>
/// Http download the spefic url and return as stream
/// </summary>
/// <param name="url">URL to call Http Get</param>
/// <param name="token">Cancellation Token</param>
/// <returns>Task to get stream result</returns>
Task<Stream> HttpGetStreamAsync(string url, CancellationToken token = default);
Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath);
/// <summary>
/// Download the specific url to a cretain file path
/// </summary>
/// <param name="url">URL to download file</param>
/// <param name="token">place to store file</param>
/// <returns>Task showing the progress</returns>
Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath, CancellationToken token = default);
/// <summary>
/// Add ActionKeyword for specific plugin
/// </summary>
/// <param name="pluginId">ID for plugin that needs to add action keyword</param>
/// <param name="newActionKeyword">The actionkeyword that is supposed to be added</param>
void AddActionKeyword(string pluginId, string newActionKeyword);
/// <summary>
/// Remove ActionKeyword for specific plugin
/// </summary>
/// <param name="pluginId">ID for plugin that needs to remove action keyword</param>
/// <param name="newActionKeyword">The actionkeyword that is supposed to be removed</param>
void RemoveActionKeyword(string pluginId, string oldActionKeyword);
/// <summary>
/// Log debug message
/// Message will only be logged in Debug mode
/// </summary>
void LogDebug(string className, string message, [CallerMemberName] string methodName = "");
/// <summary>
/// Log info message
/// </summary>
void LogInfo(string className, string message, [CallerMemberName] string methodName = "");
/// <summary>
/// Log warning message
/// </summary>
void LogWarn(string className, string message, [CallerMemberName] string methodName = "");
/// <summary>
/// Log an Exception. Will throw if in debug mode so developer will be aware,
/// otherwise logs the eror message. This is the primary logging method used for Flow
/// </summary>
void LogException(string className, string message, Exception e, [CallerMemberName] string methodName = "");
/// <summary>
/// Load JsonStorage for current plugin. This is the method used to load settings from json in Flow
/// </summary>
/// <typeparam name="T">Type for deserialization</typeparam>
/// <returns></returns>
T LoadJsonStorage<T>() where T : new();
/// <summary>
/// Save JsonStorage for current plugin. This is the method used to save settings to json in Flow
/// </summary>
/// <typeparam name="T">Type for Serialization</typeparam>
/// <returns></returns>
void SaveJsonStorage<T>(T setting) where T : new();
}
}

View file

@ -1,4 +1,4 @@
namespace Flow.Launcher.Infrastructure.Storage
namespace Flow.Launcher.Plugin
{
/// <summary>
/// Save plugin settings/cache,

View file

@ -66,6 +66,9 @@
<Content Include="Images\*.svg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Images\*.ico">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

View file

@ -121,6 +121,7 @@
<system:String x:Key="newActionKeywordsCannotBeEmpty">New Action Keyword can't be empty</system:String>
<system:String x:Key="newActionKeywordsHasBeenAssigned">This new Action Keyword is already assigned to another plugin, please choose a different one</system:String>
<system:String x:Key="success">Success</system:String>
<system:String x:Key="completedSuccessfully">Completed successfully</system:String>
<system:String x:Key="actionkeyword_tips">Use * if you don't want to specify an action keyword</system:String>
<!--Custom Query Hotkey Dialog-->

View file

@ -34,6 +34,7 @@
<Window.InputBindings>
<KeyBinding Key="Escape" Command="{Binding EscCommand}"></KeyBinding>
<KeyBinding Key="F1" Command="{Binding StartHelpCommand}"></KeyBinding>
<KeyBinding Key="F5" Command="{Binding ReloadPluginDataCommand}"></KeyBinding>
<KeyBinding Key="Tab" Command="{Binding SelectNextItemCommand}"></KeyBinding>
<KeyBinding Key="Tab" Modifiers="Shift" Command="{Binding SelectPrevItemCommand}"></KeyBinding>
<KeyBinding Key="N" Modifiers="Ctrl" Command="{Binding SelectNextItemCommand}"></KeyBinding>

View file

@ -66,7 +66,7 @@ namespace Flow.Launcher
}
if (!File.Exists(iconPath))
{
imgIco.Source = ImageLoader.Load(Path.Combine(Infrastructure.Constant.ProgramDirectory, "Images\\app.png"));
imgIco.Source = ImageLoader.Load(Path.Combine(Constant.ProgramDirectory, "Images\\app.png"));
}
else {
imgIco.Source = ImageLoader.Load(iconPath);

View file

@ -18,6 +18,9 @@ using System.Threading;
using System.IO;
using Flow.Launcher.Infrastructure.Http;
using JetBrains.Annotations;
using System.Runtime.CompilerServices;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.Storage;
namespace Flow.Launcher
{
@ -61,18 +64,15 @@ namespace Flow.Launcher
// which will cause ungraceful exit
SaveAppAllSettings();
// Restart requires Squirrel's Update.exe to be present in the parent folder,
// it is only published from the project's release pipeline. When debugging without it,
// the project may not restart or just terminates. This is expected.
UpdateManager.RestartApp(Constant.ApplicationFileName);
}
public void RestarApp()
{
RestartApp();
}
public void RestarApp() => RestartApp();
public void CheckForNewUpdate()
{
_settingsVM.UpdateApp();
}
public void CheckForNewUpdate() => _settingsVM.UpdateApp();
public void SaveAppAllSettings()
{
@ -82,15 +82,9 @@ namespace Flow.Launcher
ImageLoader.Save();
}
public Task ReloadAllPluginData()
{
return PluginManager.ReloadData();
}
public Task ReloadAllPluginData() => PluginManager.ReloadData();
public void ShowMsg(string title, string subTitle = "", string iconPath = "")
{
ShowMsg(title, subTitle, iconPath, true);
}
public void ShowMsg(string title, string subTitle = "", string iconPath = "") => ShowMsg(title, subTitle, iconPath, true);
public void ShowMsg(string title, string subTitle, string iconPath, bool useMainWindowAsOwner = true)
{
@ -109,54 +103,40 @@ namespace Flow.Launcher
});
}
public void StartLoadingBar()
{
_mainVM.ProgressBarVisibility = Visibility.Visible;
}
public void StartLoadingBar() => _mainVM.ProgressBarVisibility = Visibility.Visible;
public void StopLoadingBar()
{
_mainVM.ProgressBarVisibility = Visibility.Collapsed;
}
public void StopLoadingBar() => _mainVM.ProgressBarVisibility = Visibility.Collapsed;
public string GetTranslation(string key)
{
return InternationalizationManager.Instance.GetTranslation(key);
}
public string GetTranslation(string key) => InternationalizationManager.Instance.GetTranslation(key);
public List<PluginPair> GetAllPlugins()
{
return PluginManager.AllPlugins.ToList();
}
public event FlowLauncherGlobalKeyboardEventHandler GlobalKeyboardEvent;
public List<PluginPair> GetAllPlugins() => PluginManager.AllPlugins.ToList();
public MatchResult FuzzySearch(string query, string stringToCompare) => StringMatcher.FuzzySearch(query, stringToCompare);
public Task<string> HttpGetStringAsync(string url, CancellationToken token = default)
{
return Http.GetAsync(url);
}
public Task<string> HttpGetStringAsync(string url, CancellationToken token = default) => Http.GetAsync(url);
public Task<Stream> HttpGetStreamAsync(string url, CancellationToken token = default)
{
return Http.GetStreamAsync(url);
}
public Task<Stream> HttpGetStreamAsync(string url, CancellationToken token = default) => Http.GetStreamAsync(url);
public Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath)
{
return Http.DownloadAsync(url, filePath);
}
public Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath, CancellationToken token = default) => Http.DownloadAsync(url, filePath, token);
public void AddActionKeyword(string pluginId, string newActionKeyword)
{
PluginManager.AddActionKeyword(pluginId, newActionKeyword);
}
public void AddActionKeyword(string pluginId, string newActionKeyword) => PluginManager.AddActionKeyword(pluginId, newActionKeyword);
public void RemoveActionKeyword(string pluginId, string oldActionKeyword) => PluginManager.RemoveActionKeyword(pluginId, oldActionKeyword);
public void LogDebug(string className, string message, [CallerMemberName] string methodName = "") => Log.Debug(className, message, methodName);
public void LogInfo(string className, string message, [CallerMemberName] string methodName = "") => Log.Info(className, message, methodName);
public void LogWarn(string className, string message, [CallerMemberName] string methodName = "") => Log.Warn(className, message, methodName);
public void LogException(string className, string message, Exception e, [CallerMemberName] string methodName = "") => Log.Exception(className, message, e, methodName);
public T LoadJsonStorage<T>() where T : new() => new PluginJsonStorage<T>().Load();
public void SaveJsonStorage<T>(T setting) where T : new() => new PluginJsonStorage<T>(setting).Save();
public event FlowLauncherGlobalKeyboardEventHandler GlobalKeyboardEvent;
public void RemoveActionKeyword(string pluginId, string oldActionKeyword)
{
PluginManager.RemoveActionKeyword(pluginId, oldActionKeyword);
}
#endregion
#region Private Methods

View file

@ -11,7 +11,7 @@
xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
x:Class="Flow.Launcher.SettingWindow"
mc:Ignorable="d"
Icon="Images\app.png"
Icon="Images\app.ico"
Title="{DynamicResource flowlauncher_settings}"
ResizeMode="NoResize"
WindowStartupLocation="CenterScreen"

View file

@ -89,7 +89,6 @@ namespace Flow.Launcher.ViewModel
_resultsViewUpdateTask =
Task.Run(updateAction).ContinueWith(continueAction, TaskContinuationOptions.OnlyOnFaulted);
async Task updateAction()
{
var queue = new Dictionary<string, ResultsForUpdate>();
@ -105,9 +104,7 @@ namespace Flow.Launcher.ViewModel
UpdateResultView(queue.Values);
}
}
;
};
void continueAction(Task t)
{
@ -115,8 +112,8 @@ namespace Flow.Launcher.ViewModel
throw t.Exception;
#else
Log.Error($"Error happen in task dealing with viewupdate for results. {t.Exception}");
_resultsViewUpdateTask =
Task.Run(updateAction).ContinueWith(continueAction, TaskContinuationOptions.OnlyOnFaulted);
_resultsViewUpdateTask =
Task.Run(updateAction).ContinueWith(continueAction, TaskContinuationOptions.OnlyOnFaulted);
#endif
}
}
@ -225,6 +222,25 @@ namespace Flow.Launcher.ViewModel
SelectedResults = Results;
}
});
ReloadPluginDataCommand = new RelayCommand(_ =>
{
var msg = new Msg { Owner = Application.Current.MainWindow };
MainWindowVisibility = Visibility.Collapsed;
PluginManager
.ReloadData()
.ContinueWith(_ =>
Application.Current.Dispatcher.Invoke(() =>
{
msg.Show(
InternationalizationManager.Instance.GetTranslation("success"),
InternationalizationManager.Instance.GetTranslation("completedSuccessfully"),
"");
}))
.ConfigureAwait(false);
});
}
#endregion
@ -313,6 +329,7 @@ namespace Flow.Launcher.ViewModel
public ICommand LoadContextMenuCommand { get; set; }
public ICommand LoadHistoryCommand { get; set; }
public ICommand OpenResultCommand { get; set; }
public ICommand ReloadPluginDataCommand { get; set; }
public string OpenResultCommandModifiers { get; private set; }

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View file

@ -12,4 +12,6 @@
<system:String x:Key="flowlauncher_plugin_browserbookmark_settings_newTab">New tab</system:String>
<system:String x:Key="flowlauncher_plugin_browserbookmark_settings_setBrowserFromPath">Set browser from path:</system:String>
<system:String x:Key="flowlauncher_plugin_browserbookmark_settings_choose">Choose</system:String>
<system:String x:Key="flowlauncher_plugin_browserbookmark_copyurl_title">Copy url</system:String>
<system:String x:Key="flowlauncher_plugin_browserbookmark_copyurl_subtitle">Copy the bookmark's url to clipboard</system:String>
</ResourceDictionary>

View file

@ -1,6 +1,9 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.Storage;
using Flow.Launcher.Plugin.BrowserBookmark.Commands;
using Flow.Launcher.Plugin.BrowserBookmark.Models;
@ -9,7 +12,7 @@ using Flow.Launcher.Plugin.SharedCommands;
namespace Flow.Launcher.Plugin.BrowserBookmark
{
public class Main : ISettingProvider, IPlugin, IReloadable, IPluginI18n, ISavable
public class Main : ISettingProvider, IPlugin, IReloadable, IPluginI18n, ISavable, IContextMenu
{
private PluginInitContext context;
@ -60,7 +63,8 @@ namespace Flow.Launcher.Plugin.BrowserBookmark
}
return true;
}
},
ContextData = new BookmarkAttributes { Url = c.Url }
}).Where(r => r.Score > 0);
return returnList.ToList();
}
@ -84,7 +88,8 @@ namespace Flow.Launcher.Plugin.BrowserBookmark
}
return true;
}
},
ContextData = new BookmarkAttributes { Url = c.Url }
}).ToList();
}
}
@ -115,5 +120,39 @@ namespace Flow.Launcher.Plugin.BrowserBookmark
{
_storage.Save();
}
public List<Result> LoadContextMenus(Result selectedResult)
{
return new List<Result>() {
new Result
{
Title = context.API.GetTranslation("flowlauncher_plugin_browserbookmark_copyurl_title"),
SubTitle = context.API.GetTranslation("flowlauncher_plugin_browserbookmark_copyurl_subtitle"),
Action = _ =>
{
try
{
Clipboard.SetDataObject(((BookmarkAttributes)selectedResult.ContextData).Url);
return true;
}
catch (Exception e)
{
var message = "Failed to set url in clipboard";
Log.Exception("Main",message, e, "LoadContextMenus");
context.API.ShowMsg(message);
return false;
}
},
IcoPath = "Images\\copylink.png"
}};
}
internal class BookmarkAttributes
{
internal string Url { get; set; }
}
}
}

View file

@ -4,7 +4,7 @@
"Name": "Browser Bookmarks",
"Description": "Search your browser bookmarks",
"Author": "qianlifeng, Ioannis G.",
"Version": "1.3.2",
"Version": "1.4.0",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.BrowserBookmark.dll",

View file

@ -25,7 +25,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
{
Title = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_openwebsite_title"),
SubTitle = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_openwebsite_subtitle"),
IcoPath = "Images\\website.png",
IcoPath = selectedResult.IcoPath,
Action = _ =>
{
SharedCommands.SearchWeb.NewTabInBrowser(pluginManifestInfo.Website);
@ -63,7 +63,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
{
Title = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_pluginsmanifest_title"),
SubTitle = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_pluginsmanifest_subtitle"),
IcoPath = selectedResult.IcoPath,
IcoPath = "Images\\manifestsite.png",
Action = _ =>
{
SharedCommands.SearchWeb.NewTabInBrowser("https://github.com/Flow-Launcher/Flow.Launcher.PluginsManifest");

View file

@ -18,6 +18,7 @@
<ItemGroup>
<ProjectReference Include="..\..\Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj" />
<ProjectReference Include="..\..\Flow.Launcher.Core\Flow.Launcher.Core.csproj" />
<ProjectReference Include="..\..\Flow.Launcher.Plugin\Flow.Launcher.Plugin.csproj" />
</ItemGroup>

View file

Before

Width:  |  Height:  |  Size: 131 KiB

After

Width:  |  Height:  |  Size: 131 KiB

View file

@ -1,3 +1,4 @@
using Flow.Launcher.Core.Plugin;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Http;
using Flow.Launcher.Infrastructure.Logger;
@ -400,6 +401,9 @@ namespace Flow.Launcher.Plugin.PluginsManager
private void Uninstall(PluginMetadata plugin)
{
PluginManager.Settings.Plugins.Remove(plugin.ID);
PluginManager.AllPlugins.RemoveAll(p => p.Metadata.ID == plugin.ID);
// Marked for deletion. Will be deleted on next start up
using var _ = File.CreateText(Path.Combine(plugin.PluginDirectory, "NeedDelete.txt"));
}

View file

@ -6,7 +6,7 @@
"Name": "Plugins Manager",
"Description": "Management of installing, uninstalling or updating Flow Launcher plugins",
"Author": "Jeremy Wu",
"Version": "1.6.2",
"Version": "1.6.4",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.PluginsManager.dll",

View file

@ -38,10 +38,11 @@ Flow Launcher. Dedicated to make your workflow flow more seamlessly. Aimed at be
Windows may complain about security due to code not being signed, this will be completed at a later stage. If you downloaded from this repo, you are good to continue the set up.
**Integrations**
- If you use Python plugins:
- Install [Python3](https://www.python.org/downloads/), download `.exe` installer.
- Python plugins:
- Once a Python plugin has been detected at start up, you will be prompted to either select the location or allow Python 3.9.1 to automatic download and install.
- Add Python to `%PATH%` or set it in flow's settings.
- Use `pip` to install `flowlauncher`, cmd in `pip install flowlauncher`.
- Use `pip` to install `flowlauncher`, open cmd and type `pip install flowlauncher`.
- The Python plugin may require additional modules to be installed, please ensure you check by visiting the plugin's website via `pm install` + plugin name, go to context menu and select `Open website`.
- Start to launch your Python plugins.
- Flow searches files and contents via Windows Index Search, to use Everything, download the plugin [here](https://github.com/Flow-Launcher/Flow.Launcher.Plugin.Everything/releases/latest).
@ -51,6 +52,7 @@ Windows may complain about security due to code not being signed, this will be c
- Open context menu: on the selected result, press <kbd>Ctrl</kbd>+<kbd>O</kbd>/<kbd>Shift</kbd>+<kbd>Enter</kbd>.
- Cancel/Return to previous screen: <kbd>Esc</kbd>.
- Install/Uninstall/Update plugins: in the search window, type `pm install`/`pm uninstall`/`pm update` + the plugin name.
- Press `F5` while in the query window to reload all plugin data.
- Saved user settings are located:
- If using roaming: `%APPDATA%\FlowLauncher`
- If using portable, by default: `%localappdata%\FlowLauncher\app-<VersionOfYourFlowLauncher>\UserData`

View file

@ -16,6 +16,6 @@ using System.Runtime.InteropServices;
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: AssemblyVersion("1.7.0")]
[assembly: AssemblyFileVersion("1.7.0")]
[assembly: AssemblyInformationalVersion("1.7.0")]
[assembly: AssemblyVersion("1.7.1")]
[assembly: AssemblyFileVersion("1.7.1")]
[assembly: AssemblyInformationalVersion("1.7.1")]

View file

@ -1,4 +1,4 @@
version: '1.7.0.{build}'
version: '1.7.1.{build}'
init:
- ps: |