Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
56bdfa7
Improve code quality
Jack251970 Sep 4, 2025
f848d1b
Improve code quality
Jack251970 Sep 4, 2025
c9ca9ad
Add more settings
Jack251970 Sep 4, 2025
2d5626b
Use more settings
Jack251970 Sep 4, 2025
5976dde
Add more strings
Jack251970 Sep 4, 2025
48e845c
Change language strings
Jack251970 Sep 4, 2025
1dc1643
Change language strings
Jack251970 Sep 4, 2025
33a9654
Add settings control
Jack251970 Sep 4, 2025
1303ae5
Update string resource
Jack251970 Sep 5, 2025
978b185
Add PrivateModeArgument & Add IPropertyChanged interface for some pro…
Jack251970 Sep 5, 2025
7ca3769
Use PrivateModeArgument & Implement new logic for browser openning
Jack251970 Sep 5, 2025
34a58bc
Add InverseBoolConverter
Jack251970 Sep 5, 2025
c17db8c
Use using statements
Jack251970 Sep 5, 2025
cdf3905
Use InverseBoolConverter
Jack251970 Sep 5, 2025
33aef7e
Add convert back implementation for InverseBoolConverter
Jack251970 Sep 5, 2025
4f49077
Add & Use BoolToVisibilityConverter
Jack251970 Sep 5, 2025
96d7d18
Change string resource names & Redesign setting panel
Jack251970 Sep 5, 2025
3f6bebd
Keep default value same as origin
Jack251970 Sep 5, 2025
45bb6e9
Merge branch 'dev' into url_open_enhancement
Jack251970 Sep 6, 2025
c2e7976
Improve code quality
Jack251970 Sep 15, 2025
3a8c64b
update wording
jjw24 Sep 18, 2025
d7d88a9
Merge branch 'dev' into url_open_enhancement
Jack251970 Sep 21, 2025
08bf147
Merge branch 'dev' into url_open_enhancement
Jack251970 Sep 28, 2025
64e9150
Fix build issue
Jack251970 Sep 28, 2025
ec41ec2
Improve code quality
Jack251970 Sep 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions Plugins/Flow.Launcher.Plugin.Url/Languages/en.xaml
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
<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">
<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_url_open_search_in">Open search in:</system:String>
<system:String x:Key="flowlauncher_plugin_new_window">New Window</system:String>
<system:String x:Key="flowlauncher_plugin_new_tab">New Tab</system:String>

<system:String x:Key="flowlauncher_plugin_url_open_url">Open url:{0}</system:String>
<system:String x:Key="flowlauncher_plugin_url_cannot_open_url">Can't open url:{0}</system:String>

<system:String x:Key="flowlauncher_plugin_url_plugin_name">URL</system:String>
<system:String x:Key="flowlauncher_plugin_url_plugin_description">Open the typed URL from Flow Launcher</system:String>

<system:String x:Key="flowlauncher_plugin_url_plugin_set_tip">Please set your browser path:</system:String>
<system:String x:Key="flowlauncher_plugin_url_plugin_choose">Choose</system:String>
<system:String x:Key="flowlauncher_plugin_url_plugin_filter">Application(*.exe)|*.exe|All files|*.*</system:String>

<system:String x:Key="flowlauncher_plugin_use_custom_browser">Use custom browser</system:String>
<system:String x:Key="flowlauncher_plugin_browser_path">Browser path:</system:String>
<system:String x:Key="flowlauncher_plugin_url_open_in_new_window">Open url in new window</system:String>
<system:String x:Key="flowlauncher_plugin_url_open_in_private">Open url in private</system:String>
</ResourceDictionary>
60 changes: 38 additions & 22 deletions Plugins/Flow.Launcher.Plugin.Url/Main.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Windows.Controls;
using Flow.Launcher.Plugin.SharedCommands;

