Merge pull request #218 from Flow-Launcher/dev

Release 1.6.0 | Plugin 1.3.1
This commit is contained in:
Jeremy Wu 2020-12-31 18:00:49 +11:00 committed by GitHub
commit b3eb82a88c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
80 changed files with 1364 additions and 1314 deletions

6
.gitignore vendored
View file

@ -300,4 +300,8 @@ migrateToAutomaticPackageRestore.ps1
*.pyc
*.diagsession
Output-Performance.txt
*.diff
*.diff
# vscode
.vscode
.history

View file

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

View file

@ -1,169 +0,0 @@
using System;
using System.IO;
using System.Windows;
using ICSharpCode.SharpZipLib.Zip;
using Newtonsoft.Json;
using Flow.Launcher.Plugin;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Logger;
namespace Flow.Launcher.Core.Plugin
{
internal class PluginInstaller
{
internal static void Install(string path)
{
if (File.Exists(path))
{
string tempFolder = Path.Combine(Path.GetTempPath(), "flowlauncher", "plugins");
if (Directory.Exists(tempFolder))
{
Directory.Delete(tempFolder, true);
}
UnZip(path, tempFolder, true);
string jsonPath = Path.Combine(tempFolder, Constant.PluginMetadataFileName);
if (!File.Exists(jsonPath))
{
MessageBox.Show("Install failed: plugin config is missing");
return;
}
PluginMetadata plugin = GetMetadataFromJson(tempFolder);
if (plugin == null || plugin.Name == null)
{
MessageBox.Show("Install failed: plugin config is invalid");
return;
}
string pluginFolderPath = Infrastructure.UserSettings.DataLocation.PluginsDirectory;
string newPluginName = plugin.Name
.Replace("/", "_")
.Replace("\\", "_")
.Replace(":", "_")
.Replace("<", "_")
.Replace(">", "_")
.Replace("?", "_")
.Replace("*", "_")
.Replace("|", "_")
+ "-" + Guid.NewGuid();
string newPluginPath = Path.Combine(pluginFolderPath, newPluginName);
string content = $"Do you want to install following plugin?{Environment.NewLine}{Environment.NewLine}" +
$"Name: {plugin.Name}{Environment.NewLine}" +
$"Version: {plugin.Version}{Environment.NewLine}" +
$"Author: {plugin.Author}";
PluginPair existingPlugin = PluginManager.GetPluginForId(plugin.ID);
if (existingPlugin != null)
{
content = $"Do you want to update following plugin?{Environment.NewLine}{Environment.NewLine}" +
$"Name: {plugin.Name}{Environment.NewLine}" +
$"Old Version: {existingPlugin.Metadata.Version}" +
$"{Environment.NewLine}New Version: {plugin.Version}" +
$"{Environment.NewLine}Author: {plugin.Author}";
}
var result = MessageBox.Show(content, "Install plugin", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
if (existingPlugin != null && Directory.Exists(existingPlugin.Metadata.PluginDirectory))
{
//when plugin is in use, we can't delete them. That's why we need to make plugin folder a random name
File.Create(Path.Combine(existingPlugin.Metadata.PluginDirectory, "NeedDelete.txt")).Close();
}
Directory.Move(tempFolder, newPluginPath);
//exsiting plugins may be has loaded by application,
//if we try to delelte those kind of plugins, we will get a error that indicate the
//file is been used now.
//current solution is to restart Flow Launcher. Ugly.
//if (MainWindow.Initialized)
//{
// Plugins.Initialize();
//}
if (MessageBox.Show($"You have installed plugin {plugin.Name} successfully.{Environment.NewLine}" +
"Restart Flow Launcher to take effect?",
"Install plugin", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
{
PluginManager.API.RestartApp();
}
}
}
}
private static PluginMetadata GetMetadataFromJson(string pluginDirectory)
{
string configPath = Path.Combine(pluginDirectory, Constant.PluginMetadataFileName);
PluginMetadata metadata;
if (!File.Exists(configPath))
{
return null;
}
try
{
metadata = JsonConvert.DeserializeObject<PluginMetadata>(File.ReadAllText(configPath));
metadata.PluginDirectory = pluginDirectory;
}
catch (Exception e)
{
Log.Exception($"|PluginInstaller.GetMetadataFromJson|plugin config {configPath} failed: invalid json format", e);
return null;
}
if (!AllowedLanguage.IsAllowed(metadata.Language))
{
Log.Error($"|PluginInstaller.GetMetadataFromJson|plugin config {configPath} failed: invalid language {metadata.Language}");
return null;
}
if (!File.Exists(metadata.ExecuteFilePath))
{
Log.Error($"|PluginInstaller.GetMetadataFromJson|plugin config {configPath} failed: file {metadata.ExecuteFilePath} doesn't exist");
return null;
}
return metadata;
}
/// <summary>
/// unzip plugin contents to the given directory.
/// </summary>
/// <param name="zipFile">The path to the zip file.</param>
/// <param name="strDirectory">The output directory.</param>
/// <param name="overWrite">overwirte</param>
private static void UnZip(string zipFile, string strDirectory, bool overWrite)
{
if (strDirectory == "")
strDirectory = Directory.GetCurrentDirectory();
using (ZipInputStream zipStream = new ZipInputStream(File.OpenRead(zipFile)))
{
ZipEntry theEntry;
while ((theEntry = zipStream.GetNextEntry()) != null)
{
var pathToZip = theEntry.Name;
var directoryName = String.IsNullOrEmpty(pathToZip) ? "" : Path.GetDirectoryName(pathToZip);
var fileName = Path.GetFileName(pathToZip);
var destinationDir = Path.Combine(strDirectory, directoryName);
var destinationFile = Path.Combine(destinationDir, fileName);
Directory.CreateDirectory(destinationDir);
if (String.IsNullOrEmpty(fileName) || (File.Exists(destinationFile) && !overWrite))
continue;
using (FileStream streamWriter = File.Create(destinationFile))
{
zipStream.CopyTo(streamWriter);
}
}
}
}
}
}

View file

@ -133,11 +133,6 @@ namespace Flow.Launcher.Core.Plugin
}
}
public static void InstallPlugin(string path)
{
PluginInstaller.Install(path);
}
public static List<PluginPair> ValidPluginsForQuery(Query query)
{
if (NonGlobalPlugins.ContainsKey(query.ActionKeyword))

View file

@ -88,7 +88,6 @@ namespace Flow.Launcher.Core.Resource
{
language = language.NonNull();
Settings.Language = language.LanguageCode;
RemoveOldLanguageFiles();
if (language != AvailableLanguages.English)
@ -96,6 +95,7 @@ namespace Flow.Launcher.Core.Resource
LoadLanguage(language);
}
UpdatePluginMetadataTranslations();
Settings.Language = language.LanguageCode;
}

View file

@ -8,7 +8,6 @@ using System.Threading.Tasks;
using System.Windows;
using JetBrains.Annotations;
using Squirrel;
using Newtonsoft.Json;
using Flow.Launcher.Core.Resource;
using Flow.Launcher.Plugin.SharedCommands;
using Flow.Launcher.Infrastructure;
@ -17,6 +16,7 @@ using Flow.Launcher.Infrastructure.Logger;
using System.IO;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using System.Text.Json.Serialization;
namespace Flow.Launcher.Core
{
@ -29,101 +29,80 @@ namespace Flow.Launcher.Core
GitHubRepository = gitHubRepository;
}
public async Task UpdateApp(IPublicAPI api , bool silentUpdate = true)
public async Task UpdateApp(IPublicAPI api, bool silentUpdate = true)
{
UpdateManager updateManager;
UpdateInfo newUpdateInfo;
if (!silentUpdate)
api.ShowMsg("Please wait...", "Checking for new update");
try
{
updateManager = await GitHubUpdateManager(GitHubRepository);
}
catch (Exception e) when (e is HttpRequestException || e is WebException || e is SocketException)
{
Log.Exception($"|Updater.UpdateApp|Please check your connection and proxy settings to api.github.com.", e);
return;
}
UpdateInfo newUpdateInfo;
try
{
// UpdateApp CheckForUpdate will return value only if the app is squirrel installed
newUpdateInfo = await updateManager.CheckForUpdate().NonNull();
}
catch (Exception e) when (e is HttpRequestException || e is WebException || e is SocketException)
{
Log.Exception($"|Updater.UpdateApp|Check your connection and proxy settings to api.github.com.", e);
updateManager.Dispose();
return;
}
var newReleaseVersion = Version.Parse(newUpdateInfo.FutureReleaseEntry.Version.ToString());
var currentVersion = Version.Parse(Constant.Version);
Log.Info($"|Updater.UpdateApp|Future Release <{newUpdateInfo.FutureReleaseEntry.Formatted()}>");
if (newReleaseVersion <= currentVersion)
{
if (!silentUpdate)
MessageBox.Show("You already have the latest Flow Launcher version");
updateManager.Dispose();
return;
}
api.ShowMsg("Please wait...", "Checking for new update");
if (!silentUpdate)
api.ShowMsg("Update found", "Updating...");
using var updateManager = await GitHubUpdateManager(GitHubRepository).ConfigureAwait(false);
try
{
await updateManager.DownloadReleases(newUpdateInfo.ReleasesToApply);
// UpdateApp CheckForUpdate will return value only if the app is squirrel installed
newUpdateInfo = await updateManager.CheckForUpdate().NonNull().ConfigureAwait(false);
var newReleaseVersion = Version.Parse(newUpdateInfo.FutureReleaseEntry.Version.ToString());
var currentVersion = Version.Parse(Constant.Version);
Log.Info($"|Updater.UpdateApp|Future Release <{newUpdateInfo.FutureReleaseEntry.Formatted()}>");
if (newReleaseVersion <= currentVersion)
{
if (!silentUpdate)
MessageBox.Show("You already have the latest Flow Launcher version");
return;
}
if (!silentUpdate)
api.ShowMsg("Update found", "Updating...");
await updateManager.DownloadReleases(newUpdateInfo.ReleasesToApply).ConfigureAwait(false);
await updateManager.ApplyReleases(newUpdateInfo).ConfigureAwait(false);
if (DataLocation.PortableDataLocationInUse())
{
var targetDestination = updateManager.RootAppDirectory + $"\\app-{newReleaseVersion.ToString()}\\{DataLocation.PortableFolderName}";
FilesFolders.CopyAll(DataLocation.PortableDataPath, targetDestination);
if (!FilesFolders.VerifyBothFolderFilesEqual(DataLocation.PortableDataPath, targetDestination))
MessageBox.Show("Flow Launcher was not able to move your user profile data to the new update version. Please manually " +
$"move your profile data folder from {DataLocation.PortableDataPath} to {targetDestination}");
}
else
{
await updateManager.CreateUninstallerRegistryEntry().ConfigureAwait(false);
}
var newVersionTips = NewVersinoTips(newReleaseVersion.ToString());
Log.Info($"|Updater.UpdateApp|Update success:{newVersionTips}");
if (MessageBox.Show(newVersionTips, "New Update", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
UpdateManager.RestartApp(Constant.ApplicationFileName);
}
}
catch (Exception e) when (e is HttpRequestException || e is WebException || e is SocketException)
{
Log.Exception($"|Updater.UpdateApp|Check your connection and proxy settings to github-cloud.s3.amazonaws.com.", e);
updateManager.Dispose();
api.ShowMsg("Update Failed", "Check your connection and try updating proxy settings to github-cloud.s3.amazonaws.com.");
return;
}
await updateManager.ApplyReleases(newUpdateInfo);
if (DataLocation.PortableDataLocationInUse())
{
var targetDestination = updateManager.RootAppDirectory + $"\\app-{newReleaseVersion.ToString()}\\{DataLocation.PortableFolderName}";
FilesFolders.CopyAll(DataLocation.PortableDataPath, targetDestination);
if (!FilesFolders.VerifyBothFolderFilesEqual(DataLocation.PortableDataPath, targetDestination))
MessageBox.Show("Flow Launcher was not able to move your user profile data to the new update version. Please manually " +
$"move your profile data folder from {DataLocation.PortableDataPath} to {targetDestination}");
}
else
{
await updateManager.CreateUninstallerRegistryEntry();
}
var newVersionTips = NewVersinoTips(newReleaseVersion.ToString());
Log.Info($"|Updater.UpdateApp|Update success:{newVersionTips}");
// always dispose UpdateManager
updateManager.Dispose();
if (MessageBox.Show(newVersionTips, "New Update", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
UpdateManager.RestartApp(Constant.ApplicationFileName);
}
}
[UsedImplicitly]
private class GithubRelease
{
[JsonProperty("prerelease")]
[JsonPropertyName("prerelease")]
public bool Prerelease { get; [UsedImplicitly] set; }
[JsonProperty("published_at")]
[JsonPropertyName("published_at")]
public DateTime PublishedAt { get; [UsedImplicitly] set; }
[JsonProperty("html_url")]
[JsonPropertyName("html_url")]
public string HtmlUrl { get; [UsedImplicitly] set; }
}
@ -133,13 +112,13 @@ namespace Flow.Launcher.Core
var uri = new Uri(repository);
var api = $"https://api.github.com/repos{uri.AbsolutePath}/releases";
var json = await Http.Get(api);
var jsonStream = await Http.GetStreamAsync(api).ConfigureAwait(false);
var releases = JsonConvert.DeserializeObject<List<GithubRelease>>(json);
var releases = await System.Text.Json.JsonSerializer.DeserializeAsync<List<GithubRelease>>(jsonStream).ConfigureAwait(false);
var latest = releases.Where(r => !r.Prerelease).OrderByDescending(r => r.PublishedAt).First();
var latestUrl = latest.HtmlUrl.Replace("/tag/", "/download/");
var client = new WebClient { Proxy = Http.WebProxy() };
var client = new WebClient { Proxy = Http.WebProxy };
var downloader = new FileDownloader(client);
var manager = new UpdateManager(latestUrl, urlDownloader: downloader);

View file

@ -35,5 +35,7 @@ namespace Flow.Launcher.Infrastructure
public const string DefaultTheme = "Darker";
public const string Themes = "Themes";
public const string Website = "https://flow-launcher.github.io";
}
}

