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 *.pyc
*.diagsession *.diagsession
Output-Performance.txt Output-Performance.txt
*.diff *.diff
# vscode
.vscode
.history

View file

@ -55,7 +55,6 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="FSharp.Core" Version="4.7.1" /> <PackageReference Include="FSharp.Core" Version="4.7.1" />
<PackageReference Include="squirrel.windows" Version="1.5.2" /> <PackageReference Include="squirrel.windows" Version="1.5.2" />
<PackageReference Include="SharpZipLib" Version="1.2.0" />
</ItemGroup> </ItemGroup>
<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) public static List<PluginPair> ValidPluginsForQuery(Query query)
{ {
if (NonGlobalPlugins.ContainsKey(query.ActionKeyword)) if (NonGlobalPlugins.ContainsKey(query.ActionKeyword))

View file

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

View file

@ -8,7 +8,6 @@ using System.Threading.Tasks;
using System.Windows; using System.Windows;
using JetBrains.Annotations; using JetBrains.Annotations;
using Squirrel; using Squirrel;
using Newtonsoft.Json;
using Flow.Launcher.Core.Resource; using Flow.Launcher.Core.Resource;
using Flow.Launcher.Plugin.SharedCommands; using Flow.Launcher.Plugin.SharedCommands;
using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure;
@ -17,6 +16,7 @@ using Flow.Launcher.Infrastructure.Logger;
using System.IO; using System.IO;
using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin; using Flow.Launcher.Plugin;
using System.Text.Json.Serialization;
namespace Flow.Launcher.Core namespace Flow.Launcher.Core
{ {
@ -29,101 +29,80 @@ namespace Flow.Launcher.Core
GitHubRepository = gitHubRepository; 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 try
{ {
updateManager = await GitHubUpdateManager(GitHubRepository); UpdateInfo newUpdateInfo;
}
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;
}
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) if (!silentUpdate)
MessageBox.Show("You already have the latest Flow Launcher version"); api.ShowMsg("Please wait...", "Checking for new update");
updateManager.Dispose();
return;
}
if (!silentUpdate) using var updateManager = await GitHubUpdateManager(GitHubRepository).ConfigureAwait(false);
api.ShowMsg("Update found", "Updating...");
try
{ // UpdateApp CheckForUpdate will return value only if the app is squirrel installed
await updateManager.DownloadReleases(newUpdateInfo.ReleasesToApply); 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) 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); 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; 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] [UsedImplicitly]
private class GithubRelease private class GithubRelease
{ {
[JsonProperty("prerelease")] [JsonPropertyName("prerelease")]
public bool Prerelease { get; [UsedImplicitly] set; } public bool Prerelease { get; [UsedImplicitly] set; }
[JsonProperty("published_at")] [JsonPropertyName("published_at")]
public DateTime PublishedAt { get; [UsedImplicitly] set; } public DateTime PublishedAt { get; [UsedImplicitly] set; }
[JsonProperty("html_url")] [JsonPropertyName("html_url")]
public string HtmlUrl { get; [UsedImplicitly] set; } public string HtmlUrl { get; [UsedImplicitly] set; }
} }
@ -133,13 +112,13 @@ namespace Flow.Launcher.Core
var uri = new Uri(repository); var uri = new Uri(repository);
var api = $"https://api.github.com/repos{uri.AbsolutePath}/releases"; 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 latest = releases.Where(r => !r.Prerelease).OrderByDescending(r => r.PublishedAt).First();
var latestUrl = latest.HtmlUrl.Replace("/tag/", "/download/"); 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 downloader = new FileDownloader(client);
var manager = new UpdateManager(latestUrl, urlDownloader: downloader); 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 DefaultTheme = "Darker";
public const string Themes = "Themes"; 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 JetBrains.Annotations;
using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Infrastructure.UserSettings;
using System;
using System.ComponentModel;
namespace Flow.Launcher.Infrastructure.Http 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 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() static Http()
{ {
// need to be added so it would work on a win10 machine // 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 ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls
| SecurityProtocolType.Tls11 | SecurityProtocolType.Tls11
| SecurityProtocolType.Tls12; | SecurityProtocolType.Tls12;
client = new HttpClient(socketsHttpHandler, false);
client.DefaultRequestHeaders.Add("User-Agent", UserAgent);
} }
public static HttpProxy Proxy { private get; set; } private static HttpProxy proxy;
public static IWebProxy WebProxy()
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); true => Proxy.UserName switch
return webProxy;
}
else
{
var webProxy = new WebProxy(Proxy.Server, Proxy.Port)
{ {
Credentials = new NetworkCredential(Proxy.UserName, Proxy.Password) var userName when !string.IsNullOrEmpty(userName) =>
}; (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), null),
return webProxy; _ => (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 else
{ {
return WebRequest.GetSystemWebProxy(); throw new HttpRequestException($"Error code <{response.StatusCode}> returned from <{url}>");
} }
} }
public static void Download([NotNull] string url, [NotNull] string filePath) /// <summary>
{ /// Asynchrously get the result as string from url.
var client = new WebClient { Proxy = WebProxy() }; /// When supposing the result is long and large, try using GetStreamAsync to avoid reading as string
client.Headers.Add("user-agent", UserAgent); /// </summary>
client.DownloadFile(url, filePath); /// <param name="url"></param>
} /// <returns></returns>
public static Task<string> GetAsync([NotNull] string url)
public static async Task<string> Get([NotNull] string url, string encoding = "UTF-8")
{ {
Log.Debug($"|Http.Get|Url <{url}>"); Log.Debug($"|Http.Get|Url <{url}>");
var request = WebRequest.CreateHttp(url); return GetAsync(new Uri(url.Replace("#", "%23")));
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();
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(); return content;
if (response.StatusCode == HttpStatusCode.OK) }
{ else
return content; {
} throw new HttpRequestException(
else $"Error code <{response.StatusCode}> with content <{content}> returned from <{url}>");
{
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; private const int MaxCached = 50;
public ConcurrentDictionary<string, ImageUsage> Data { get; private set; } = new ConcurrentDictionary<string, ImageUsage>(); public ConcurrentDictionary<string, ImageUsage> Data { get; private set; } = new ConcurrentDictionary<string, ImageUsage>();
private const int permissibleFactor = 2; private const int permissibleFactor = 2;
public void Initialization(Dictionary<string, int> usage) public void Initialization(Dictionary<string, int> usage)
{ {
foreach (var key in usage.Keys) foreach (var key in usage.Keys)
@ -44,14 +44,14 @@ namespace Flow.Launcher.Infrastructure.Image
value.usage++; value.usage++;
return value.imageSource; return value.imageSource;
} }
return null; return null;
} }
set set
{ {
Data.AddOrUpdate( Data.AddOrUpdate(
path, path,
new ImageUsage(0, value), new ImageUsage(0, value),
(k, v) => (k, v) =>
{ {
v.imageSource = value; v.imageSource = value;
@ -65,22 +65,15 @@ namespace Flow.Launcher.Infrastructure.Image
if (Data.Count > permissibleFactor * MaxCached) if (Data.Count > permissibleFactor * MaxCached)
{ {
// To delete the images from the data dictionary based on the resizing of the Usage Dictionary. // 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)) foreach (var key in Data.OrderBy(x => x.Value.usage).Take(Data.Count - MaxCached).Select(x => x.Key))
{ Data.TryRemove(key, out _);
if (!(key.Equals(Constant.ErrorIcon) || key.Equals(Constant.DefaultIcon)))
{
Data.TryRemove(key, out _);
}
}
} }
} }
} }
public bool ContainsKey(string key) public bool ContainsKey(string key)
{ {
var contains = Data.ContainsKey(key); var contains = Data.ContainsKey(key) && Data[key] != null;
return contains; return contains;
} }
@ -97,5 +90,4 @@ namespace Flow.Launcher.Infrastructure.Image
return Data.Values.Select(x => x.imageSource).Distinct().Count(); 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 readonly ConcurrentDictionary<string, string> GuidToKey = new ConcurrentDictionary<string, string>();
private static IImageHashGenerator _hashGenerator; private static IImageHashGenerator _hashGenerator;
private static bool EnableImageHash = true; private static bool EnableImageHash = true;
public static ImageSource DefaultImage { get; } = new BitmapImage(new Uri(Constant.MissingImgIcon));
private static readonly string[] ImageExtensions = private static readonly string[] ImageExtensions =
{ {
@ -61,7 +63,7 @@ namespace Flow.Launcher.Infrastructure.Image
{ {
lock (_storage) 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); option);
} }
public static bool CacheContainImage(string path)
{
return ImageCache.ContainsKey(path) && ImageCache[path] != null;
}
public static ImageSource Load(string path, bool loadFullImage = false) public static ImageSource Load(string path, bool loadFullImage = false)
{ {
var imageResult = LoadInternal(path, loadFullImage); var imageResult = LoadInternal(path, loadFullImage);
@ -221,7 +228,7 @@ namespace Flow.Launcher.Infrastructure.Image
string hash = EnableImageHash ? _hashGenerator.GetHashFromImage(img) : null; string hash = EnableImageHash ? _hashGenerator.GetHashFromImage(img) : null;
if (hash != null) if (hash != null)
{ {
if (GuidToKey.TryGetValue(hash, out string key)) if (GuidToKey.TryGetValue(hash, out string key))
{ // image already exists { // image already exists
img = ImageCache[key] ?? img; img = ImageCache[key] ?? img;

View file

@ -1,14 +1,10 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using JetBrains.Annotations; using JetBrains.Annotations;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.Storage;
using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Infrastructure.UserSettings;
using ToolGood.Words.Pinyin; using ToolGood.Words.Pinyin;
using System.Threading.Tasks;
namespace Flow.Launcher.Infrastructure namespace Flow.Launcher.Infrastructure
{ {
@ -27,7 +23,6 @@ namespace Flow.Launcher.Infrastructure
_settings = settings ?? throw new ArgumentNullException(nameof(settings)); _settings = settings ?? throw new ArgumentNullException(nameof(settings));
} }
public string Translate(string content) public string Translate(string content)
{ {
if (_settings.ShouldUsePinyin) if (_settings.ShouldUsePinyin)
@ -36,10 +31,40 @@ namespace Flow.Launcher.Infrastructure
{ {
if (WordsHelper.HasChinese(content)) if (WordsHelper.HasChinese(content))
{ {
var result = WordsHelper.GetPinyin(content, ";"); var resultList = WordsHelper.GetPinyinList(content);
result = GetFirstPinyinChar(result) + result.Replace(";", "");
_pinyinCache[content] = result; StringBuilder resultBuilder = new StringBuilder();
return result;
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 else
{ {
@ -56,10 +81,5 @@ namespace Flow.Launcher.Infrastructure
return content; 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 class HttpProxy
{ {
public bool Enabled { get; set; } = false; private bool _enabled = false;
public string Server { get; set; } private string _server;
public int Port { get; set; } private int _port;
public string UserName { get; set; } private string _userName;
public string Password { get; set; } 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 public class Settings : BaseModel
{ {
private string language = "en";
public string Hotkey { get; set; } = $"{KeyConstant.Alt} + {KeyConstant.Space}"; public string Hotkey { get; set; } = $"{KeyConstant.Alt} + {KeyConstant.Space}";
public string OpenResultModifiers { get; set; } = KeyConstant.Alt; public string OpenResultModifiers { get; set; } = KeyConstant.Alt;
public bool ShowOpenResultHotkey { get; set; } = true; 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 string Theme { get; set; } = Constant.DefaultTheme;
public bool UseDropShadowEffect { get; set; } = false; public bool UseDropShadowEffect { get; set; } = false;
public string QueryBoxFont { get; set; } = FontFamily.GenericSansSerif.Name; public string QueryBoxFont { get; set; } = FontFamily.GenericSansSerif.Name;

View file

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

View file

@ -63,12 +63,6 @@ namespace Flow.Launcher.Plugin
/// </summary> /// </summary>
void OpenSettingDialog(); void OpenSettingDialog();
/// <summary>
/// Install Flow Launcher plugin
/// </summary>
/// <param name="path">Plugin path (ends with .flowlauncher)</param>
void InstallPlugin(string path);
/// <summary> /// <summary>
/// Get translation of current language /// Get translation of current language
/// You need to implement IPluginI18n if you want to support multiple languages for your plugin /// 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 ProjectSection(ProjectDependencies) = postProject
{1EE20B48-82FB-48A2-8086-675D6DDAB4F0} = {1EE20B48-82FB-48A2-8086-675D6DDAB4F0} {1EE20B48-82FB-48A2-8086-675D6DDAB4F0} = {1EE20B48-82FB-48A2-8086-675D6DDAB4F0}
{0B9DE348-9361-4940-ADB6-F5953BFFCCEC} = {0B9DE348-9361-4940-ADB6-F5953BFFCCEC} {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} {FDB3555B-58EF-4AE6-B5F1-904719637AB4} = {FDB3555B-58EF-4AE6-B5F1-904719637AB4}
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F} = {F9C4C081-4CC3-4146-95F1-E102B4E10A5F} {F9C4C081-4CC3-4146-95F1-E102B4E10A5F} = {F9C4C081-4CC3-4146-95F1-E102B4E10A5F}
{59BD9891-3837-438A-958D-ADC7F91F6F7E} = {59BD9891-3837-438A-958D-ADC7F91F6F7E} {59BD9891-3837-438A-958D-ADC7F91F6F7E} = {59BD9891-3837-438A-958D-ADC7F91F6F7E}
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} = {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} {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} {9B130CC5-14FB-41FF-B310-0A95B6894C37} = {9B130CC5-14FB-41FF-B310-0A95B6894C37}
{FDED22C8-B637-42E8-824A-63B5B6E05A3A} = {FDED22C8-B637-42E8-824A-63B5B6E05A3A} {FDED22C8-B637-42E8-824A-63B5B6E05A3A} = {FDED22C8-B637-42E8-824A-63B5B6E05A3A}
{A3DCCBCA-ACC1-421D-B16E-210896234C26} = {A3DCCBCA-ACC1-421D-B16E-210896234C26} {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} {403B57F2-1856-4FC7-8A24-36AB346B763E} = {403B57F2-1856-4FC7-8A24-36AB346B763E}
{588088F4-3262-4F9F-9663-A05DE12534C3} = {588088F4-3262-4F9F-9663-A05DE12534C3} {588088F4-3262-4F9F-9663-A05DE12534C3} = {588088F4-3262-4F9F-9663-A05DE12534C3}
EndProjectSection EndProjectSection
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Infrastructure", "Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj", "{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Infrastructure", "Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj", "{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}"
EndProject 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}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Core", "Flow.Launcher.Core\Flow.Launcher.Core.csproj", "{B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}"
EndProject 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}" 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 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}" 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 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}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{FFD651C7-0546-441F-BC8C-D4EE8FD01EA7}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
.gitattributes = .gitattributes .gitattributes = .gitattributes
@ -71,6 +66,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Explor
EndProject 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}" 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 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 Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU 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|x64.Build.0 = Release|Any CPU
{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Release|x86.ActiveCfg = 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 {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.ActiveCfg = Debug|Any CPU
{B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}.Debug|Any CPU.Build.0 = 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 {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|x64.Build.0 = Release|Any CPU
{A3DCCBCA-ACC1-421D-B16E-210896234C26}.Release|x86.ActiveCfg = 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 {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.ActiveCfg = Debug|Any CPU
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}.Debug|Any CPU.Build.0 = 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 {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|x64.Build.0 = Release|Any CPU
{588088F4-3262-4F9F-9663-A05DE12534C3}.Release|x86.ActiveCfg = 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 {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 EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(NestedProjects) = preSolution GlobalSection(NestedProjects) = preSolution
{049490F0-ECD2-4148-9B39-2135EC346EBE} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{FDB3555B-58EF-4AE6-B5F1-904719637AB4} = {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} {403B57F2-1856-4FC7-8A24-36AB346B763E} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{1EE20B48-82FB-48A2-8086-675D6DDAB4F0} = {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} {FDED22C8-B637-42E8-824A-63B5B6E05A3A} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{0B9DE348-9361-4940-ADB6-F5953BFFCCEC} = {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} {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} {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{9B130CC5-14FB-41FF-B310-0A95B6894C37} = {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} {59BD9891-3837-438A-958D-ADC7F91F6F7E} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F} = {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} {588088F4-3262-4F9F-9663-A05DE12534C3} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{4792A74A-0CEA-4173-A8B2-30E6764C6217} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F26ACB50-3F6C-4907-B0C9-1ADACC1D0DED} 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="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="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="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="setAsTopMostInThisQuery">Pri tomto zadaní umiestniť navrchu</system:String>
<system:String x:Key="cancelTopMostInThisQuery">Zrušiť umiestnenie navrchu pri tomto dopyte</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="executeQuery">Spustiť dopyt: {0}</system:String>
<system:String x:Key="lastExecuteTime">Posledný čas realizácie: {0}</system:String> <system:String x:Key="lastExecuteTime">Posledný čas realizácie: {0}</system:String>
<system:String x:Key="iconTrayOpen">Otvoriť</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="dontPromptUpdateMsg">Nezobrazovať upozornenia na novú verziu</system:String>
<system:String x:Key="rememberLastLocation">Zapamätať si posledné umiestnenie</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="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="LastQueryPreserved">Ponechať</system:String>
<system:String x:Key="LastQuerySelected">Označiť</system:String> <system:String x:Key="LastQuerySelected">Označiť</system:String>
<system:String x:Key="LastQueryEmpty">Vymazať</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="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="hideNotifyIcon">Schovať ikonu z oblasti oznámení</system:String>
<system:String x:Key="querySearchPrecision">Presnosť vyhľadávania</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--> <!--Setting Plugin-->
<system:String x:Key="plugin">Plugin</system:String> <system:String x:Key="plugin">Plugin</system:String>
@ -96,11 +96,11 @@
<system:String x:Key="version">Verzia</system:String> <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="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="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="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"> <system:String x:Key="downloadUpdatesFailed">
Sťahovanie aktualizácií zlyhalo, skontrolujte pripojenie na internet a nastavenie proxy k github-cloud.s3.amazonaws.com, 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>
<system:String x:Key="releaseNotes">Poznámky k vydaniu</system:String> <system:String x:Key="releaseNotes">Poznámky k vydaniu</system:String>

View file

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

View file

@ -52,12 +52,14 @@ namespace Flow.Launcher
private void OnInitialized(object sender, EventArgs e) private void OnInitialized(object sender, EventArgs e)
{ {
// show notify icon when flowlauncher is hided
InitializeNotifyIcon();
} }
private void OnLoaded(object sender, RoutedEventArgs _) private void OnLoaded(object sender, RoutedEventArgs _)
{ {
// show notify icon when flowlauncher is hidden
InitializeNotifyIcon();
// todo is there a way to set blur only once? // todo is there a way to set blur only once?
ThemeManager.Instance.SetBlurForWindow(); ThemeManager.Instance.SetBlurForWindow();
WindowsInteropHelper.DisableControlBox(this); WindowsInteropHelper.DisableControlBox(this);
@ -87,11 +89,17 @@ namespace Flow.Launcher
}; };
_settings.PropertyChanged += (o, e) => _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(); InitializePosition();
} }
@ -103,6 +111,18 @@ namespace Flow.Launcher
_settings.WindowLeft = Left; _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() private void InitializeNotifyIcon()
{ {
_notifyIcon = new NotifyIcon _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) private void OnPreviewDragOver(object sender, DragEventArgs e)
{ {
e.Handled = true; e.Handled = true;
@ -294,7 +295,5 @@ namespace Flow.Launcher
_viewModel.QueryTextCursorMovedToEnd = false; _viewModel.QueryTextCursorMovedToEnd = false;
} }
} }
} }
} }

View file

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

View file

@ -42,7 +42,7 @@
<ColumnDefinition Width="0" /> <ColumnDefinition Width="0" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Image x:Name="ImageIcon" Width="32" Height="32" HorizontalAlignment="Left" <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 Margin="5 0 5 0" Grid.Column="1" HorizontalAlignment="Stretch">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition /> <RowDefinition />

View file

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

View file

@ -1,23 +1,66 @@
using System; using System;
using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Threading;
using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Image; using Flow.Launcher.Infrastructure.Image;
using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin; using Flow.Launcher.Plugin;
namespace Flow.Launcher.ViewModel namespace Flow.Launcher.ViewModel
{ {
public class ResultViewModel : BaseModel 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) public ResultViewModel(Result result, Settings settings)
{ {
if (result != null) if (result != null)
{ {
Result = result; Result = result;
Image = new LazyAsync<ImageSource>(
SetImage,
ImageLoader.DefaultImage,
() =>
{
OnPropertyChanged(nameof(Image));
});
} }
Settings = settings; Settings = settings;
@ -25,39 +68,45 @@ namespace Flow.Launcher.ViewModel
public Settings Settings { get; private set; } 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 OpenResultModifiers => Settings.OpenResultModifiers;
public string ShowTitleToolTip => string.IsNullOrEmpty(Result.TitleToolTip) public string ShowTitleToolTip => string.IsNullOrEmpty(Result.TitleToolTip)
? Result.Title ? Result.Title
: Result.TitleToolTip; : Result.TitleToolTip;
public string ShowSubTitleToolTip => string.IsNullOrEmpty(Result.SubTitleToolTip) public string ShowSubTitleToolTip => string.IsNullOrEmpty(Result.SubTitleToolTip)
? Result.SubTitle ? Result.SubTitle
: Result.SubTitleToolTip; : 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; try
if (string.IsNullOrEmpty(imagePath) && Result.Icon != null)
{ {
try return Result.Icon();
{
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;
}
} }
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 // will get here either when icoPath has value\icon delegate is null\when had exception in delegate
return ImageLoader.Load(imagePath); return ImageLoader.Load(imagePath);
} }
else
{
return await Task.Run(() => ImageLoader.Load(imagePath));
}
} }
public Result Result { get; } public Result Result { get; }
@ -84,6 +133,5 @@ namespace Flow.Launcher.ViewModel
{ {
return Result.ToString(); return Result.ToString();
} }
} }
} }

View file

@ -213,7 +213,7 @@ namespace Flow.Launcher.ViewModel
#region plugin #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 PluginViewModel SelectedPlugin { get; set; }
public IList<PluginViewModel> PluginViewModels public IList<PluginViewModel> PluginViewModels
@ -450,7 +450,7 @@ namespace Flow.Launcher.ViewModel
#region about #region about
public string Github => _updater.GitHubRepository; public string Website => Constant.Website;
public string ReleaseNotes => _updater.GitHubRepository + @"/releases/latest"; public string ReleaseNotes => _updater.GitHubRepository + @"/releases/latest";
public static string Version => Constant.Version; public static string Version => Constant.Version;
public string ActivatedTimes => string.Format(_translater.GetTranslation("about_activate_times"), Settings.ActivateTimes); 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) The MIT License (MIT)
Copyright (c) 2019 Flow-Launcher
Copyright (c) 2015 Wox Copyright (c) 2015 Wox
Permission is hereby granted, free of charge, to any person obtaining a copy of 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_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_comma">Čiarka (,)</system:String>
<system:String x:Key="flowlauncher_plugin_calculator_decimal_seperator_dot">Bodka (.)</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> </ResourceDictionary>

View file

@ -4,7 +4,7 @@
"Name": "Calculator", "Name": "Calculator",
"Description": "Provide mathematical calculations.(Try 5*3-2 in Flow Launcher)", "Description": "Provide mathematical calculations.(Try 5*3-2 in Flow Launcher)",
"Author": "cxfksword", "Author": "cxfksword",
"Version": "1.1.2", "Version": "1.1.3",
"Language": "csharp", "Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher", "Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.Caculator.dll", "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_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_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--> <!--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">Ú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> <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", "Name": "Program",
"Description": "Search programs in Flow.Launcher", "Description": "Search programs in Flow.Launcher",
"Author": "qianlifeng", "Author": "qianlifeng",
"Version": "1.2.1", "Version": "1.2.2",
"Language": "csharp", "Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher", "Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.Program.dll", "ExecuteFileName": "Flow.Launcher.Plugin.Program.dll",

View file

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

View file

@ -8,6 +8,7 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Flow.Launcher.Infrastructure.Http; using Flow.Launcher.Infrastructure.Http;
using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.Logger;
using System.Net.Http;
namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
{ {
@ -22,9 +23,9 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
try try
{ {
const string api = "http://suggestion.baidu.com/su?json=1&wd="; 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); Log.Exception("|Baidu.Suggestions|Can't get suggestion from baidu", e);
return new List<string>(); 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 Newtonsoft.Json.Linq;
using Flow.Launcher.Infrastructure.Http; using Flow.Launcher.Infrastructure.Http;
using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.Logger;
using System.Net.Http;
namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
{ {
@ -18,13 +19,12 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
try try
{ {
const string api = "https://www.google.com/complete/search?output=chrome&q="; 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); Log.Exception("|Google.Suggestions|Can't get suggestion from google", e);
return new List<string>(); return new List<string>();
;
} }
if (string.IsNullOrEmpty(result)) return new List<string>(); if (string.IsNullOrEmpty(result)) return new List<string>();
JContainer json; JContainer json;

View file

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

View file

@ -1,5 +1,7 @@
<p align="center"> <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> </p>
![Maintenance](https://img.shields.io/maintenance/yes/2020) ![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) ![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 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 - Support search using environment variable paths
- Run batch and PowerShell commands as Administrator or a different user. - Run batch and PowerShell commands as Administrator or a different user.
- Support languages from Chinese to Italian and more. - 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 ## 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. 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:** **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 - If you use 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) - 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** **Usage**
- Open flow's search window: <kbd>Alt</kbd>+<kbd>Space</kbd> is the default hotkey - 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> - Open context menu: <kbd>Ctrl</kbd>+<kbd>O</kbd>/<kbd>Shift</kbd>+<kbd>Enter</kbd>.
- Cancel/Return to previous screen: <kbd>Esc</kbd> - Cancel/Return to previous screen: <kbd>Esc</kbd>.
- Install/Uninstall plugins: in the search window, type `wpm install/uninstall` + the plugin name - Install/Uninstall/Update plugins: in the search window, type `pm install`/`pm uninstall`/`pm update` + the plugin name.
- Saved user settings are located: - Saved user settings are located:
- If using roaming: `%APPDATA%\FlowLauncher` - If using roaming: `%APPDATA%\FlowLauncher`
- If using portable, by default: `%localappdata%\FlowLauncher\app-<VersionOfYourFlowLauncher>\UserData` - 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 ## Documentation
[Wiki](https://github.com/Flow-Launcher/Flow.Launcher/wiki) [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" $output = "$path\Output"
$target = "$output\$config" $target = "$output\$config"
Copy-Item -Recurse -Force $project\Images\* $target\Images\ 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. # 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 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: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
[assembly: ComVisible(false)] [assembly: ComVisible(false)]
[assembly: AssemblyVersion("1.5.0")] [assembly: AssemblyVersion("1.6.0")]
[assembly: AssemblyFileVersion("1.5.0")] [assembly: AssemblyFileVersion("1.6.0")]
[assembly: AssemblyInformationalVersion("1.5.0")] [assembly: AssemblyInformationalVersion("1.6.0")]

View file

@ -1,4 +1,4 @@
version: '1.5.0.{build}' version: '1.6.0.{build}'
init: init:
- ps: | - 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.