Compare commits

...

17 commits

Author SHA1 Message Date
VictoriousRaptor
ceae0937a5
Merge a8e0d65e39 into 3b3a59fd62 2026-02-24 18:14:58 +01:00
Jack Ye
3b3a59fd62
Fix process priority for logon startup task set to BelowNormal by default (#4283)
Some checks are pending
Build / build (push) Waiting to run
2026-02-24 21:17:22 +11:00
Jack Ye
73f657f84e
Merge pull request #4279 from Flow-Launcher/websearch_ui
Some checks are pending
Build / build (push) Waiting to run
Enable TwoWay binding for MaxSuggestions NumberBox
2026-02-24 14:25:10 +08:00
Jack Ye
c9baab7ac9
Merge branch 'dev' into websearch_ui 2026-02-24 00:08:25 +08:00
Jack251970
72269db8e6 Enable TwoWay binding for MaxSuggestions NumberBox
Changed NumberBox binding for Settings.MaxSuggestions from OneWay to TwoWay, allowing user input in the UI to update the underlying setting. This ensures changes made by users are saved back to the settings model.
2026-02-24 00:07:36 +08:00
Jack Ye
ffa974b4e5
Merge pull request #4277 from Flow-Launcher/websearch_ui
Some checks are pending
Build / build (push) Waiting to run
Downgrade iNKORE.UI.WPF.Modern to 0.10.1 for WebSearch Plugin
2026-02-23 17:09:43 +08:00
Jack Ye
727aac4b82
Merge pull request #4278 from Flow-Launcher/SkiaSharp_runtimes
Some checks are pending
Build / build (push) Waiting to run
Remove Skia sharp unnecessary runtimes from packages
2026-02-23 17:01:00 +08:00
Jack251970
9e66f71774 Revert version bump of SkiaSharp package 2026-02-23 16:36:08 +08:00
Jack Ye
d511872c00
Ensure runtimes folder
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-22 21:29:01 +08:00
Jack251970
c46a52d1ca Remove SkiaSharp .pdb files after build and publish
Added MSBuild targets to delete unnecessary SkiaSharp .pdb files from output and publish directories after build and publish steps, reducing artifact size.
2026-02-22 21:22:17 +08:00
Jack251970
c04da3ab83 Add RISC-V 64-bit runtime support to project config
Included linux-musl-riscv64 and linux-riscv64 in both OutputPath and PublishDir of Flow.Launcher.Plugin.BrowserBookmark.csproj to ensure runtime files for RISC-V 64-bit architectures are available during build and publish.
2026-02-22 21:13:38 +08:00
Jack251970
cb60492657 Downgrade iNKORE.UI.WPF.Modern to 0.10.1
The package reference for iNKORE.UI.WPF.Modern in Flow.Launcher.Plugin.WebSearch.csproj was changed from version 0.10.2.1 to 0.10.1.
2026-02-22 12:38:17 +08:00
VictoriousRaptor
a8e0d65e39 Refactor URL plugin test 2025-12-29 22:06:05 +08:00
VictoriousRaptor
77f81cfb16 Fix matching pattern for IPv6 addresses with consecutive ":" 2025-12-28 22:04:37 +08:00
VictoriousRaptor
d0a274a668 Add test case 2025-12-28 13:52:01 +08:00
VictoriousRaptor
e9a68d2563 Fix match logic 2025-12-28 10:56:27 +08:00
VictoriousRaptor
be630f1ab5 Update UrlPattern to include private addresses and update test cases 2025-12-28 10:43:38 +08:00
6 changed files with 106 additions and 53 deletions

View file

@ -1,33 +1,73 @@
using NUnit.Framework;
using NUnit.Framework.Legacy;
using Flow.Launcher.Plugin.Url;
using System.Reflection;
namespace Flow.Launcher.Test.Plugins
{
[TestFixture]
public class UrlPluginTest
{
[Test]
public void URLMatchTest()
private static Main plugin;
[OneTimeSetUp]
public void OneTimeSetup()
{
var plugin = new Main();
ClassicAssert.IsTrue(plugin.IsURL("http://www.google.com"));
ClassicAssert.IsTrue(plugin.IsURL("https://www.google.com"));
ClassicAssert.IsTrue(plugin.IsURL("http://google.com"));
ClassicAssert.IsTrue(plugin.IsURL("www.google.com"));
ClassicAssert.IsTrue(plugin.IsURL("google.com"));
ClassicAssert.IsTrue(plugin.IsURL("http://localhost"));
ClassicAssert.IsTrue(plugin.IsURL("https://localhost"));
ClassicAssert.IsTrue(plugin.IsURL("http://localhost:80"));
ClassicAssert.IsTrue(plugin.IsURL("https://localhost:80"));
ClassicAssert.IsTrue(plugin.IsURL("http://110.10.10.10"));
ClassicAssert.IsTrue(plugin.IsURL("110.10.10.10"));
ClassicAssert.IsTrue(plugin.IsURL("ftp://110.10.10.10"));
var settingsField = typeof(Main).GetField("Settings", BindingFlags.NonPublic | BindingFlags.Static);
settingsField?.SetValue(null, new Settings());
plugin = new Main();
}
ClassicAssert.IsFalse(plugin.IsURL("wwww"));
ClassicAssert.IsFalse(plugin.IsURL("wwww.c"));
ClassicAssert.IsFalse(plugin.IsURL("wwww.c"));
[TestCase("http://www.google.com")]
[TestCase("https://www.google.com")]
[TestCase("http://google.com")]
[TestCase("ftp://google.com")]
[TestCase("www.google.com")]
[TestCase("google.com")]
[TestCase("http://localhost")]
[TestCase("https://localhost")]
[TestCase("http://localhost:80")]
[TestCase("https://localhost:80")]
[TestCase("localhost")]
[TestCase("localhost:8080")]
[TestCase("http://110.10.10.10")]
[TestCase("110.10.10.10")]
[TestCase("110.10.10.10:8080")]
[TestCase("192.168.1.1")]
[TestCase("192.168.1.1:3000")]
[TestCase("ftp://110.10.10.10")]
[TestCase("[2001:db8::1]")]
[TestCase("[2001:db8::1]:8080")]
[TestCase("http://[2001:db8::1]")]
[TestCase("https://[2001:db8::1]:8080")]
[TestCase("[::1]")]
[TestCase("[::1]:8080")]
[TestCase("2001:db8::1")]
[TestCase("fe80:1:2::3:4")]
[TestCase("::1")]
[TestCase("HTTP://EXAMPLE.COM")]
[TestCase("HTTPS://EXAMPLE.COM")]
[TestCase("EXAMPLE.COM")]
[TestCase("LOCALHOST")]
public void WhenValidUrlThenIsUrlReturnsTrue(string url)
{
Assert.That(plugin.IsURL(url), Is.True);
}
[TestCase("wwww")]
[TestCase("wwww.c")]
[TestCase("not a url")]
[TestCase("just text")]
[TestCase("http://")]
[TestCase("://example.com")]
[TestCase("0.0.0.0")] // Pattern excludes 0.0.0.0
[TestCase("256.1.1.1")] // Invalid IPv4
[TestCase("example")] // No TLD
[TestCase(".com")]
[TestCase("http://.com")]
public void WhenInvalidUrlThenIsUrlReturnsFalse(string url)
{
Assert.That(plugin.IsURL(url), Is.False);
}
}
}

View file

@ -1,4 +1,5 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Security.Principal;
using Flow.Launcher.Infrastructure;
@ -64,7 +65,9 @@ public class AutoStartup
if (task.Definition.Actions.FirstOrDefault() is Microsoft.Win32.TaskScheduler.Action taskAction)
{
var action = taskAction.ToString().Trim();
if (!action.Equals(Constant.ExecutablePath, StringComparison.OrdinalIgnoreCase))
var needsRecreation = !action.Equals(Constant.ExecutablePath, StringComparison.OrdinalIgnoreCase)
|| task.Definition.Settings.Priority != ProcessPriorityClass.Normal;
if (needsRecreation)
{
UnscheduleLogonTask();
ScheduleLogonTask();
@ -184,6 +187,7 @@ public class AutoStartup
td.Settings.StopIfGoingOnBatteries = false;
td.Settings.DisallowStartIfOnBatteries = false;
td.Settings.ExecutionTimeLimit = TimeSpan.Zero;
td.Settings.Priority = ProcessPriorityClass.Normal;
try
{

View file

@ -51,6 +51,8 @@
$(OutputPath)runtimes\linux-s390x;
$(OutputPath)runtimes\linux-x64;
$(OutputPath)runtimes\linux-x86;
$(OutputPath)runtimes\linux-musl-riscv64;
$(OutputPath)runtimes\linux-riscv64;
$(OutputPath)runtimes\maccatalyst-arm64;
$(OutputPath)runtimes\maccatalyst-x64;
$(OutputPath)runtimes\osx;
@ -74,6 +76,8 @@
$(PublishDir)runtimes\linux-s390x;
$(PublishDir)runtimes\linux-x64;
$(PublishDir)runtimes\linux-x86;
$(PublishDir)runtimes\linux-musl-riscv64;
$(PublishDir)runtimes\linux-riscv64;
$(PublishDir)runtimes\maccatalyst-arm64;
$(PublishDir)runtimes\maccatalyst-x64;
$(PublishDir)runtimes\osx;
@ -107,7 +111,7 @@
<PackageReference Include="Flow.Launcher.Localization" Version="0.0.6" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="10.0.3" />
<PackageReference Include="Svg.Skia" Version="3.4.1" />
<PackageReference Include="SkiaSharp" Version="3.119.2" />
<PackageReference Include="SkiaSharp" Version="3.119.1" />
</ItemGroup>
</Project>
</Project>

View file

@ -1,5 +1,6 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Controls;
using Flow.Launcher.Plugin.SharedCommands;
@ -15,19 +16,28 @@ namespace Flow.Launcher.Plugin.Url
// user:pass authentication
"(?:\\S+(?::\\S*)?@)?" +
"(?:" +
// IP address exclusion
// private & local networks
"(?!(?:10|127)(?:\\.\\d{1,3}){3})" +
"(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})" +
"(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})" +
// IP address dotted notation octets
// excludes loopback network 0.0.0.0
// excludes reserved space >= 224.0.0.0
// excludes network & broacast addresses
// (first & last IP address of each class)
"(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" +
"(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" +
"(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" +
// IPv6 address with optional brackets (brackets required if followed by port)
// IPv6 with brackets
"(?:\\[(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\\]|" + // standard IPv6
"\\[(?:[0-9a-fA-F]{1,4}:){1,7}:\\]|" + // IPv6 with trailing ::
"\\[(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}\\]|" + // IPv6 compressed
"\\[::(?:[0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4}\\]|" + // IPv6 with leading ::
"\\[(?:(?:[0-9a-fA-F]{1,4}:){1,6}|:):(?:[0-9a-fA-F]{1,4}:){0,5}[0-9a-fA-F]{1,4}\\]|" + // IPv6 with :: in the middle
"\\[::1\\])" + // IPv6 loopback
"|" +
// IPv6 without brackets (only when no port follows)
"(?:(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|" + // standard IPv6
"(?:[0-9a-fA-F]{1,4}:){1,7}:|" + // IPv6 with trailing ::
"(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|" + // IPv6 compressed
"::(?:[0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4}|" + // IPv6 with leading ::
"(?:(?:[0-9a-fA-F]{1,4}:){1,6}|:):(?:[0-9a-fA-F]{1,4}:){0,5}[0-9a-fA-F]{1,4}|" + // IPv6 with :: in the middle
"::1)(?!:[0-9])" + // IPv6 loopback (not followed by port)
"|" +
// IPv4 address - all valid addresses including private networks (excluding 0.0.0.0)
"(?:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|[1-9])\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d))" +
"|" +
// localhost
"localhost" +
"|" +
// host name
"(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)" +
@ -37,7 +47,7 @@ namespace Flow.Launcher.Plugin.Url
"(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))" +
")" +
// port number
"(?::\\d{2,5})?" +
"(?::\\d{1,5})?" +
// resource path
"(?:/\\S*)?" +
"$";
@ -45,12 +55,17 @@ namespace Flow.Launcher.Plugin.Url
internal static PluginInitContext Context { get; private set; }
internal static Settings Settings { get; private set; }
private static readonly string[] UrlSchemes = ["http://", "https://", "ftp://"];
public List<Result> Query(Query query)
{
var raw = query.Search;
if (IsURL(raw))
if (!IsURL(raw))
{
return
return [];
}
return
[
new()
{
@ -60,7 +75,8 @@ namespace Flow.Launcher.Plugin.Url
Score = 8,
Action = _ =>
{
if (!raw.StartsWith("http://", StringComparison.OrdinalIgnoreCase) && !raw.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
// not a recognized scheme, add preferred http scheme
if (!UrlSchemes.Any(scheme => raw.StartsWith(scheme, StringComparison.OrdinalIgnoreCase)))
{
raw = GetHttpPreference() + "://" + raw;
}
@ -92,9 +108,6 @@ namespace Flow.Launcher.Plugin.Url
}
}
];
}
return [];
}
private static string GetHttpPreference()
@ -108,14 +121,6 @@ namespace Flow.Launcher.Plugin.Url
if (UrlRegex.Match(raw).Value == raw) return true;
if (raw == "localhost" || raw.StartsWith("localhost:") ||
raw == "http://localhost" || raw.StartsWith("http://localhost:") ||
raw == "https://localhost" || raw.StartsWith("https://localhost:")
)
{
return true;
}
return false;
}

View file

@ -51,7 +51,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="iNKORE.UI.WPF.Modern" Version="0.10.2.1" />
<PackageReference Include="iNKORE.UI.WPF.Modern" Version="0.10.1" />
</ItemGroup>
<ItemGroup>

View file

@ -164,7 +164,7 @@
SpinButtonPlacementMode="Compact"
ValidationMode="InvalidInputOverwritten"
ValueChanged="NumberBox_ValueChanged"
Value="{Binding Settings.MaxSuggestions, Mode=OneWay}" />
Value="{Binding Settings.MaxSuggestions, Mode=TwoWay}" />
</StackPanel>
<CheckBox
Name="EnableSuggestion"