namespace Flow.Launcher.Plugin.Url
{
public class Main : IPlugin, IPluginI18n
public class Main : IPlugin, IPluginI18n, ISettingProvider
{
//based on https://gist.github.com/dperini/729294
private const string urlPattern = "^" +
private const string UrlPattern = "^" +
// protocol identifier
"(?:(?:https?|ftp)://|)" +
// user:pass authentication
Expand All @@ -21,7 +23,7 @@
// IP address dotted notation octets
// excludes loopback network 0.0.0.0
// excludes reserved space >= 224.0.0.0
// excludes network & broacast addresses

Check warning on line 26 in Plugins/Flow.Launcher.Plugin.Url/Main.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`broacast` is not a recognized word. (unrecognized-spelling)
// (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}" +
Expand All @@ -39,52 +41,62 @@
// resource path
"(?:/\\S*)?" +
"$";
Regex reg = new Regex(urlPattern, RegexOptions.Compiled | RegexOptions.IgnoreCase);
private PluginInitContext context;
private Settings _settings;

private readonly Regex UrlRegex = new(UrlPattern, RegexOptions.Compiled | RegexOptions.IgnoreCase);

public static PluginInitContext Context { get; private set; }

public static Settings Settings { get; private set; }

public List<Result> Query(Query query)
{
var raw = query.Search;
if (IsURL(raw))
{
return new List<Result>
{
new Result
return
[
new()
{
Title = raw,
SubTitle = string.Format(context.API.GetTranslation("flowlauncher_plugin_url_open_url"),raw),
SubTitle = string.Format(Context.API.GetTranslation("flowlauncher_plugin_url_open_url"),raw),
IcoPath = "Images/url.png",
Score = 8,
Action = _ =>
{
if (!raw.ToLower().StartsWith("http"))
if (!raw.StartsWith("http", StringComparison.OrdinalIgnoreCase))
{
raw = "http://" + raw;
}
try
{
context.API.OpenUrl(raw);

var browserPath = Settings.UseCustomBrowser ? Settings.BrowserPath : string.Empty;
if (Settings.OpenInNewBrowserWindow)
{
SearchWeb.OpenInBrowserWindow(raw, browserPath, Settings.OpenInPrivateMode);
}
else
{
SearchWeb.OpenInBrowserTab(raw, browserPath, Settings.OpenInPrivateMode);
}
return true;
}
catch(Exception)
{
context.API.ShowMsgError(string.Format(context.API.GetTranslation("flowlauncher_plugin_url_cannot_open_url"), raw));
Context.API.ShowMsgError(string.Format(Context.API.GetTranslation("flowlauncher_plugin_url_cannot_open_url"), raw));
return false;
}
}
}
};
];
}
return new List<Result>(0);

return [];
}

public bool IsURL(string raw)
{
raw = raw.ToLower();

if (reg.Match(raw).Value == raw) return true;
if (UrlRegex.Match(raw).Value == raw) return true;

if (raw == "localhost" || raw.StartsWith("localhost:") ||
raw == "http://localhost" || raw.StartsWith("http://localhost:") ||
Expand All @@ -99,19 +111,23 @@

public void Init(PluginInitContext context)
{
this.context = context;

_settings = context.API.LoadSettingJsonStorage<Settings>();
Context = context;
Settings = context.API.LoadSettingJsonStorage<Settings>();
}

public string GetTranslatedPluginTitle()
{
return context.API.GetTranslation("flowlauncher_plugin_url_plugin_name");
return Context.API.GetTranslation("flowlauncher_plugin_url_plugin_name");
}

public string GetTranslatedPluginDescription()
{
return context.API.GetTranslation("flowlauncher_plugin_url_plugin_description");
return Context.API.GetTranslation("flowlauncher_plugin_url_plugin_description");
}

public Control CreateSettingPanel()
{
return new SettingsControl();
}
}
}
22 changes: 19 additions & 3 deletions Plugins/Flow.Launcher.Plugin.Url/Settings.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
namespace Flow.Launcher.Plugin.Url
{
public class Settings
public class Settings : BaseModel
{
public string BrowserPath { get; set; }
public bool UseCustomBrowser { get; set; } = false;

public bool OpenInNewBrowserWindow { get; set; } = true;
private string _browserPath = string.Empty;
public string BrowserPath
{
get => _browserPath;
set
{
if (_browserPath != value)
{
_browserPath = value;
OnPropertyChanged();
}
}
}

public bool OpenInNewBrowserWindow { get; set; } = false;

public bool OpenInPrivateMode { get; set; } = false;
}
}
86 changes: 86 additions & 0 deletions Plugins/Flow.Launcher.Plugin.Url/SettingsControl.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<UserControl
x:Class="Flow.Launcher.Plugin.Url.SettingsControl"
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:local="clr-namespace:Flow.Launcher.Plugin.Url"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="450"
d:DesignWidth="800"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
mc:Ignorable="d">

<Grid Margin="{StaticResource SettingPanelMargin}">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>

<CheckBox
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Content="{DynamicResource flowlauncher_plugin_use_custom_browser}"
IsChecked="{Binding Settings.UseCustomBrowser, Mode=TwoWay}" />

<TextBlock
Grid.Row="1"
Grid.Column="0"
Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
VerticalAlignment="Center"
FontSize="14"
Text="{DynamicResource flowlauncher_plugin_browser_path}" />
<Grid
Grid.Row="1"
Grid.Column="1"
Margin="{StaticResource SettingPanelItemLeftTopBottomMargin}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>

<TextBox
Grid.Column="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
IsReadOnly="True"
Text="{Binding Settings.BrowserPath, Mode=OneWay}" />
<Button
Grid.Column="1"
Margin="{StaticResource SettingPanelItemLeftMargin}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Click="SelectBrowserPath"
Content="{DynamicResource flowlauncher_plugin_url_plugin_choose}" />
</Grid>

<CheckBox
Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Content="{DynamicResource flowlauncher_plugin_url_open_in_new_window}"
IsChecked="{Binding Settings.OpenInNewBrowserWindow, Mode=TwoWay}" />

<CheckBox
Grid.Row="3"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Content="{DynamicResource flowlauncher_plugin_url_open_in_private}"
IsChecked="{Binding Settings.OpenInPrivateMode, Mode=TwoWay}" />
</Grid>
</UserControl>
27 changes: 27 additions & 0 deletions Plugins/Flow.Launcher.Plugin.Url/SettingsControl.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.Windows;
using System.Windows.Controls;

namespace Flow.Launcher.Plugin.Url;

public partial class SettingsControl : UserControl
{
public Settings Settings => Main.Settings;

public SettingsControl()
{
InitializeComponent();
}

private void SelectBrowserPath(object sender, RoutedEventArgs e)
{
var dlg = new Microsoft.Win32.OpenFileDialog
{
Filter = Main.Context.API.GetTranslation("flowlauncher_plugin_url_plugin_filter")
Copy link
Member

@jjw24 jjw24 Sep 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think it's good to use a translatable string as filter, that filter shouldn't even need translation right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think it's good to use a translatable string as filter, that filter shouldn't even need translation right?

I do not think so. In Application(*.exe)|*.exe|All files|*.*, I think Application & All files should be translated

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Take Notepad filter for example, those filters are translated:

image

};

if (dlg.ShowDialog() == true && !string.IsNullOrEmpty(dlg.FileName))
{
Settings.BrowserPath = dlg.FileName;
}
}
}
Loading