View file

@ -6,6 +6,8 @@ using System.Threading.Tasks;
using JetBrains.Annotations;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings;
using System;
using System.ComponentModel;
namespace Flow.Launcher.Infrastructure.Http
{
@ -13,6 +15,14 @@ namespace Flow.Launcher.Infrastructure.Http
{
private const string UserAgent = @"Mozilla/5.0 (Trident/7.0; rv:11.0) like Gecko";
private static HttpClient client;
private static SocketsHttpHandler socketsHttpHandler = new SocketsHttpHandler()
{
UseProxy = true,
Proxy = WebProxy
};
static Http()
{
// need to be added so it would work on a win10 machine
@ -20,64 +30,103 @@ namespace Flow.Launcher.Infrastructure.Http
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls
| SecurityProtocolType.Tls11
| SecurityProtocolType.Tls12;
client = new HttpClient(socketsHttpHandler, false);
client.DefaultRequestHeaders.Add("User-Agent", UserAgent);
}
public static HttpProxy Proxy { private get; set; }
public static IWebProxy WebProxy()
private static HttpProxy proxy;
public static HttpProxy Proxy
{
if (Proxy != null && Proxy.Enabled && !string.IsNullOrEmpty(Proxy.Server))
private get { return proxy; }
set
{
if (string.IsNullOrEmpty(Proxy.UserName) || string.IsNullOrEmpty(Proxy.Password))
proxy = value;
proxy.PropertyChanged += UpdateProxy;
}
}
public static WebProxy WebProxy { get; } = new WebProxy();
/// <summary>
/// Update the Address of the Proxy to modify the client Proxy
/// </summary>
public static void UpdateProxy(ProxyProperty property)
{
(WebProxy.Address, WebProxy.Credentials) = property switch
{
ProxyProperty.Enabled => Proxy.Enabled switch
{
var webProxy = new WebProxy(Proxy.Server, Proxy.Port);
return webProxy;
}
else
{
var webProxy = new WebProxy(Proxy.Server, Proxy.Port)
true => Proxy.UserName switch
{
Credentials = new NetworkCredential(Proxy.UserName, Proxy.Password)
};
return webProxy;
}
var userName when !string.IsNullOrEmpty(userName) =>
(new Uri($"http://{Proxy.Server}:{Proxy.Port}"), null),
_ => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"),
new NetworkCredential(Proxy.UserName, Proxy.Password))
},
false => (null, null)
},
ProxyProperty.Server => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), WebProxy.Credentials),
ProxyProperty.Port => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), WebProxy.Credentials),
ProxyProperty.UserName => (WebProxy.Address, new NetworkCredential(Proxy.UserName, Proxy.Password)),
ProxyProperty.Password => (WebProxy.Address, new NetworkCredential(Proxy.UserName, Proxy.Password)),
_ => throw new ArgumentOutOfRangeException()
};
}
public static async Task Download([NotNull] string url, [NotNull] string filePath)
{
using var response = await client.GetAsync(url);
if (response.StatusCode == HttpStatusCode.OK)
{
await using var fileStream = new FileStream(filePath, FileMode.CreateNew);
await response.Content.CopyToAsync(fileStream);
}
else
{
return WebRequest.GetSystemWebProxy();
throw new HttpRequestException($"Error code <{response.StatusCode}> returned from <{url}>");
}
}
public static void Download([NotNull] string url, [NotNull] string filePath)
{
var client = new WebClient { Proxy = WebProxy() };
client.Headers.Add("user-agent", UserAgent);
client.DownloadFile(url, filePath);
}
public static async Task<string> Get([NotNull] string url, string encoding = "UTF-8")
/// <summary>
/// Asynchrously get the result as string from url.
/// When supposing the result is long and large, try using GetStreamAsync to avoid reading as string
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public static Task<string> GetAsync([NotNull] string url)
{
Log.Debug($"|Http.Get|Url <{url}>");
var request = WebRequest.CreateHttp(url);
request.Method = "GET";
request.Timeout = 1000;
request.Proxy = WebProxy();
request.UserAgent = UserAgent;
var response = await request.GetResponseAsync() as HttpWebResponse;
response = response.NonNull();
var stream = response.GetResponseStream().NonNull();
return GetAsync(new Uri(url.Replace("#", "%23")));
}
using (var reader = new StreamReader(stream, Encoding.GetEncoding(encoding)))
public static async Task<string> GetAsync([NotNull] Uri url)
{
Log.Debug($"|Http.Get|Url <{url}>");
using var response = await client.GetAsync(url);
var content = await response.Content.ReadAsStringAsync();
if (response.StatusCode == HttpStatusCode.OK)
{
var content = await reader.ReadToEndAsync();
if (response.StatusCode == HttpStatusCode.OK)
{
return content;
}
else
{
throw new HttpRequestException($"Error code <{response.StatusCode}> with content <{content}> returned from <{url}>");
}
return content;
}
else
{
throw new HttpRequestException(
$"Error code <{response.StatusCode}> with content <{content}> returned from <{url}>");
}
}
/// <summary>
/// Asynchrously get the result as stream from url.
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public static async Task<Stream> GetStreamAsync([NotNull] string url)
{
Log.Debug($"|Http.Get|Url <{url}>");
var response = await client.GetAsync(url);
return await response.Content.ReadAsStreamAsync();
}
}
}
}

View file

@ -26,7 +26,7 @@ namespace Flow.Launcher.Infrastructure.Image
private const int MaxCached = 50;
public ConcurrentDictionary<string, ImageUsage> Data { get; private set; } = new ConcurrentDictionary<string, ImageUsage>();
private const int permissibleFactor = 2;
public void Initialization(Dictionary<string, int> usage)
{
foreach (var key in usage.Keys)
@ -44,14 +44,14 @@ namespace Flow.Launcher.Infrastructure.Image
value.usage++;
return value.imageSource;
}
return null;
}
set
{
Data.AddOrUpdate(
path,
new ImageUsage(0, value),
path,
new ImageUsage(0, value),
(k, v) =>
{
v.imageSource = value;
@ -65,22 +65,15 @@ namespace Flow.Launcher.Infrastructure.Image
if (Data.Count > permissibleFactor * MaxCached)
{
// To delete the images from the data dictionary based on the resizing of the Usage Dictionary.
foreach (var key in Data.OrderBy(x => x.Value.usage).Take(Data.Count - MaxCached).Select(x => x.Key))
{
if (!(key.Equals(Constant.ErrorIcon) || key.Equals(Constant.DefaultIcon)))
{
Data.TryRemove(key, out _);
}
}
Data.TryRemove(key, out _);
}
}
}
public bool ContainsKey(string key)
{
var contains = Data.ContainsKey(key);
var contains = Data.ContainsKey(key) && Data[key] != null;
return contains;
}
@ -97,5 +90,4 @@ namespace Flow.Launcher.Infrastructure.Image
return Data.Values.Select(x => x.imageSource).Distinct().Count();
}
}
}

View file

