Skip to content
Open
Show file tree
Hide file tree
Changes from 18 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
Copy link
Contributor

@VictoriousRaptor VictoriousRaptor Sep 22, 2025

Choose a reason for hiding this comment

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

IIRC we've got a same converter. Do we need this?

Copy link
Member Author

Choose a reason for hiding this comment

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

IIRC we've got a same converter. Do we need this?

This is used in url project. We must need to keep it.

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace Flow.Launcher.Plugin.Url.Converters;

[ValueConversion(typeof(bool), typeof(Visibility))]
public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is not bool)
throw new ArgumentException("value should be boolean", nameof(value));

return (bool)value ? Visibility.Visible : Visibility.Collapsed;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new InvalidOperationException();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using System.Globalization;
using System.Windows.Data;

namespace Flow.Launcher.Plugin.Url.Converters;

[ValueConversion(typeof(bool), typeof(bool))]
public class InverseBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is not bool)
throw new ArgumentException("value should be boolean", nameof(value));

return !(bool)value;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is not bool)
throw new ArgumentException("value should be boolean", nameof(value));

return !(bool)value;
}
}
20 changes: 12 additions & 8 deletions Plugins/Flow.Launcher.Plugin.Url/Languages/en.xaml
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
<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_url_use_custom_browser">Use custom web browser instead of Flow default web browser</system:String>
<system:String x:Key="flowlauncher_plugin_url_browser_path">Browser path</system:String>

<system:String x:Key="flowlauncher_plugin_url_new_tab">New tab</system:String>
<system:String x:Key="flowlauncher_plugin_url_new_window">New window</system:String>

<system:String x:Key="flowlauncher_plugin_url_private_mode">Private mode</system:String>
</ResourceDictionary>
66 changes: 44 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,68 @@
// 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);

if (Settings.UseCustomBrowser)
{
if (Settings.OpenInNewBrowserWindow)
{
SearchWeb.OpenInBrowserWindow(raw, Settings.BrowserPath, Settings.OpenInPrivateMode, Settings.PrivateModeArgument);
}
else
{
SearchWeb.OpenInBrowserTab(raw, Settings.BrowserPath, Settings.OpenInPrivateMode, Settings.PrivateModeArgument);
}
}
else
{
Context.API.OpenWebUrl(raw);
}
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 +117,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();
}
}
}
48 changes: 45 additions & 3 deletions Plugins/Flow.Launcher.Plugin.Url/Settings.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,51 @@
namespace Flow.Launcher.Plugin.Url
{
public class Settings
public class Settings : BaseModel
{
public string BrowserPath { get; set; }
private bool _useCustomBrowser = false;
public bool UseCustomBrowser
{
get => _useCustomBrowser;
set
{
if (_useCustomBrowser != value)
{
_useCustomBrowser = value;
OnPropertyChanged();
}
}
}

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

private bool _openInNewBrowserWindow = true;
public bool OpenInNewBrowserWindow
{
get => _openInNewBrowserWindow;
set
{
if (_openInNewBrowserWindow != value)
{
_openInNewBrowserWindow = value;
OnPropertyChanged();
}
}
}

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

public string PrivateModeArgument { get; set; } = string.Empty;
}
}
117 changes: 117 additions & 0 deletions Plugins/Flow.Launcher.Plugin.Url/SettingsControl.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<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:converters="clr-namespace:Flow.Launcher.Plugin.Url.Converters"
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">
<UserControl.Resources>
<converters:InverseBoolConverter x:Key="InverseBoolConverter" />
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
</UserControl.Resources>

<Grid Margin="{StaticResource SettingPanelMargin}">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>

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

<Grid
Grid.Row="1"
HorizontalAlignment="Left"
Visibility="{Binding Settings.UseCustomBrowser, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>

<TextBlock
Grid.Row="0"
Grid.Column="0"
Margin="{StaticResource SettingPanelItemRightTopBottomMargin}"
VerticalAlignment="Center"
FontSize="14"
Text="{DynamicResource flowlauncher_plugin_url_browser_path}" />
<Grid
Grid.Row="0"
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>

<StackPanel
Grid.Row="1"
Grid.Column="1"
Margin="{StaticResource SettingPanelItemLeftTopBottomMargin}"
Orientation="Horizontal">
<RadioButton Content="{DynamicResource flowlauncher_plugin_url_new_tab}" IsChecked="{Binding Settings.OpenInNewBrowserWindow, Converter={StaticResource InverseBoolConverter}, Mode=TwoWay}" />
<RadioButton Content="{DynamicResource flowlauncher_plugin_url_new_window}" IsChecked="{Binding Settings.OpenInNewBrowserWindow, Mode=TwoWay}" />
</StackPanel>

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

<TextBox
Grid.Column="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Text="{Binding Settings.PrivateModeArgument, Mode=TwoWay}" />
<CheckBox
Grid.Column="1"
Margin="{StaticResource SettingPanelItemLeftMargin}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Content=""
IsChecked="{Binding Settings.OpenInPrivateMode, Mode=TwoWay}" />
</Grid>
</Grid>
</Grid>
</UserControl>
Loading
Loading