From a7994402fe9ea7b89abf0702abb9833977367f4d Mon Sep 17 00:00:00 2001 From: Yu Leng <42196638+moooyo@users.noreply.github.com> Date: Thu, 10 Apr 2025 23:34:52 +0800 Subject: [PATCH] [cmdpal] Add Open URL fallback command for WebSearch ext (#38685) ## Summary of the Pull Request 1. Add new fallback command for websearch https://github.com/user-attachments/assets/39362d66-db59-42d4-b07c-7bfd60b2e420 ## PR Checklist - [x] **Closes:** #38497 --------- Co-authored-by: Yu Leng (from Dev Box) --- .../Commands/OpenURLCommand.cs | 37 ++++++++ .../FallbackOpenURLItem.cs | 88 +++++++++++++++++++ .../Properties/Resources.Designer.cs | 18 ++++ .../Properties/Resources.resx | 6 ++ .../WebSearchCommandsProvider.cs | 4 +- 5 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Commands/OpenURLCommand.cs create mode 100644 src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/FallbackOpenURLItem.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Commands/OpenURLCommand.cs b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Commands/OpenURLCommand.cs new file mode 100644 index 0000000000..fd3f3a8f18 --- /dev/null +++ b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Commands/OpenURLCommand.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CmdPal.Ext.WebSearch.Helpers; +using Microsoft.CommandPalette.Extensions.Toolkit; + +using BrowserInfo = Microsoft.CmdPal.Ext.WebSearch.Helpers.DefaultBrowserInfo; + +namespace Microsoft.CmdPal.Ext.WebSearch.Commands; + +internal sealed partial class OpenURLCommand : InvokableCommand +{ + private readonly SettingsManager _settingsManager; + + public string Url { get; internal set; } = string.Empty; + + internal OpenURLCommand(string url, SettingsManager settingsManager) + { + Url = url; + BrowserInfo.UpdateIfTimePassed(); + Icon = IconHelpers.FromRelativePath("Assets\\WebSearch.png"); + Name = string.Empty; + _settingsManager = settingsManager; + } + + public override CommandResult Invoke() + { + if (!ShellHelpers.OpenCommandInShell(BrowserInfo.Path, BrowserInfo.ArgumentsPattern, $"{Url}")) + { + // TODO GH# 138 --> actually display feedback from the extension somewhere. + return CommandResult.KeepOpen(); + } + + return CommandResult.Dismiss(); + } +} diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/FallbackOpenURLItem.cs b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/FallbackOpenURLItem.cs new file mode 100644 index 0000000000..721af2ec8e --- /dev/null +++ b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/FallbackOpenURLItem.cs @@ -0,0 +1,88 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Globalization; +using System.Text; +using Microsoft.CmdPal.Ext.WebSearch.Commands; +using Microsoft.CmdPal.Ext.WebSearch.Helpers; +using Microsoft.CmdPal.Ext.WebSearch.Properties; +using Microsoft.CommandPalette.Extensions.Toolkit; +using BrowserInfo = Microsoft.CmdPal.Ext.WebSearch.Helpers.DefaultBrowserInfo; + +namespace Microsoft.CmdPal.Ext.WebSearch; + +internal sealed partial class FallbackOpenURLItem : FallbackCommandItem +{ + private readonly OpenURLCommand _executeItem; + private static readonly CompositeFormat PluginOpenURL = System.Text.CompositeFormat.Parse(Properties.Resources.plugin_open_url); + private static readonly CompositeFormat PluginOpenUrlInBrowser = System.Text.CompositeFormat.Parse(Properties.Resources.plugin_open_url_in_browser); + + public FallbackOpenURLItem(SettingsManager settings) + : base(new OpenURLCommand(string.Empty, settings), string.Empty) + { + _executeItem = (OpenURLCommand)this.Command!; + Title = string.Empty; + _executeItem.Name = string.Empty; + Subtitle = string.Empty; + Icon = IconHelpers.FromRelativePath("Assets\\WebSearch.png"); + } + + public override void UpdateQuery(string query) + { + if (!IsValidUrl(query)) + { + Title = string.Empty; + Subtitle = string.Empty; + return; + } + + var success = Uri.TryCreate(query, UriKind.Absolute, out var uri); + + // if url not contain schema, add http:// by default. + if (!success) + { + query = "https://" + query; + } + + _executeItem.Url = query; + _executeItem.Name = string.IsNullOrEmpty(query) ? string.Empty : Properties.Resources.open_in_default_browser; + + Title = string.Format(CultureInfo.CurrentCulture, PluginOpenURL, query); + Subtitle = string.Format(CultureInfo.CurrentCulture, PluginOpenUrlInBrowser, BrowserInfo.Name ?? BrowserInfo.MSEdgeName); + } + + public static bool IsValidUrl(string url) + { + if (string.IsNullOrWhiteSpace(url)) + { + return false; + } + + if (!url.Contains('.', StringComparison.OrdinalIgnoreCase)) + { + // eg: 'com', 'org'. We don't think it's a valid url. + // This can simplify the logic of checking if the url is valid. + return false; + } + + if (Uri.IsWellFormedUriString(url, UriKind.Absolute)) + { + return true; + } + + if (!url.StartsWith("http://", StringComparison.OrdinalIgnoreCase) && + !url.StartsWith("https://", StringComparison.OrdinalIgnoreCase) && + !url.StartsWith("ftp://", StringComparison.OrdinalIgnoreCase) && + !url.StartsWith("file://", StringComparison.OrdinalIgnoreCase)) + { + if (Uri.IsWellFormedUriString("https://" + url, UriKind.Absolute)) + { + return true; + } + } + + return false; + } +} diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Properties/Resources.Designer.cs b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Properties/Resources.Designer.cs index e138b74ecc..15a8c4631f 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Properties/Resources.Designer.cs +++ b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Properties/Resources.Designer.cs @@ -195,6 +195,24 @@ namespace Microsoft.CmdPal.Ext.WebSearch.Properties { } } + /// + /// Looks up a localized string similar to Open "{0}". + /// + public static string plugin_open_url { + get { + return ResourceManager.GetString("plugin_open_url", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open url in {0}. + /// + public static string plugin_open_url_in_browser { + get { + return ResourceManager.GetString("plugin_open_url_in_browser", resourceCulture); + } + } + /// /// Looks up a localized string similar to Failed to open {0}.. /// diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Properties/Resources.resx b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Properties/Resources.resx index 9caaca6c2f..aec5b771c9 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Properties/Resources.resx +++ b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Properties/Resources.resx @@ -163,6 +163,12 @@ Search the web in {0} + + Open "{0}" + + + Open url in {0} + Failed to open {0}. diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/WebSearchCommandsProvider.cs b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/WebSearchCommandsProvider.cs index 7772d9b8b3..6768ff8baf 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/WebSearchCommandsProvider.cs +++ b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/WebSearchCommandsProvider.cs @@ -14,6 +14,7 @@ public partial class WebSearchCommandsProvider : CommandProvider { private readonly SettingsManager _settingsManager = new(); private readonly FallbackExecuteSearchItem _fallbackItem; + private readonly FallbackOpenURLItem _openUrlFallbackItem; public WebSearchCommandsProvider() { @@ -23,6 +24,7 @@ public partial class WebSearchCommandsProvider : CommandProvider Settings = _settingsManager.Settings; _fallbackItem = new FallbackExecuteSearchItem(_settingsManager); + _openUrlFallbackItem = new FallbackOpenURLItem(_settingsManager); } public override ICommandItem[] TopLevelCommands() @@ -36,5 +38,5 @@ public partial class WebSearchCommandsProvider : CommandProvider ]; } - public override IFallbackCommandItem[]? FallbackCommands() => [_fallbackItem]; + public override IFallbackCommandItem[]? FallbackCommands() => [_openUrlFallbackItem, _fallbackItem]; }