@ -18,6 +18,8 @@ namespace Flow.Launcher.Infrastructure.Image
private static readonly ConcurrentDictionary<string, string> GuidToKey = new ConcurrentDictionary<string, string>();
private static IImageHashGenerator _hashGenerator;
private static bool EnableImageHash = true;
public static ImageSource DefaultImage { get; } = new BitmapImage(new Uri(Constant.MissingImgIcon));
private static readonly string[] ImageExtensions =
{
@ -61,7 +63,7 @@ namespace Flow.Launcher.Infrastructure.Image
{
lock (_storage)
{
_storage.Save(ImageCache.Data.Select(x => (x.Key, x.Value.usage)).ToDictionary(x => x.Key, y => y.usage));
_storage.Save(ImageCache.Data.Select(x => (x.Key, x.Value.usage)).ToDictionary(x => x.Key, x => x.usage));
}
}
@ -211,6 +213,11 @@ namespace Flow.Launcher.Infrastructure.Image
option);
}
public static bool CacheContainImage(string path)
{
return ImageCache.ContainsKey(path) && ImageCache[path] != null;
}
public static ImageSource Load(string path, bool loadFullImage = false)
{
var imageResult = LoadInternal(path, loadFullImage);
@ -221,7 +228,7 @@ namespace Flow.Launcher.Infrastructure.Image
string hash = EnableImageHash ? _hashGenerator.GetHashFromImage(img) : null;
if (hash != null)
{
if (GuidToKey.TryGetValue(hash, out string key))
{ // image already exists
img = ImageCache[key] ?? img;

View file

@ -1,14 +1,10 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using JetBrains.Annotations;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.Storage;
using Flow.Launcher.Infrastructure.UserSettings;
using ToolGood.Words.Pinyin;
using System.Threading.Tasks;
namespace Flow.Launcher.Infrastructure
{
@ -27,7 +23,6 @@ namespace Flow.Launcher.Infrastructure
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
}
public string Translate(string content)
{
if (_settings.ShouldUsePinyin)
@ -36,10 +31,40 @@ namespace Flow.Launcher.Infrastructure
{
if (WordsHelper.HasChinese(content))
{
var result = WordsHelper.GetPinyin(content, ";");
result = GetFirstPinyinChar(result) + result.Replace(";", "");
_pinyinCache[content] = result;
return result;
var resultList = WordsHelper.GetPinyinList(content);
StringBuilder resultBuilder = new StringBuilder();
for (int i = 0; i < resultList.Length; i++)
{
if (content[i] >= 0x3400 && content[i] <= 0x9FD5)
resultBuilder.Append(resultList[i].First());
}
resultBuilder.Append(' ');
bool pre = false;
for (int i = 0; i < resultList.Length; i++)
{
if (content[i] >= 0x3400 && content[i] <= 0x9FD5)
{
resultBuilder.Append(' ');
resultBuilder.Append(resultList[i]);
pre = true;
}
else
{
if (pre)
{
pre = false;
resultBuilder.Append(' ');
}
resultBuilder.Append(resultList[i]);
}
}
return _pinyinCache[content] = resultBuilder.ToString();
}
else
{
@ -56,10 +81,5 @@ namespace Flow.Launcher.Infrastructure
return content;
}
}
private string GetFirstPinyinChar(string content)
{
return string.Concat(content.Split(';').Select(x => x.First()));
}
}
}

View file

@ -1,11 +1,80 @@
namespace Flow.Launcher.Infrastructure.UserSettings
using System.ComponentModel;
namespace Flow.Launcher.Infrastructure.UserSettings
{
public enum ProxyProperty
{
Enabled,
Server,
Port,
UserName,
Password
}
public class HttpProxy
{
public bool Enabled { get; set; } = false;
public string Server { get; set; }
public int Port { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
private bool _enabled = false;
private string _server;
private int _port;
private string _userName;
private string _password;
public bool Enabled
{
get => _enabled;
set
{
_enabled = value;
OnPropertyChanged(ProxyProperty.Enabled);
}
}
public string Server
{
get => _server;
set
{
_server = value;
OnPropertyChanged(ProxyProperty.Server);
}
}
public int Port
{
get => _port;
set
{
_port = value;
OnPropertyChanged(ProxyProperty.Port);
}
}
public string UserName
{
get => _userName;
set
{
_userName = value;
OnPropertyChanged(ProxyProperty.UserName);
}
}
public string Password
{
get => _password;
set
{
_password = value;
OnPropertyChanged(ProxyProperty.Password);
}
}
public delegate void ProxyPropertyChangedHandler(ProxyProperty property);
public event ProxyPropertyChangedHandler PropertyChanged;
private void OnPropertyChanged(ProxyProperty property)
{
PropertyChanged?.Invoke(property);
}
}
}

View file

@ -9,10 +9,18 @@ namespace Flow.Launcher.Infrastructure.UserSettings
{
public class Settings : BaseModel
{
private string language = "en";
public string Hotkey { get; set; } = $"{KeyConstant.Alt} + {KeyConstant.Space}";
public string OpenResultModifiers { get; set; } = KeyConstant.Alt;
public bool ShowOpenResultHotkey { get; set; } = true;
public string Language { get; set; } = "en";
public string Language
{
get => language; set {
language = value;
OnPropertyChanged();
}
}
public string Theme { get; set; } = Constant.DefaultTheme;
public bool UseDropShadowEffect { get; set; } = false;
public string QueryBoxFont { get; set; } = FontFamily.GenericSansSerif.Name;

View file

@ -14,10 +14,10 @@
</PropertyGroup>
<PropertyGroup>
<Version>1.3.0</Version>
<PackageVersion>1.3.0</PackageVersion>
<AssemblyVersion>1.3.0</AssemblyVersion>
<FileVersion>1.3.0</FileVersion>
<Version>1.3.1</Version>
<PackageVersion>1.3.1</PackageVersion>
<AssemblyVersion>1.3.1</AssemblyVersion>
<FileVersion>1.3.1</FileVersion>
<PackageId>Flow.Launcher.Plugin</PackageId>
<Authors>Flow-Launcher</Authors>
<PackageLicenseExpression>MIT</PackageLicenseExpression>

View file

@ -63,12 +63,6 @@ namespace Flow.Launcher.Plugin
/// </summary>
void OpenSettingDialog();
/// <summary>
/// Install Flow Launcher plugin
/// </summary>
/// <param name="path">Plugin path (ends with .flowlauncher)</param>
void InstallPlugin(string path);
/// <summary>
/// Get translation of current language
/// You need to implement IPluginI18n if you want to support multiple languages for your plugin

View file

@ -15,23 +15,20 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher", "Flow.Launc
ProjectSection(ProjectDependencies) = postProject
{1EE20B48-82FB-48A2-8086-675D6DDAB4F0} = {1EE20B48-82FB-48A2-8086-675D6DDAB4F0}
{0B9DE348-9361-4940-ADB6-F5953BFFCCEC} = {0B9DE348-9361-4940-ADB6-F5953BFFCCEC}
{4792A74A-0CEA-4173-A8B2-30E6764C6217} = {4792A74A-0CEA-4173-A8B2-30E6764C6217}
{FDB3555B-58EF-4AE6-B5F1-904719637AB4} = {FDB3555B-58EF-4AE6-B5F1-904719637AB4}
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F} = {F9C4C081-4CC3-4146-95F1-E102B4E10A5F}
{59BD9891-3837-438A-958D-ADC7F91F6F7E} = {59BD9891-3837-438A-958D-ADC7F91F6F7E}
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} = {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}
{F35190AA-4758-4D9E-A193-E3BDF6AD3567} = {F35190AA-4758-4D9E-A193-E3BDF6AD3567}
{9B130CC5-14FB-41FF-B310-0A95B6894C37} = {9B130CC5-14FB-41FF-B310-0A95B6894C37}
{FDED22C8-B637-42E8-824A-63B5B6E05A3A} = {FDED22C8-B637-42E8-824A-63B5B6E05A3A}
{A3DCCBCA-ACC1-421D-B16E-210896234C26} = {A3DCCBCA-ACC1-421D-B16E-210896234C26}
{049490F0-ECD2-4148-9B39-2135EC346EBE} = {049490F0-ECD2-4148-9B39-2135EC346EBE}
{403B57F2-1856-4FC7-8A24-36AB346B763E} = {403B57F2-1856-4FC7-8A24-36AB346B763E}
{588088F4-3262-4F9F-9663-A05DE12534C3} = {588088F4-3262-4F9F-9663-A05DE12534C3}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Infrastructure", "Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj", "{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.PluginManagement", "Plugins\Flow.Launcher.Plugin.PluginManagement\Flow.Launcher.Plugin.PluginManagement.csproj", "{049490F0-ECD2-4148-9B39-2135EC346EBE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Core", "Flow.Launcher.Core\Flow.Launcher.Core.csproj", "{B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Program", "Plugins\Flow.Launcher.Plugin.Program\Flow.Launcher.Plugin.Program.csproj", "{FDB3555B-58EF-4AE6-B5F1-904719637AB4}"
@ -46,8 +43,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Sys",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Url", "Plugins\Flow.Launcher.Plugin.Url\Flow.Launcher.Plugin.Url.csproj", "{A3DCCBCA-ACC1-421D-B16E-210896234C26}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Color", "Plugins\Flow.Launcher.Plugin.Color\Flow.Launcher.Plugin.Color.csproj", "{F35190AA-4758-4D9E-A193-E3BDF6AD3567}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{FFD651C7-0546-441F-BC8C-D4EE8FD01EA7}"
ProjectSection(SolutionItems) = preProject
.gitattributes = .gitattributes
@ -71,6 +66,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Explor
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.ProcessKiller", "Plugins\Flow.Launcher.Plugin.ProcessKiller\Flow.Launcher.Plugin.ProcessKiller.csproj", "{588088F4-3262-4F9F-9663-A05DE12534C3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.PluginsManager", "Plugins\Flow.Launcher.Plugin.PluginsManager\Flow.Launcher.Plugin.PluginsManager.csproj", "{4792A74A-0CEA-4173-A8B2-30E6764C6217}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -129,18 +126,6 @@ Global
{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Release|x64.Build.0 = Release|Any CPU
{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Release|x86.ActiveCfg = Release|Any CPU
{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Release|x86.Build.0 = Release|Any CPU
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Debug|x64.ActiveCfg = Debug|Any CPU
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Debug|x64.Build.0 = Debug|Any CPU
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Debug|x86.ActiveCfg = Debug|Any CPU
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Debug|x86.Build.0 = Debug|Any CPU
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Release|Any CPU.Build.0 = Release|Any CPU
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Release|x64.ActiveCfg = Release|Any CPU
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Release|x64.Build.0 = Release|Any CPU
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Release|x86.ActiveCfg = Release|Any CPU
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Release|x86.Build.0 = Release|Any CPU
{B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}.Debug|x64.ActiveCfg = Debug|Any CPU
@ -226,18 +211,6 @@ Global
{A3DCCBCA-ACC1-421D-B16E-210896234C26}.Release|x64.Build.0 = Release|Any CPU
{A3DCCBCA-ACC1-421D-B16E-210896234C26}.Release|x86.ActiveCfg = Release|Any CPU
{A3DCCBCA-ACC1-421D-B16E-210896234C26}.Release|x86.Build.0 = Release|Any CPU
{F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Debug|x64.ActiveCfg = Debug|Any CPU
{F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Debug|x64.Build.0 = Debug|Any CPU
{F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Debug|x86.ActiveCfg = Debug|Any CPU
{F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Debug|x86.Build.0 = Debug|Any CPU
{F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Release|Any CPU.Build.0 = Release|Any CPU
{F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Release|x64.ActiveCfg = Release|Any CPU
{F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Release|x64.Build.0 = Release|Any CPU
{F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Release|x86.ActiveCfg = Release|Any CPU
{F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Release|x86.Build.0 = Release|Any CPU
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}.Debug|x64.ActiveCfg = Debug|Any CPU
@ -298,24 +271,35 @@ Global
{588088F4-3262-4F9F-9663-A05DE12534C3}.Release|x64.Build.0 = Release|Any CPU
{588088F4-3262-4F9F-9663-A05DE12534C3}.Release|x86.ActiveCfg = Release|Any CPU
{588088F4-3262-4F9F-9663-A05DE12534C3}.Release|x86.Build.0 = Release|Any CPU
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Debug|x64.ActiveCfg = Debug|Any CPU
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Debug|x64.Build.0 = Debug|Any CPU
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Debug|x86.ActiveCfg = Debug|Any CPU
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Debug|x86.Build.0 = Debug|Any CPU
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Release|Any CPU.Build.0 = Release|Any CPU
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Release|x64.ActiveCfg = Release|Any CPU
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Release|x64.Build.0 = Release|Any CPU
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Release|x86.ActiveCfg = Release|Any CPU
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{049490F0-ECD2-4148-9B39-2135EC346EBE} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{FDB3555B-58EF-4AE6-B5F1-904719637AB4} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{403B57F2-1856-4FC7-8A24-36AB346B763E} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{1EE20B48-82FB-48A2-8086-675D6DDAB4F0} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{FDED22C8-B637-42E8-824A-63B5B6E05A3A} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{0B9DE348-9361-4940-ADB6-F5953BFFCCEC} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{A3DCCBCA-ACC1-421D-B16E-210896234C26} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{F35190AA-4758-4D9E-A193-E3BDF6AD3567} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{9B130CC5-14FB-41FF-B310-0A95B6894C37} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{59BD9891-3837-438A-958D-ADC7F91F6F7E} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{588088F4-3262-4F9F-9663-A05DE12534C3} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{4792A74A-0CEA-4173-A8B2-30E6764C6217} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F26ACB50-3F6C-4907-B0C9-1ADACC1D0DED}

View file

@ -5,8 +5,8 @@
<system:String x:Key="registerHotkeyFailed">Nepodarilo sa registrovať klávesovú skratku {0}</system:String>
<system:String x:Key="couldnotStartCmd">Nepodarilo sa spustiť {0}</system:String>
<system:String x:Key="invalidFlowLauncherPluginFileFormat">Neplatný formát súboru pre plugin Flow Launchera</system:String>
<system:String x:Key="setAsTopMostInThisQuery">Pri tomto dopyte umiestniť navrchu</system:String>
<system:String x:Key="cancelTopMostInThisQuery">Zrušiť umiestnenie navrchu pri tomto dopyte</system:String>
<system:String x:Key="setAsTopMostInThisQuery">Pri tomto zadaní umiestniť navrchu</system:String>
<system:String x:Key="cancelTopMostInThisQuery">Zrušiť umiestnenie navrchu pri tomto zadaní</system:String>
<system:String x:Key="executeQuery">Spustiť dopyt: {0}</system:String>
<system:String x:Key="lastExecuteTime">Posledný čas realizácie: {0}</system:String>
<system:String x:Key="iconTrayOpen">Otvoriť</system:String>
@ -22,7 +22,7 @@
<system:String x:Key="dontPromptUpdateMsg">Nezobrazovať upozornenia na novú verziu</system:String>
<system:String x:Key="rememberLastLocation">Zapamätať si posledné umiestnenie</system:String>
<system:String x:Key="language">Jazyk</system:String>
<system:String x:Key="lastQueryMode">Posledný dopyt</system:String>
<system:String x:Key="lastQueryMode">Posledné vyhľadávanie</system:String>
<system:String x:Key="LastQueryPreserved">Ponechať</system:String>
<system:String x:Key="LastQuerySelected">Označiť</system:String>
<system:String x:Key="LastQueryEmpty">Vymazať</system:String>
@ -34,7 +34,7 @@
<system:String x:Key="hideOnStartup">Schovať Flow Launcher po spustení</system:String>
<system:String x:Key="hideNotifyIcon">Schovať ikonu z oblasti oznámení</system:String>
<system:String x:Key="querySearchPrecision">Presnosť vyhľadávania</system:String>
<system:String x:Key="ShouldUsePinyin">Dá sa použiť Pinyin</system:String>
<system:String x:Key="ShouldUsePinyin">Použiť Pinyin</system:String>
<!--Setting Plugin-->
<system:String x:Key="plugin">Plugin</system:String>
@ -96,11 +96,11 @@
<system:String x:Key="version">Verzia</system:String>
<system:String x:Key="about_activate_times">Flow Launcher bol aktivovaný {0}-krát</system:String>
<system:String x:Key="checkUpdates">Skontrolovať aktualizácie</system:String>
<system:String x:Key="newVersionTips">Je dostupná nová verzia {0}, prosím, reštartujte Flow Launcher.</system:String>
<system:String x:Key="newVersionTips">Je dostupná nová verzia {0}, chcete reštartovať Flow Launcher, aby sa mohol aktualizovať?</system:String>
<system:String x:Key="checkUpdatesFailed">Kontrola aktualizácií zlyhala, prosím, skontrolujte pripojenie na internet a nastavenie proxy k api.github.com.</system:String>
<system:String x:Key="downloadUpdatesFailed">
Sťahovanie aktualizácií zlyhalo, skontrolujte pripojenie na internet a nastavenie proxy k github-cloud.s3.amazonaws.com,
alebo prejdite na https://github.com/Flow-Launcher/Flow.Launcher/releases pre manuálne stiahnutie aktualizácií.
alebo prejdite na https://github.com/Flow-Launcher/Flow.Launcher/releases pre manuálne stiahnutie aktualizácie.
</system:String>
<system:String x:Key="releaseNotes">Poznámky k vydaniu</system:String>

View file

@ -22,7 +22,6 @@
Loaded="OnLoaded"
Initialized="OnInitialized"
Closing="OnClosing"
Drop="OnDrop"
LocationChanged="OnLocationChanged"
Deactivated="OnDeactivated"
PreviewKeyDown="OnKeyDown"

View file

@ -52,12 +52,14 @@ namespace Flow.Launcher
private void OnInitialized(object sender, EventArgs e)
{
// show notify icon when flowlauncher is hided
InitializeNotifyIcon();
}
private void OnLoaded(object sender, RoutedEventArgs _)
{
// show notify icon when flowlauncher is hidden
InitializeNotifyIcon();
// todo is there a way to set blur only once?
ThemeManager.Instance.SetBlurForWindow();
WindowsInteropHelper.DisableControlBox(this);
@ -87,11 +89,17 @@ namespace Flow.Launcher
};
_settings.PropertyChanged += (o, e) =>
{
if (e.PropertyName == nameof(Settings.HideNotifyIcon))
switch (e.PropertyName)
{
_notifyIcon.Visible = !_settings.HideNotifyIcon;
case nameof(Settings.HideNotifyIcon):
_notifyIcon.Visible = !_settings.HideNotifyIcon;
break;
case nameof(Settings.Language):
UpdateNotifyIconText();
break;
}
};
InitializePosition();
}
@ -103,6 +111,18 @@ namespace Flow.Launcher
_settings.WindowLeft = Left;
}
private void UpdateNotifyIconText()
{
var menu = _notifyIcon.ContextMenuStrip;
var open = menu.Items[0];
var setting = menu.Items[1];
var exit = menu.Items[2];
open.Text = InternationalizationManager.Instance.GetTranslation("iconTrayOpen");
setting.Text = InternationalizationManager.Instance.GetTranslation("iconTraySettings");
exit.Text = InternationalizationManager.Instance.GetTranslation("iconTrayExit");
}
private void InitializeNotifyIcon()
{
_notifyIcon = new NotifyIcon
@ -179,25 +199,6 @@ namespace Flow.Launcher
}
}
private void OnDrop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
// Note that you can have more than one file.
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
if (files[0].ToLower().EndsWith(".flowlauncher"))
{
PluginManager.InstallPlugin(files[0]);
}
else
{
MessageBox.Show(InternationalizationManager.Instance.GetTranslation("invalidFlowLauncherPluginFileFormat"));
}
}
e.Handled = false;
}
private void OnPreviewDragOver(object sender, DragEventArgs e)
{
e.Handled = true;
@ -294,7 +295,5 @@ namespace Flow.Launcher
_viewModel.QueryTextCursorMovedToEnd = false;
}
}
}
}

View file

@ -115,11 +115,6 @@ namespace Flow.Launcher
_mainVM.ProgressBarVisibility = Visibility.Collapsed;
}
public void InstallPlugin(string path)
{
Application.Current.Dispatcher.Invoke(() => PluginManager.InstallPlugin(path));
}
public string GetTranslation(string key)
{
return InternationalizationManager.Instance.GetTranslation(key);

View file

@ -42,7 +42,7 @@
<ColumnDefinition Width="0" />
</Grid.ColumnDefinitions>
<Image x:Name="ImageIcon" Width="32" Height="32" HorizontalAlignment="Left"
Source="{Binding Image ,IsAsync=True}" />
Source="{Binding Image.Value}" />
<Grid Margin="5 0 5 0" Grid.Column="1" HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition />

View file

@ -429,8 +429,8 @@
<TextBlock Grid.Row="0" Grid.ColumnSpan="2" Text="{Binding ActivatedTimes, Mode=OneWay}" FontSize="12" />
<TextBlock Grid.Row="1" Grid.Column="0" Text="{DynamicResource website}"/>
<TextBlock Grid.Row="1" Grid.Column="1" HorizontalAlignment="Left">
<Hyperlink NavigateUri="{Binding Github, Mode=OneWay}" RequestNavigate="OnRequestNavigate">
<Run Text="{Binding Github, Mode=OneWay}" />
<Hyperlink NavigateUri="{Binding Website, Mode=OneWay}" RequestNavigate="OnRequestNavigate">
<Run Text="{Binding Website, Mode=OneWay}" />
</Hyperlink>
</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="0" Text="Version" />

View file

@ -1,23 +1,66 @@
using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Threading;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Image;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
namespace Flow.Launcher.ViewModel
{
public class ResultViewModel : BaseModel
{
public class LazyAsync<T> : Lazy<Task<T>>
{
private T defaultValue;
private readonly Action _updateCallback;
public new T Value
{
get
{
if (!IsValueCreated)
{
base.Value.ContinueWith(_ =>
{
_updateCallback();
});
return defaultValue;
}
if (!base.Value.IsCompleted || base.Value.IsFaulted)
return defaultValue;
return base.Value.Result;
}
}
public LazyAsync(Func<Task<T>> factory, T defaultValue, Action updateCallback) : base(factory)
{
if (defaultValue != null)
{
this.defaultValue = defaultValue;
}
_updateCallback = updateCallback;
}
}
public ResultViewModel(Result result, Settings settings)
{
if (result != null)
{
Result = result;
Image = new LazyAsync<ImageSource>(
SetImage,
ImageLoader.DefaultImage,
() =>
{
OnPropertyChanged(nameof(Image));
});
}
Settings = settings;
@ -25,39 +68,45 @@ namespace Flow.Launcher.ViewModel
public Settings Settings { get; private set; }
public Visibility ShowOpenResultHotkey => Settings.ShowOpenResultHotkey ? Visibility.Visible : Visibility.Hidden;
public Visibility ShowOpenResultHotkey => Settings.ShowOpenResultHotkey ? Visibility.Visible : Visibility.Hidden;
public string OpenResultModifiers => Settings.OpenResultModifiers;
public string ShowTitleToolTip => string.IsNullOrEmpty(Result.TitleToolTip)
? Result.Title
? Result.Title
: Result.TitleToolTip;
public string ShowSubTitleToolTip => string.IsNullOrEmpty(Result.SubTitleToolTip)
? Result.SubTitle
? Result.SubTitle
: Result.SubTitleToolTip;
public ImageSource Image
public LazyAsync<ImageSource> Image { get; set; }
private async Task<ImageSource> SetImage()
{
get
var imagePath = Result.IcoPath;
if (string.IsNullOrEmpty(imagePath) && Result.Icon != null)
{
var imagePath = Result.IcoPath;
if (string.IsNullOrEmpty(imagePath) && Result.Icon != null)
try
{
try
{
return Result.Icon();
}
catch (Exception e)
{
Log.Exception($"|ResultViewModel.Image|IcoPath is empty and exception when calling Icon() for result <{Result.Title}> of plugin <{Result.PluginDirectory}>", e);
imagePath = Constant.MissingImgIcon;
}
return Result.Icon();
}
catch (Exception e)
{
Log.Exception($"|ResultViewModel.Image|IcoPath is empty and exception when calling Icon() for result <{Result.Title}> of plugin <{Result.PluginDirectory}>", e);
imagePath = Constant.MissingImgIcon;
}
}
if (ImageLoader.CacheContainImage(imagePath))
{
// will get here either when icoPath has value\icon delegate is null\when had exception in delegate
return ImageLoader.Load(imagePath);
}
else
{
return await Task.Run(() => ImageLoader.Load(imagePath));
}
}
public Result Result { get; }
@ -84,6 +133,5 @@ namespace Flow.Launcher.ViewModel
{
return Result.ToString();
}
}
}

View file

@ -213,7 +213,7 @@ namespace Flow.Launcher.ViewModel
#region plugin
public static string Plugin => "http://www.wox.one/plugin";
public static string Plugin => @"https://github.com/Flow-Launcher/Flow.Launcher.PluginsManifest";
public PluginViewModel SelectedPlugin { get; set; }
public IList<PluginViewModel> PluginViewModels
@ -450,7 +450,7 @@ namespace Flow.Launcher.ViewModel
#region about
public string Github => _updater.GitHubRepository;
public string Website => Constant.Website;
public string ReleaseNotes => _updater.GitHubRepository + @"/releases/latest";
public static string Version => Constant.Version;
public string ActivatedTimes => string.Format(_translater.GetTranslation("about_activate_times"), Settings.ActivateTimes);

View file

@ -1,115 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import print_function
import json
import sys
import inspect
class FlowLauncher(object):
"""
Flow.Launcher python plugin base
"""
def __init__(self):
rpc_request = json.loads(sys.argv[1])
# proxy is not working now
self.proxy = rpc_request.get("proxy",{})
request_method_name = rpc_request.get("method")
request_parameters = rpc_request.get("parameters")
methods = inspect.getmembers(self, predicate=inspect.ismethod)
request_method = dict(methods)[request_method_name]
results = request_method(*request_parameters)
if request_method_name == "query" or request_method_name == "context_menu":
print(json.dumps({"result": results}))
def query(self,query):
"""
sub class need to override this method
"""
return []
def context_menu(self, data):
"""
optional context menu entries for a result
"""
return []
def debug(self,msg):
"""
alert msg
"""
print("DEBUG:{}".format(msg))
sys.exit()
class FlowLauncherAPI(object):
@classmethod
def change_query(cls,query,requery = False):
"""
change flowlauncher query
"""
print(json.dumps({"method": "Flow.Launcher.ChangeQuery","parameters":[query,requery]}))
@classmethod
def shell_run(cls,cmd):
"""
run shell commands
"""
print(json.dumps({"method": "Flow.Launcher.ShellRun","parameters":[cmd]}))
@classmethod
def close_app(cls):
"""
close flowlauncher
"""
print(json.dumps({"method": "Flow.Launcher.CloseApp","parameters":[]}))
@classmethod
def hide_app(cls):
"""
hide flowlauncher
"""
print(json.dumps({"method": "Flow.Launcher.HideApp","parameters":[]}))
@classmethod
def show_app(cls):
"""
show flowlauncher
"""
print(json.dumps({"method": "Flow.Launcher.ShowApp","parameters":[]}))
@classmethod
def show_msg(cls,title,sub_title,ico_path=""):
"""
show messagebox
"""
print(json.dumps({"method": "Flow.Launcher.ShowMsg","parameters":[title,sub_title,ico_path]}))
@classmethod
def open_setting_dialog(cls):
"""
open setting dialog
"""
print(json.dumps({"method": "Flow.Launcher.OpenSettingDialog","parameters":[]}))
@classmethod
def start_loadingbar(cls):
"""
start loading animation in flowlauncher
"""
print(json.dumps({"method": "Flow.Launcher.StartLoadingBar","parameters":[]}))
@classmethod
def stop_loadingbar(cls):
"""
stop loading animation in flowlauncher
"""
print(json.dumps({"method": "Flow.Launcher.StopLoadingBar","parameters":[]}))
@classmethod
def reload_plugins(cls):
"""
reload all flowlauncher plugins
"""
print(json.dumps({"method": "Flow.Launcher.ReloadPlugins","parameters":[]}))

View file

@ -1,5 +1,6 @@
The MIT License (MIT)
Copyright (c) 2019 Flow-Launcher
Copyright (c) 2015 Wox
Permission is hereby granted, free of charge, to any person obtaining a copy of

View file

@ -12,5 +12,5 @@
<system:String x:Key="flowlauncher_plugin_calculator_decimal_seperator_use_system_locale">Použiť podľa systému</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_decimal_seperator_comma">Čiarka (,)</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_decimal_seperator_dot">Bodka (.)</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_max_decimal_places">Max. desatinných miest</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_max_decimal_places">Desatinné miesta</system:String>
</ResourceDictionary>

View file

@ -4,7 +4,7 @@
"Name": "Calculator",
"Description": "Provide mathematical calculations.(Try 5*3-2 in Flow Launcher)",
"Author": "cxfksword",
"Version": "1.1.2",
"Version": "1.1.3",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.Caculator.dll",

View file

@ -1,101 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<ProjectGuid>{F35190AA-4758-4D9E-A193-E3BDF6AD3567}</ProjectGuid>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Flow.Launcher.Plugin.Color</RootNamespace>
<AssemblyName>Flow.Launcher.Plugin.Color</AssemblyName>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.Color\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\..\Output\Release\Plugins\Flow.Launcher.Plugin.Color\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<None Include="Images\color.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<None Include="plugin.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj" />
<ProjectReference Include="..\..\Flow.Launcher.Plugin\Flow.Launcher.Plugin.csproj" />
</ItemGroup>
<ItemGroup>
<Content Include="Languages\en.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Content Include="Languages\zh-cn.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Content Include="Languages\zh-tw.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Content Include="Languages\de.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Content Include="Languages\pl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Content Include="Languages\tr.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -1,8 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="flowlauncher_plugin_color_plugin_name">Farben</system:String>
<system:String x:Key="flowlauncher_plugin_color_plugin_description">Stellt eine HEX-Farben Vorschau bereit. (Versuche #000 in Flow Launcher)</system:String>
</ResourceDictionary>

View file

@ -1,8 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="flowlauncher_plugin_color_plugin_name">Colors</system:String>
<system:String x:Key="flowlauncher_plugin_color_plugin_description">Allows to preview colors using hex values.(Try #000 in Flow Launcher)</system:String>
</ResourceDictionary>

View file

@ -1,8 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="flowlauncher_plugin_color_plugin_name">Kolory</system:String>
<system:String x:Key="flowlauncher_plugin_color_plugin_description">Podgląd kolorów po wpisaniu ich kodu szesnastkowego. (Spróbuj wpisać #000 w oknie Flow Launchera)</system:String>
</ResourceDictionary>

View file

@ -1,8 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="flowlauncher_plugin_color_plugin_name">Farby</system:String>
<system:String x:Key="flowlauncher_plugin_color_plugin_description">Zobrazuje náhľad farieb v HEX formáte. (Skúste #000 vo Flow Launcheri)</system:String>
</ResourceDictionary>

View file

@ -1,8 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="flowlauncher_plugin_color_plugin_name">Renkler</system:String>
<system:String x:Key="flowlauncher_plugin_color_plugin_description">Hex kodunu girdiğiniz renkleri görüntülemeye yarar.(#000 yazmayı deneyin)</system:String>
</ResourceDictionary>

View file

@ -1,8 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="flowlauncher_plugin_color_plugin_name">颜色</system:String>
<system:String x:Key="flowlauncher_plugin_color_plugin_description">提供在Flow Launcher查询hex颜色。(尝试在Flow Launcher中输入#000)</system:String>
</ResourceDictionary>

View file

@ -1,7 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="flowlauncher_plugin_color_plugin_name">顏色</system:String>
<system:String x:Key="flowlauncher_plugin_color_plugin_description">提供在 Flow Launcher 查詢 hex 顏色。(試著在 Flow Launcher 中輸入 #000)</system:String>
</ResourceDictionary>

View file

@ -1,121 +0,0 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Windows;
namespace Flow.Launcher.Plugin.Color
{
public sealed class ColorsPlugin : IPlugin, IPluginI18n
{
private string DIR_PATH = Path.Combine(Path.GetTempPath(), @"Plugins\Colors\");
private PluginInitContext context;
private const int IMG_SIZE = 32;
private DirectoryInfo ColorsDirectory { get; set; }
public ColorsPlugin()
{
if (!Directory.Exists(DIR_PATH))
{
ColorsDirectory = Directory.CreateDirectory(DIR_PATH);
}
else
{
ColorsDirectory = new DirectoryInfo(DIR_PATH);
}
}
public List<Result> Query(Query query)
{
var raw = query.Search;
if (!IsAvailable(raw)) return new List<Result>(0);
try
{
var cached = Find(raw);
if (cached.Length == 0)
{
var path = CreateImage(raw);
return new List<Result>
{
new Result
{
Title = raw,
IcoPath = path,
Action = _ =>
{
Clipboard.SetText(raw);
return true;
}
}
};
}
return cached.Select(x => new Result
{
Title = raw,
IcoPath = x.FullName,
Action = _ =>
{
Clipboard.SetText(raw);
return true;
}
}).ToList();
}
catch (Exception exception)
{
// todo: log
return new List<Result>(0);
}
}
private bool IsAvailable(string query)
{
// todo: rgb, names
var length = query.Length - 1; // minus `#` sign
return query.StartsWith("#") && (length == 3 || length == 6);
}
public FileInfo[] Find(string name)
{
var file = string.Format("{0}.png", name.Substring(1));
return ColorsDirectory.GetFiles(file, SearchOption.TopDirectoryOnly);
}
private string CreateImage(string name)
{
using (var bitmap = new Bitmap(IMG_SIZE, IMG_SIZE))
using (var graphics = Graphics.FromImage(bitmap))
{
var color = ColorTranslator.FromHtml(name);
graphics.Clear(color);
var path = CreateFileName(name);
bitmap.Save(path, ImageFormat.Png);
return path;
}
}
private string CreateFileName(string name)
{
return string.Format("{0}{1}.png", ColorsDirectory.FullName, name.Substring(1));
}
public void Init(PluginInitContext context)
{
this.context = context;
}
public string GetTranslatedPluginTitle()
{
return context.API.GetTranslation("flowlauncher_plugin_color_plugin_name");
}
public string GetTranslatedPluginDescription()
{
return context.API.GetTranslation("flowlauncher_plugin_color_plugin_description");
}
}
}

View file

@ -1,12 +0,0 @@
{
"ID": "9B36CE6181FC47FBB597AA2C29CD9B0A",
"ActionKeyword": "*",
"Name": "Colors",
"Description": "Provide hex color preview.(Try #000 in Flow Launcher)",
"Author": "qianlifeng",
"Version": "1.1.1",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.Color.dll",
"IcoPath": "Images\\color.png"
}

View file

@ -1,102 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<ProjectGuid>{049490F0-ECD2-4148-9B39-2135EC346EBE}</ProjectGuid>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Flow.Launcher.Plugin.PluginManagement</RootNamespace>
<AssemblyName>Flow.Launcher.Plugin.PluginManagement</AssemblyName>
<UseWindowsForms>true</UseWindowsForms>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.PluginManagement\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\..\Output\Release\Plugins\Flow.Launcher.Plugin.PluginManagement\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj" />
<ProjectReference Include="..\..\Flow.Launcher.Plugin\Flow.Launcher.Plugin.csproj" />
</ItemGroup>
<ItemGroup>
<None Include="plugin.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<None Include="Images\plugin.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Content Include="Languages\en.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Content Include="Languages\zh-cn.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Content Include="Languages\zh-tw.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Content Include="Languages\de.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Content Include="Languages\pl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Content Include="Languages\tr.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

View file

@ -1,11 +0,0 @@
namespace Flow.Launcher.Plugin.PluginManagement
{
public class FlowLauncherPluginResult
{
public string plugin_file;
public string description;
public int liked_count;
public string name;
public string version;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 269 B

View file

@ -1,8 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_name">Flow Launcher Plugin Verwaltung</system:String>
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_description">Installiere/Entferne/Aktualisiere Flow Launcher Plugins</system:String>
</ResourceDictionary>

View file

@ -1,8 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_name">Plugin Management</system:String>
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_description">Install, remove or update Flow Launcher plugins</system:String>
</ResourceDictionary>

View file

@ -1,8 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_name">Zarządzanie wtyczkami Flow Launcher</system:String>
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_description">Pozwala na instalacje, usuwanie i aktualizacje wtyczek</system:String>
</ResourceDictionary>

View file

@ -1,8 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_name">Správca pluginov</system:String>
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_description">Inštalácia, odinštalácia alebo aktualizácia pluginov Flow Launchera</system:String>
</ResourceDictionary>

View file

@ -1,8 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_name">Flow Launcher Eklenti Yöneticisi</system:String>
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_description">Flow Launcher eklentilerini kurun, kaldırın ya da güncelleyin</system:String>
</ResourceDictionary>

View file

@ -1,8 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_name">Flow Launcher插件管理</system:String>
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_description">安装/卸载/更新Flow Launcher插件</system:String>
</ResourceDictionary>

View file

@ -1,8 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_name">Flow Launcher 外掛管理</system:String>
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_description">安裝/解除安裝/更新 Flow Launcher 外掛</system:String>
</ResourceDictionary>

View file

@ -1,258 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using Newtonsoft.Json;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Http;
using Flow.Launcher.Infrastructure.Logger;
namespace Flow.Launcher.Plugin.PluginManagement
{
public class Main : IPlugin, IPluginI18n
{
private static string APIBASE = "http://api.wox.one";
private static string pluginSearchUrl = APIBASE + "/plugin/search/";
private const string ListCommand = "list";
private const string InstallCommand = "install";
private const string UninstallCommand = "uninstall";
private PluginInitContext context;
public List<Result> Query(Query query)
{
List<Result> results = new List<Result>();
if (string.IsNullOrEmpty(query.Search))
{
results.Add(ResultForListCommandAutoComplete(query));
results.Add(ResultForInstallCommandAutoComplete(query));
results.Add(ResultForUninstallCommandAutoComplete(query));
return results;
}
string command = query.FirstSearch.ToLower();
if (string.IsNullOrEmpty(command)) return results;
if (command == ListCommand)
{
return ResultForListInstalledPlugins();
}
if (command == UninstallCommand)
{
return ResultForUnInstallPlugin(query);
}
if (command == InstallCommand)
{
return ResultForInstallPlugin(query);
}
if (InstallCommand.Contains(command))
{
results.Add(ResultForInstallCommandAutoComplete(query));
}
if (UninstallCommand.Contains(command))
{
results.Add(ResultForUninstallCommandAutoComplete(query));
}
if (ListCommand.Contains(command))
{
results.Add(ResultForListCommandAutoComplete(query));
}
return results;
}
private Result ResultForListCommandAutoComplete(Query query)
{
string title = ListCommand;
string subtitle = "list installed plugins";
return ResultForCommand(query, ListCommand, title, subtitle);
}
private Result ResultForInstallCommandAutoComplete(Query query)
{
string title = $"{InstallCommand} <Package Name>";
string subtitle = "list installed plugins";
return ResultForCommand(query, InstallCommand, title, subtitle);
}
private Result ResultForUninstallCommandAutoComplete(Query query)
{
string title = $"{UninstallCommand} <Package Name>";
string subtitle = "list installed plugins";
return ResultForCommand(query, UninstallCommand, title, subtitle);
}
private Result ResultForCommand(Query query, string command, string title, string subtitle)
{
const string seperater = Plugin.Query.TermSeperater;
var result = new Result
{
Title = title,
IcoPath = "Images\\plugin.png",
SubTitle = subtitle,
Action = e =>
{
context.API.ChangeQuery($"{query.ActionKeyword}{seperater}{command}{seperater}");
return false;
}
};
return result;
}
private List<Result> ResultForInstallPlugin(Query query)
{
List<Result> results = new List<Result>();
string pluginName = query.SecondSearch;
if (string.IsNullOrEmpty(pluginName)) return results;
string json;
try
{
json = Http.Get(pluginSearchUrl + pluginName).Result;
}
catch (WebException e)
{
//todo happlebao add option in log to decide give user prompt or not
context.API.ShowMsg("PluginManagement.ResultForInstallPlugin: Can't connect to Wox plugin website, check your conenction");
Log.Exception("|PluginManagement.ResultForInstallPlugin|Can't connect to Wox plugin website, check your conenction", e);
return new List<Result>();
}
List<FlowLauncherPluginResult> searchedPlugins;
try
{
searchedPlugins = JsonConvert.DeserializeObject<List<FlowLauncherPluginResult>>(json);
}
catch (JsonSerializationException e)
{
context.API.ShowMsg("PluginManagement.ResultForInstallPlugin: Coundn't parse api search results, Please update your Flow Launcher!");
Log.Exception("|PluginManagement.ResultForInstallPlugin|Coundn't parse api search results, Please update your Flow Launcher!", e);
return results;
}
foreach (FlowLauncherPluginResult r in searchedPlugins)
{
FlowLauncherPluginResult r1 = r;
results.Add(new Result
{
Title = r.name,
SubTitle = r.description,
IcoPath = "Images\\plugin.png",
TitleHighlightData = StringMatcher.FuzzySearch(query.SecondSearch, r.name).MatchData,
SubTitleHighlightData = StringMatcher.FuzzySearch(query.SecondSearch, r.description).MatchData,
Action = c =>
{
MessageBoxResult result = MessageBox.Show("Are you sure you wish to install the \'" + r.name + "\' plugin",
"Install plugin", MessageBoxButton.YesNo);
if (result == MessageBoxResult.Yes)
{
string folder = Path.Combine(Path.GetTempPath(), "FlowLauncherPluginDownload");
if (!Directory.Exists(folder)) Directory.CreateDirectory(folder);
string filePath = Path.Combine(folder, Guid.NewGuid().ToString() + ".flowlauncher");
string pluginUrl = APIBASE + "/media/" + r1.plugin_file;
try
{
Http.Download(pluginUrl, filePath);
}
catch (WebException e)
{
context.API.ShowMsg($"PluginManagement.ResultForInstallPlugin: download failed for <{r.name}>");
Log.Exception($"|PluginManagement.ResultForInstallPlugin|download failed for <{r.name}>", e);
return false;
}
context.API.InstallPlugin(filePath);
}
return false;
}
});
}
return results;
}
private List<Result> ResultForUnInstallPlugin(Query query)
{
List<Result> results = new List<Result>();
List<PluginMetadata> allInstalledPlugins = context.API.GetAllPlugins().Select(o => o.Metadata).ToList();
if (!string.IsNullOrEmpty(query.SecondSearch))
{
allInstalledPlugins =
allInstalledPlugins.Where(o => o.Name.ToLower().Contains(query.SecondSearch.ToLower())).ToList();
}
foreach (PluginMetadata plugin in allInstalledPlugins)
{
results.Add(new Result
{
Title = plugin.Name,
SubTitle = plugin.Description,
IcoPath = plugin.IcoPath,
TitleHighlightData = StringMatcher.FuzzySearch(query.SecondSearch, plugin.Name).MatchData,
SubTitleHighlightData = StringMatcher.FuzzySearch(query.SecondSearch, plugin.Description).MatchData,
Action = e =>
{
UnInstallPlugin(plugin);
return false;
}
});
}
return results;
}
private void UnInstallPlugin(PluginMetadata plugin)
{
string content = $"Do you want to uninstall following plugin?{Environment.NewLine}{Environment.NewLine}" +
$"Name: {plugin.Name}{Environment.NewLine}" +
$"Version: {plugin.Version}{Environment.NewLine}" +
$"Author: {plugin.Author}";
if (MessageBox.Show(content, "Flow Launcher", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
File.Create(Path.Combine(plugin.PluginDirectory, "NeedDelete.txt")).Close();
var result = MessageBox.Show($"You have uninstalled plugin {plugin.Name} successfully.{Environment.NewLine}" +
"Restart Flow Launcher to take effect?",
"Install plugin", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
context.API.RestartApp();
}
}
}
private List<Result> ResultForListInstalledPlugins()
{
List<Result> results = new List<Result>();
foreach (PluginMetadata plugin in context.API.GetAllPlugins().Select(o => o.Metadata))
{
string actionKeywordString = string.Join(" or ", plugin.ActionKeywords.ToArray());
results.Add(new Result
{
Title = $"{plugin.Name} - Action Keywords: {actionKeywordString}",
SubTitle = plugin.Description,
IcoPath = plugin.IcoPath
});
}
return results;
}
public void Init(PluginInitContext context)
{
this.context = context;
}
public string GetTranslatedPluginTitle()
{
return context.API.GetTranslation("flowlauncher_plugin_plugin_management_plugin_name");
}
public string GetTranslatedPluginDescription()
{
return context.API.GetTranslation("flowlauncher_plugin_plugin_management_plugin_description");
}
}
}

View file

@ -1,12 +0,0 @@
{
"ID": "D2D2C23B084D422DB66FE0C79D6C2A6A",
"ActionKeyword": "wpm",
"Name": "Plugin Management",
"Description": "Install/Remove/Update Flow Launcher plugins",
"Author": "qianlifeng",
"Version": "1.1.1",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.PluginManagement.dll",
"IcoPath": "Images\\plugin.png"
}

View file

@ -0,0 +1,76 @@
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin.PluginsManager.Models;
using System;
using System.Collections.Generic;
using System.Text;
namespace Flow.Launcher.Plugin.PluginsManager
{
internal class ContextMenu : IContextMenu
{
private PluginInitContext Context { get; set; }
public ContextMenu(PluginInitContext context)
{
Context = context;
}
public List<Result> LoadContextMenus(Result selectedResult)
{
var pluginManifestInfo = selectedResult.ContextData as UserPlugin;
return new List<Result>
{
new Result
{
Title = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_openwebsite_title"),
SubTitle = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_openwebsite_subtitle"),
IcoPath = "Images\\website.png",
Action = _ =>
{
SharedCommands.SearchWeb.NewTabInBrowser(pluginManifestInfo.Website);
return true;
}
},
new Result
{
Title = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_gotosourcecode_title"),
SubTitle = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_gotosourcecode_subtitle"),
IcoPath = "Images\\sourcecode.png",
Action = _ =>
{
SharedCommands.SearchWeb.NewTabInBrowser(pluginManifestInfo.UrlSourceCode);
return true;
}
},
new Result
{
Title = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_newissue_title"),
SubTitle = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_newissue_subtitle"),
IcoPath = "Images\\request.png",
Action = _ =>
{
// standard UrlSourceCode format in PluginsManifest's plugins.json file: https://github.com/jjw24/WoxDictionary/tree/master
var link = pluginManifestInfo.UrlSourceCode.StartsWith("https://github.com")
? pluginManifestInfo.UrlSourceCode.Replace("/tree/master", "/issues/new/choose")
: pluginManifestInfo.UrlSourceCode;
SharedCommands.SearchWeb.NewTabInBrowser(link);
return true;
}
},
new Result
{
Title = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_pluginsmanifest_title"),
SubTitle = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_pluginsmanifest_subtitle"),
IcoPath = selectedResult.IcoPath,
Action = _ =>
{
SharedCommands.SearchWeb.NewTabInBrowser("https://github.com/Flow-Launcher/Flow.Launcher.PluginsManifest");
return true;
}
}
};
}
}
}

View file

@ -0,0 +1,46 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<OutputPath>..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.PluginsManager</OutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<OutputPath>..\..\Output\Release\Plugins\Flow.Launcher.Plugin.PluginsManager</OutputPath>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj" />
<ProjectReference Include="..\..\Flow.Launcher.Plugin\Flow.Launcher.Plugin.csproj" />
</ItemGroup>
<ItemGroup>
<None Include="plugin.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Content Include="Images\**">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Content Include="Languages\**">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="SharpZipLib" Version="1.2.0" />
</ItemGroup>
</Project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 484 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

View file

@ -0,0 +1,37 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<!--Dialogues-->
<system:String x:Key="plugin_pluginsmanager_downloading_plugin">Downloading plugin</system:String>
<system:String x:Key="plugin_pluginsmanager_please_wait">Please wait...</system:String>
<system:String x:Key="plugin_pluginsmanager_download_success">Successfully downloaded</system:String>
<system:String x:Key="plugin_pluginsmanager_uninstall_prompt">{0} by {1} {2}{3}Would you like to uninstall this plugin? After the uninstallation Flow will automatically restart.</system:String>
<system:String x:Key="plugin_pluginsmanager_install_prompt">{0} by {1} {2}{3}Would you like to install this plugin? After the installation Flow will automatically restart.</system:String>
<system:String x:Key="plugin_pluginsmanager_install_title">Plugin Install</system:String>
<system:String x:Key="plugin_pluginsmanager_uninstall_title">Plugin Uninstall</system:String>
<system:String x:Key="plugin_pluginsmanager_install_errormetadatafile">Install failed: unable to find the plugin.json metadata file from the new plugin</system:String>
<system:String x:Key="plugin_pluginsmanager_update_noresult_title">No update available</system:String>
<system:String x:Key="plugin_pluginsmanager_update_noresult_subtitle">All plugins are up to date</system:String>
<system:String x:Key="plugin_pluginsmanager_update_prompt">{0} by {1} {2}{3}Would you like to update this plugin? After the update Flow will automatically restart.</system:String>
<system:String x:Key="plugin_pluginsmanager_update_title">Plugin Update</system:String>
<system:String x:Key="plugin_pluginsmanager_update_exists">This plugin has an update, would you like to see it?</system:String>
<system:String x:Key="plugin_pluginsmanager_update_alreadyexists">This plugin is already installed</system:String>
<!--Controls-->
<!--Plugin Infos-->
<system:String x:Key="plugin_pluginsmanager_plugin_name">Plugins Manager</system:String>
<system:String x:Key="plugin_pluginsmanager_plugin_description">Management of installing, uninstalling or updating Flow Launcher plugins</system:String>
<!--Context menu items-->
<system:String x:Key="plugin_pluginsmanager_plugin_contextmenu_openwebsite_title">Open website</system:String>
<system:String x:Key="plugin_pluginsmanager_plugin_contextmenu_openwebsite_subtitle">Visit the plugin's website</system:String>
<system:String x:Key="plugin_pluginsmanager_plugin_contextmenu_gotosourcecode_title">See source code</system:String>
<system:String x:Key="plugin_pluginsmanager_plugin_contextmenu_gotosourcecode_subtitle">See the plugin's source code</system:String>
<system:String x:Key="plugin_pluginsmanager_plugin_contextmenu_newissue_title">Suggest an enhancement or submit an issue</system:String>
<system:String x:Key="plugin_pluginsmanager_plugin_contextmenu_newissue_subtitle">Suggest an enhancement or submit an issue to the plugin developer</system:String>
<system:String x:Key="plugin_pluginsmanager_plugin_contextmenu_pluginsmanifest_title">Go to Flow's plugins repository</system:String>
<system:String x:Key="plugin_pluginsmanager_plugin_contextmenu_pluginsmanifest_subtitle">Visit the PluginsManifest repository to see comunity-made plugin submissions</system:String>
</ResourceDictionary>

View file

@ -0,0 +1,91 @@
using Flow.Launcher.Infrastructure.Storage;
using Flow.Launcher.Plugin.PluginsManager.ViewModels;
using Flow.Launcher.Plugin.PluginsManager.Views;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Controls;
using Flow.Launcher.Infrastructure;
using System;
using System.Threading.Tasks;
namespace Flow.Launcher.Plugin.PluginsManager
{
public class Main : ISettingProvider, IPlugin, ISavable, IContextMenu, IPluginI18n
{
internal PluginInitContext Context { get; set; }
internal Settings Settings;
private SettingsViewModel viewModel;
private IContextMenu contextMenu;
internal PluginsManager pluginManager;
private DateTime lastUpdateTime = DateTime.MinValue;
public Control CreateSettingPanel()
{
return new PluginsManagerSettings(viewModel);
}
public void Init(PluginInitContext context)
{
Context = context;
viewModel = new SettingsViewModel(context);
Settings = viewModel.Settings;
contextMenu = new ContextMenu(Context);
pluginManager = new PluginsManager(Context, Settings);
lastUpdateTime = DateTime.Now;
}
public List<Result> LoadContextMenus(Result selectedResult)
{
return contextMenu.LoadContextMenus(selectedResult);
}
public List<Result> Query(Query query)
{
var search = query.Search.ToLower();
if (string.IsNullOrWhiteSpace(search))
return pluginManager.GetDefaultHotKeys();
if ((DateTime.Now - lastUpdateTime).TotalHours > 12) // 12 hours
{
Task.Run(async () =>
{
await pluginManager.UpdateManifest();
lastUpdateTime = DateTime.Now;
});
}
return search switch
{
var s when s.StartsWith(Settings.HotKeyInstall) => pluginManager.RequestInstallOrUpdate(s),
var s when s.StartsWith(Settings.HotkeyUninstall) => pluginManager.RequestUninstall(s),
var s when s.StartsWith(Settings.HotkeyUpdate) => pluginManager.RequestUpdate(s),
_ => pluginManager.GetDefaultHotKeys().Where(hotkey =>
{
hotkey.Score = StringMatcher.FuzzySearch(search, hotkey.Title).Score;
return hotkey.Score > 0;
}).ToList()
};
}
public void Save()
{
viewModel.Save();
}
public string GetTranslatedPluginTitle()
{
return Context.API.GetTranslation("plugin_pluginsmanager_plugin_name");
}
public string GetTranslatedPluginDescription()
{
return Context.API.GetTranslation("plugin_pluginsmanager_plugin_description");
}
}
}

View file

@ -0,0 +1,36 @@
using Flow.Launcher.Infrastructure.Http;
using Flow.Launcher.Infrastructure.Logger;
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading.Tasks;
namespace Flow.Launcher.Plugin.PluginsManager.Models
{
internal class PluginsManifest
{
internal List<UserPlugin> UserPlugins { get; private set; }
internal PluginsManifest()
{
Task.Run(async () => await DownloadManifest()).Wait();
}
internal async Task DownloadManifest()
{
try
{
await using var jsonStream = await Http.GetStreamAsync("https://raw.githubusercontent.com/Flow-Launcher/Flow.Launcher.PluginsManifest/main/plugins.json")
.ConfigureAwait(false);
UserPlugins = await JsonSerializer.DeserializeAsync<List<UserPlugin>>(jsonStream).ConfigureAwait(false);
}
catch (Exception e)
{
Log.Exception("|PluginManagement.GetManifest|Encountered error trying to download plugins manifest", e);
UserPlugins = new List<UserPlugin>();
}
}
}
}

View file

@ -0,0 +1,16 @@

namespace Flow.Launcher.Plugin.PluginsManager.Models
{
public class UserPlugin
{
public string ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Author { get; set; }
public string Version { get; set; }
public string Language { get; set; }
public string Website { get; set; }
public string UrlDownload { get; set; }
public string UrlSourceCode { get; set; }
}
}

View file

@ -0,0 +1,394 @@
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Http;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin.PluginsManager.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace Flow.Launcher.Plugin.PluginsManager
{
internal class PluginsManager
{
private PluginsManifest pluginsManifest;
private PluginInitContext Context { get; set; }
private Settings Settings { get; set; }
private bool shouldHideWindow = true;
private bool ShouldHideWindow
{
set { shouldHideWindow = value; }
get
{
var setValue = shouldHideWindow;
// Default value for hide main window is true. Revert after get call.
// This ensures when set by another method to false, it is only used once.
shouldHideWindow = true;
return setValue;
}
}
private readonly string icoPath = "Images\\pluginsmanager.png";
internal PluginsManager(PluginInitContext context, Settings settings)
{
pluginsManifest = new PluginsManifest();
Context = context;
Settings = settings;
}
internal async Task UpdateManifest()
{
await pluginsManifest.DownloadManifest();
}
internal List<Result> GetDefaultHotKeys()
{
return new List<Result>()
{
new Result()
{
Title = Settings.HotKeyInstall,
IcoPath = icoPath,
Action = _ =>
{
Context.API.ChangeQuery("pm install ");
return false;
}
},
new Result()
{
Title = Settings.HotkeyUninstall,
IcoPath = icoPath,
Action = _ =>
{
Context.API.ChangeQuery("pm uninstall ");
return false;
}
},
new Result()
{
Title = Settings.HotkeyUpdate,
IcoPath = icoPath,
Action = _ =>
{
Context.API.ChangeQuery("pm update ");
return false;
}
}
};
}
internal async Task InstallOrUpdate(UserPlugin plugin)
{
if (PluginExists(plugin.ID))
{
if (Context.API.GetAllPlugins()
.Any(x => x.Metadata.ID == plugin.ID && x.Metadata.Version.CompareTo(plugin.Version) < 0))
{
if (MessageBox.Show(Context.API.GetTranslation("plugin_pluginsmanager_update_exists"),
Context.API.GetTranslation("plugin_pluginsmanager_update_title"),
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
Context
.API
.ChangeQuery(
$"{Context.CurrentPluginMetadata.ActionKeywords.FirstOrDefault()} {Settings.HotkeyUpdate} {plugin.Name}");
Application.Current.MainWindow.Show();
shouldHideWindow = false;
return;
}
Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_update_alreadyexists"));
return;
}
var message = string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_prompt"),
plugin.Name, plugin.Author,
Environment.NewLine, Environment.NewLine);
if (MessageBox.Show(message, Context.API.GetTranslation("plugin_pluginsmanager_install_title"),
MessageBoxButton.YesNo) == MessageBoxResult.No)
return;
var filePath = Path.Combine(DataLocation.PluginsDirectory, $"{plugin.Name}-{plugin.Version}.zip");
try
{
Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"),
Context.API.GetTranslation("plugin_pluginsmanager_please_wait"));
await Http.Download(plugin.UrlDownload, filePath).ConfigureAwait(false);
Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"),
Context.API.GetTranslation("plugin_pluginsmanager_download_success"));
}
catch (Exception e)
{
Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"),
Context.API.GetTranslation("plugin_pluginsmanager_download_success"));
Log.Exception("PluginsManager", "An error occured while downloading plugin", e, "PluginDownload");
}
Install(plugin, filePath);
Context.API.RestartApp();
}
internal List<Result> RequestUpdate(string search)
{
var autocompletedResults = AutoCompleteReturnAllResults(search,
Settings.HotkeyUpdate,
"Update",
"Select a plugin to update");
if (autocompletedResults.Any())
return autocompletedResults;
var uninstallSearch = search.Replace(Settings.HotkeyUpdate, string.Empty).TrimStart();
var resultsForUpdate =
from existingPlugin in Context.API.GetAllPlugins()
join pluginFromManifest in pluginsManifest.UserPlugins
on existingPlugin.Metadata.ID equals pluginFromManifest.ID
where existingPlugin.Metadata.Version.CompareTo(pluginFromManifest.Version) < 0 // if current version precedes manifest version
select
new
{
pluginFromManifest.Name,
pluginFromManifest.Author,
CurrentVersion = existingPlugin.Metadata.Version,
NewVersion = pluginFromManifest.Version,
existingPlugin.Metadata.IcoPath,
PluginExistingMetadata = existingPlugin.Metadata,
PluginNewUserPlugin = pluginFromManifest
};
if (!resultsForUpdate.Any())
return new List<Result>
{
new Result
{
Title = Context.API.GetTranslation("plugin_pluginsmanager_update_noresult_title"),
SubTitle = Context.API.GetTranslation("plugin_pluginsmanager_update_noresult_subtitle"),
IcoPath = icoPath
}
};
var results = resultsForUpdate
.Select(x =>
new Result
{
Title = $"{x.Name} by {x.Author}",
SubTitle = $"Update from version {x.CurrentVersion} to {x.NewVersion}",
IcoPath = x.IcoPath,
Action = e =>
{
string message = string.Format(
Context.API.GetTranslation("plugin_pluginsmanager_update_prompt"),
x.Name, x.Author,
Environment.NewLine, Environment.NewLine);
if (MessageBox.Show(message,
Context.API.GetTranslation("plugin_pluginsmanager_update_title"),
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
Uninstall(x.PluginExistingMetadata);
var downloadToFilePath = Path.Combine(DataLocation.PluginsDirectory,
$"{x.Name}-{x.NewVersion}.zip");
Task.Run(async delegate
{
await Http.Download(x.PluginNewUserPlugin.UrlDownload, downloadToFilePath).ConfigureAwait(false);
Install(x.PluginNewUserPlugin, downloadToFilePath);
Context.API.RestartApp();
});
return true;
}
return false;
}
});
return Search(results, uninstallSearch);
}
internal bool PluginExists(string id)
{
return Context.API.GetAllPlugins().Any(x => x.Metadata.ID == id);
}
internal List<Result> Search(IEnumerable<Result> results, string searchName)
{
if (string.IsNullOrEmpty(searchName))
return results.ToList();
return results
.Where(x =>
{
var matchResult = StringMatcher.FuzzySearch(searchName, x.Title);
if (matchResult.IsSearchPrecisionScoreMet())
x.Score = matchResult.Score;
return matchResult.IsSearchPrecisionScoreMet();
})
.ToList();
}
internal List<Result> RequestInstallOrUpdate(string searchName)
{
var searchNameWithoutKeyword = searchName.Replace(Settings.HotKeyInstall, string.Empty).Trim();
var results =
pluginsManifest
.UserPlugins
.Select(x =>
new Result
{
Title = $"{x.Name} by {x.Author}",
SubTitle = x.Description,
IcoPath = icoPath,
Action = e =>
{
Application.Current.MainWindow.Hide();
_ = InstallOrUpdate(x); // No need to wait
return ShouldHideWindow;
},
ContextData = x
});
return Search(results, searchNameWithoutKeyword);
}
private void Install(UserPlugin plugin, string downloadedFilePath)
{
if (!File.Exists(downloadedFilePath))
return;
var tempFolderPath = Path.Combine(Path.GetTempPath(), "flowlauncher");
var tempFolderPluginPath = Path.Combine(tempFolderPath, "plugin");
if (Directory.Exists(tempFolderPath))
Directory.Delete(tempFolderPath, true);
Directory.CreateDirectory(tempFolderPath);
var zipFilePath = Path.Combine(tempFolderPath, Path.GetFileName(downloadedFilePath));
File.Move(downloadedFilePath, zipFilePath);
Utilities.UnZip(zipFilePath, tempFolderPluginPath, true);
var pluginFolderPath = Utilities.GetContainingFolderPathAfterUnzip(tempFolderPluginPath);
var metadataJsonFilePath = string.Empty;
if (File.Exists(Path.Combine(pluginFolderPath, Constant.PluginMetadataFileName)))
metadataJsonFilePath = Path.Combine(pluginFolderPath, Constant.PluginMetadataFileName);
if (string.IsNullOrEmpty(metadataJsonFilePath) || string.IsNullOrEmpty(pluginFolderPath))
{
MessageBox.Show(Context.API.GetTranslation("plugin_pluginsmanager_install_errormetadatafile"));
return;
}
string newPluginPath = Path.Combine(DataLocation.PluginsDirectory, $"{plugin.Name}-{plugin.Version}");
Directory.Move(pluginFolderPath, newPluginPath);
}
internal List<Result> RequestUninstall(string search)
{
var autocompletedResults = AutoCompleteReturnAllResults(search,
Settings.HotkeyUninstall,
"Uninstall",
"Select a plugin to uninstall");
if (autocompletedResults.Any())
return autocompletedResults;
var uninstallSearch = search.Replace(Settings.HotkeyUninstall, string.Empty).TrimStart();
var results = Context.API
.GetAllPlugins()
.Select(x =>
new Result
{
Title = $"{x.Metadata.Name} by {x.Metadata.Author}",
SubTitle = x.Metadata.Description,
IcoPath = x.Metadata.IcoPath,
Action = e =>
{
string message = string.Format(
Context.API.GetTranslation("plugin_pluginsmanager_uninstall_prompt"),
x.Metadata.Name, x.Metadata.Author,
Environment.NewLine, Environment.NewLine);
if (MessageBox.Show(message,
Context.API.GetTranslation("plugin_pluginsmanager_uninstall_title"),
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
Application.Current.MainWindow.Hide();
Uninstall(x.Metadata);
Context.API.RestartApp();
return true;
}
return false;
}
});
return Search(results, uninstallSearch);
}
private void Uninstall(PluginMetadata plugin)
{
// Marked for deletion. Will be deleted on next start up
using var _ = File.CreateText(Path.Combine(plugin.PluginDirectory, "NeedDelete.txt"));
}
private List<Result> AutoCompleteReturnAllResults(string search, string hotkey, string title, string subtitle)
{
if (!string.IsNullOrEmpty(search)
&& hotkey.StartsWith(search)
&& (hotkey != search || !search.StartsWith(hotkey)))
{
return
new List<Result>
{
new Result
{
Title = title,
IcoPath = icoPath,
SubTitle = subtitle,
Action = e =>
{
Context
.API
.ChangeQuery(
$"{Context.CurrentPluginMetadata.ActionKeywords.FirstOrDefault()} {hotkey} ");
return false;
}
}
};
}
return new List<Result>();
}
}
}

View file

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Flow.Launcher.Plugin.PluginsManager
{
internal class Settings
{
internal string HotKeyInstall { get; set; } = "install";
internal string HotkeyUninstall { get; set; } = "uninstall";
internal string HotkeyUpdate { get; set; } = "update";
}
}

View file

@ -0,0 +1,61 @@
using Flow.Launcher.Infrastructure.Http;
using ICSharpCode.SharpZipLib.Zip;
using System.IO;
using System.Net;
namespace Flow.Launcher.Plugin.PluginsManager
{
internal static class Utilities
{
/// <summary>
/// Unzip contents to the given directory.
/// </summary>
/// <param name="zipFilePath">The path to the zip file.</param>
/// <param name="strDirectory">The output directory.</param>
/// <param name="overwrite">overwrite</param>
internal static void UnZip(string zipFilePath, string strDirectory, bool overwrite)
{
if (strDirectory == "")
strDirectory = Directory.GetCurrentDirectory();
using var zipStream = new ZipInputStream(File.OpenRead(zipFilePath));
ZipEntry theEntry;
while ((theEntry = zipStream.GetNextEntry()) != null)
{
var pathToZip = theEntry.Name;
var directoryName = string.IsNullOrEmpty(pathToZip) ? "" : Path.GetDirectoryName(pathToZip);
var fileName = Path.GetFileName(pathToZip);
var destinationDir = Path.Combine(strDirectory, directoryName);
var destinationFile = Path.Combine(destinationDir, fileName);
Directory.CreateDirectory(destinationDir);
if (string.IsNullOrEmpty(fileName) || (File.Exists(destinationFile) && !overwrite))
continue;
using var streamWriter = File.Create(destinationFile);
zipStream.CopyTo(streamWriter);
}
}
internal static string GetContainingFolderPathAfterUnzip(string unzippedParentFolderPath)
{
var unzippedFolderCount = Directory.GetDirectories(unzippedParentFolderPath).Length;
var unzippedFilesCount = Directory.GetFiles(unzippedParentFolderPath).Length;
// adjust path depending on how the plugin is zipped up
// the recommended should be to zip up the folder not the contents
if (unzippedFolderCount == 1 && unzippedFilesCount == 0)
// folder is zipped up, unzipped plugin directory structure: tempPath/unzippedParentPluginFolder/pluginFolderName/
return Directory.GetDirectories(unzippedParentFolderPath)[0];
if (unzippedFilesCount > 1)
// content is zipped up, unzipped plugin directory structure: tempPath/unzippedParentPluginFolder/
return unzippedParentFolderPath;
return string.Empty;
}
}
}

View file

@ -0,0 +1,26 @@
using Flow.Launcher.Infrastructure.Storage;
using Flow.Launcher.Infrastructure.UserSettings;
namespace Flow.Launcher.Plugin.PluginsManager.ViewModels
{
public class SettingsViewModel
{
private readonly PluginJsonStorage<Settings> storage;
internal Settings Settings { get; set; }
internal PluginInitContext Context { get; set; }
public SettingsViewModel(PluginInitContext context)
{
Context = context;
storage = new PluginJsonStorage<Settings>();
Settings = storage.Load();
}
public void Save()
{
storage.Save();
}
}
}

View file

@ -0,0 +1,12 @@
<UserControl x:Class="Flow.Launcher.Plugin.PluginsManager.Views.PluginsManagerSettings"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Flow.Launcher.Plugin.PluginsManager.ViewModels"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
</Grid>
</UserControl>

View file

@ -0,0 +1,22 @@

using Flow.Launcher.Plugin.PluginsManager.ViewModels;
namespace Flow.Launcher.Plugin.PluginsManager.Views
{
/// <summary>
/// Interaction logic for PluginsManagerSettings.xaml
/// </summary>
public partial class PluginsManagerSettings
{
private readonly SettingsViewModel viewModel;
public PluginsManagerSettings(SettingsViewModel viewModel)
{
InitializeComponent();
this.viewModel = viewModel;
//RefreshView();
}
}
}

View file

@ -0,0 +1,14 @@
{
"ID": "9f8f9b14-2518-4907-b211-35ab6290dee7",
"ActionKeywords": [
"pm"
],
"Name": "Plugins Manager",
"Description": "Management of installing, uninstalling or updating Flow Launcher plugins",
"Author": "Jeremy Wu",
"Version": "1.3.1",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.PluginsManager.dll",
"IcoPath": "Images\\pluginsmanager.png"
}

View file

@ -40,7 +40,12 @@
<system:String x:Key="flowlauncher_plugin_program_plugin_description">Vyhľadávanie programov vo Flow Launcheri</system:String>
<system:String x:Key="flowlauncher_plugin_program_invalid_path">Neplatná cesta</system:String>
<system:String x:Key="flowlauncher_plugin_program_customizedexplorer">Vlastný správca súborov</system:String>
<system:String x:Key="flowlauncher_plugin_program_args">Arg.</system:String>
<system:String x:Key="flowlauncher_plugin_program_tooltip_customizedexplorer">Môžete si prispôsobiť otváranie umiestnenia priečinka vložením Premenných prostredia, ktoré chcete použiť. Dostupnosť premenných prostredia môžete vyskúšať cez príkazový riadok.</system:String>
<system:String x:Key="flowlauncher_plugin_program_tooltip_args">Zadajte argumenty, ktoré chcete pridať pre správcu súborov. %s pre rodičovský priečinok, %f pre celú cestu (funguje iba pre win32). Pre podrobnosti pozrite webovú stránku správcu súborov.</system:String>
<!--Dialogs-->
<system:String x:Key="flowlauncher_plugin_program_disable_dlgtitle_success">Úspešné</system:String>
<system:String x:Key="flowlauncher_plugin_program_disable_dlgtitle_success_message">Úspešne zakázané zobrazovanie tohto programu vo výsledkoch vyhľadávania</system:String>

View file

@ -4,7 +4,7 @@
"Name": "Program",
"Description": "Search programs in Flow.Launcher",
"Author": "qianlifeng",
"Version": "1.2.1",
"Version": "1.2.2",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.Program.dll",

View file

@ -196,7 +196,8 @@ namespace Flow.Launcher.Plugin.WebSearch
[JsonIgnore]
public SuggestionSource[] Suggestions { get; set; } = {
new Google(),
new Baidu()
new Baidu(),
new Bing()
};
[JsonIgnore]

View file

@ -8,6 +8,7 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Flow.Launcher.Infrastructure.Http;
using Flow.Launcher.Infrastructure.Logger;
using System.Net.Http;
namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
{
@ -22,9 +23,9 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
try
{
const string api = "http://suggestion.baidu.com/su?json=1&wd=";
result = await Http.Get(api + Uri.EscapeUriString(query), "GB2312");
result = await Http.GetAsync(api + Uri.EscapeUriString(query)).ConfigureAwait(false);
}
catch (WebException e)
catch (HttpRequestException e)
{
Log.Exception("|Baidu.Suggestions|Can't get suggestion from baidu", e);
return new List<string>();

View file

@ -0,0 +1,62 @@
using Flow.Launcher.Infrastructure.Http;
using Flow.Launcher.Infrastructure.Logger;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Text.Json;
using System.Linq;
namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
{
class Bing : SuggestionSource
{
public override async Task<List<string>> Suggestions(string query)
{
Stream resultStream;
try
{
const string api = "https://api.bing.com/qsonhs.aspx?q=";
resultStream = await Http.GetStreamAsync(api + Uri.EscapeUriString(query)).ConfigureAwait(false);
}
catch (HttpRequestException e)
{
Log.Exception("|Bing.Suggestions|Can't get suggestion from Bing", e);
return new List<string>();
}
if (resultStream.Length == 0) return new List<string>();
JsonElement json;
try
{
json = (await JsonDocument.ParseAsync(resultStream)).RootElement.GetProperty("AS");
}
catch (JsonException e)
{
Log.Exception("|Bing.Suggestions|can't parse suggestions", e);
return new List<string>();
}
if (json.GetProperty("FullResults").GetInt32() == 0)
return new List<string>();
return json.GetProperty("Results")
.EnumerateArray()
.SelectMany(r => r.GetProperty("Suggests")
.EnumerateArray()
.Select(s => s.GetProperty("Txt").GetString()))
.ToList();
}
public override string ToString()
{
return "Bing";
}
}
}

View file

@ -7,6 +7,7 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Flow.Launcher.Infrastructure.Http;
using Flow.Launcher.Infrastructure.Logger;
using System.Net.Http;
namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
{
@ -18,13 +19,12 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
try
{
const string api = "https://www.google.com/complete/search?output=chrome&q=";
result = await Http.Get(api + Uri.EscapeUriString(query));
result = await Http.GetAsync(api + Uri.EscapeUriString(query)).ConfigureAwait(false);
}
catch (WebException e)
catch (HttpRequestException e)
{
Log.Exception("|Google.Suggestions|Can't get suggestion from google", e);
return new List<string>();
;
}
if (string.IsNullOrEmpty(result)) return new List<string>();
JContainer json;

View file

@ -25,7 +25,7 @@
"Name": "Web Searches",
"Description": "Provide the web search ability",
"Author": "qianlifeng",
"Version": "1.1.2",
"Version": "1.2.0",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.WebSearch.dll",

View file

@ -1,5 +1,7 @@
<p align="center">
<img width="500px" src="https://github.com/Flow-Launcher/Flow.Launcher/blob/5ba4514f31e624c679628d4dfe89036c0e24006c/Doc/Logo/resources/flow-header-square-transparent.png">
<a href="https://flow-launcher.github.io">
<img width="500px" src="https://github.com/Flow-Launcher/Flow.Launcher/blob/5ba4514f31e624c679628d4dfe89036c0e24006c/Doc/Logo/resources/flow-header-square-transparent.png">
</a>
</p>
![Maintenance](https://img.shields.io/maintenance/yes/2020)
@ -17,7 +19,7 @@ Flow Launcher. Dedicated to make your workflow flow more seamlessly. Aimed at be
![The Flow](https://user-images.githubusercontent.com/26427004/82151677-fa9c7100-989f-11ea-9143-81de60aaf07d.gif)
- Search everything from applications, files, bookmarks, YouTube, Twitter and more. All from the comfort of your keyboard without ever touching the mouse.
- Search for file contents
- Search for file contents.
- Support search using environment variable paths
- Run batch and PowerShell commands as Administrator or a different user.
- Support languages from Chinese to Italian and more.
@ -26,20 +28,24 @@ Flow Launcher. Dedicated to make your workflow flow more seamlessly. Aimed at be
## Running Flow Launcher
| [Windows 7 and up](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest)
| ------------- |
| [Windows 7 and up](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest) |
| ---------------------------------------------------------------------------------- |
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/): `.exe` installer + add it to `%PATH%` or set it in flow's settings
- 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)
**Integrations**
- If you use Python plugins:
- Install [Python3](https://www.python.org/downloads/), download `.exe` installer.
- Add Python to `%PATH%` or set it in flow's settings.
- Use `pip` to install `flowlauncher`, cmd in `pip install flowlauncher`.
- 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).
**Usage**
- Open flow's search window: <kbd>Alt</kbd>+<kbd>Space</kbd> is the default hotkey
- Open context menu: <kbd>Ctrl</kbd>+<kbd>O</kbd>/<kbd>Shift</kbd>+<kbd>Enter</kbd>
- Cancel/Return to previous screen: <kbd>Esc</kbd>
- Install/Uninstall plugins: in the search window, type `wpm install/uninstall` + the plugin name
- Open flow's search window: <kbd>Alt</kbd>+<kbd>Space</kbd> is the default hotkey.
- Open context menu: <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.
- Saved user settings are located:
- If using roaming: `%APPDATA%\FlowLauncher`
- If using portable, by default: `%localappdata%\FlowLauncher\app-<VersionOfYourFlowLauncher>\UserData`
@ -74,10 +80,3 @@ Install .Net Core 3.1 SDK via Visual Studio installer or manually from [here](ht
## Documentation
[Wiki](https://github.com/Flow-Launcher/Flow.Launcher/wiki)
## A history of the flow
Flow's roots came from a rebrand of the [JJW24/Wox fork](https://github.com/jjw24/Wox/issues/156) and WoX.
A big thank you and all credits to [Bao](https://github.com/bao-qian), the author of WoX, and its contrbutors for all the amazing work.
The JJW24/Wox fork started adding new changes on top of main WoX repo's code base from release v1.3.524. Flow is a continuation of the work from JJW24/Wox

View file

@ -36,7 +36,6 @@ function Copy-Resources ($path, $config) {
$output = "$path\Output"
$target = "$output\$config"
Copy-Item -Recurse -Force $project\Images\* $target\Images\
Copy-Item -Recurse -Force $path\JsonRPC $target\JsonRPC
# making version static as multiple versions can exist in the nuget folder and in the case a breaking change is introduced.
Copy-Item -Force $env:USERPROFILE\.nuget\packages\squirrel.windows\1.5.2\tools\Squirrel.exe $output\Update.exe
}

View file

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

View file

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

6
attribution.md Normal file
View file

@ -0,0 +1,6 @@
Flow's roots came from a rebrand of the [JJW24/Wox fork](https://github.com/jjw24/Wox/issues/156) and [WoX](https://github.com/Wox-launcher/Wox).
A big thank you and all credits to [Bao](https://github.com/bao-qian), the author of WoX, and its contributors for all the amazing work.
The JJW24/Wox fork started adding new changes on top of main WoX repo's code base from release v1.3.524.
Flow is a continuation of the work from JJW24/Wox.