mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-05 02:36:19 +02:00
Rename the [Ee]xts dir to ext (#38852)
**WARNING:** This PR will probably blow up all in-flight PRs at some point in the early days of CmdPal, two of us created seperate `Exts` and `exts` dirs. Depending on what the casing was on the branch that you checked one of those out from, it'd get stuck like that on your PC forever. Windows didn't care, so we never noticed. But GitHub does care, and now browsing the source on GitHub is basically impossible. Closes #38081
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 6.4 KiB |
@@ -0,0 +1,46 @@
|
||||
<svg width="17" height="18" viewBox="0 0 17 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.5 16.5C12.9183 16.5 16.5 12.9183 16.5 8.5C16.5 4.08172 12.9183 0.5 8.5 0.5C4.08172 0.5 0.5 4.08172 0.5 8.5C0.5 12.9183 4.08172 16.5 8.5 16.5Z" fill="url(#paint0_linear_1874_17772)"/>
|
||||
<g style="mix-blend-mode:overlay">
|
||||
<mask id="mask0_1874_17772" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="17" height="17">
|
||||
<path d="M8.5 16.5C12.9183 16.5 16.5 12.9183 16.5 8.5C16.5 4.08172 12.9183 0.5 8.5 0.5C4.08172 0.5 0.5 4.08172 0.5 8.5C0.5 12.9183 4.08172 16.5 8.5 16.5Z" fill="url(#paint1_linear_1874_17772)"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_1874_17772)">
|
||||
<g opacity="0.4">
|
||||
<path d="M8.50093 16.9422C10.2322 16.9422 11.6356 13.1618 11.6356 8.49844C11.6356 3.83508 10.2322 0.0546875 8.50093 0.0546875C6.76967 0.0546875 5.36621 3.83508 5.36621 8.49844C5.36621 13.1618 6.76967 16.9422 8.50093 16.9422Z" stroke="white" stroke-width="0.888889" stroke-miterlimit="10"/>
|
||||
<path d="M-1.27734 10.7227H17.3893" stroke="white" stroke-width="0.888889" stroke-miterlimit="10"/>
|
||||
<path d="M-0.388672 6.27734H16.9447" stroke="white" stroke-width="0.888889" stroke-miterlimit="10"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g filter="url(#filter0_dd_1874_17772)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.49414 8.79454C3.49414 10.7549 5.0596 12.318 6.95878 12.318C8.85796 12.318 10.4234 10.7549 10.4234 8.79454C10.4234 6.8342 8.85796 5.27105 6.95878 5.27105C5.0596 5.27105 3.49414 6.8342 3.49414 8.79454ZM2.27778 8.79454C2.27778 11.4123 4.37353 13.5344 6.95878 13.5344C8.07111 13.5344 9.09282 13.1415 9.89597 12.4854L13.1894 15.8203C13.4314 16.0653 13.8237 16.0653 14.0656 15.8203C14.3076 15.5753 14.3076 15.1781 14.0656 14.9331L10.7498 11.5756C11.3096 10.7944 11.6398 9.83353 11.6398 8.79454C11.6398 6.17679 9.54403 4.05469 6.95878 4.05469C4.37353 4.05469 2.27778 6.17679 2.27778 8.79454Z" fill="url(#paint2_linear_1874_17772)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_dd_1874_17772" x="1.5678" y="3.69991" width="13.3888" height="13.3683" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="0.354773"/>
|
||||
<feGaussianBlur stdDeviation="0.354773"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.32 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1874_17772"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="0.0709546"/>
|
||||
<feGaussianBlur stdDeviation="0.0354773"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.24 0"/>
|
||||
<feBlend mode="normal" in2="effect1_dropShadow_1874_17772" result="effect2_dropShadow_1874_17772"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_1874_17772" result="shape"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_1874_17772" x1="4.05556" y1="3.16667" x2="15.6111" y2="13.8333" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#30CCD7"/>
|
||||
<stop offset="1" stop-color="#0669BC"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_1874_17772" x1="4.05556" y1="3.16667" x2="15.6111" y2="13.8333" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#30CCD7"/>
|
||||
<stop offset="1" stop-color="#0669BC"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_1874_17772" x1="3.84394" y1="4.05469" x2="14.6506" y2="16.7175" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#E7ECF1"/>
|
||||
<stop offset="1" stop-color="#CBCCCD"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// 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 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.Commands;
|
||||
|
||||
internal sealed partial class SearchWebCommand : InvokableCommand
|
||||
{
|
||||
private readonly SettingsManager _settingsManager;
|
||||
|
||||
public string Arguments { get; internal set; } = string.Empty;
|
||||
|
||||
internal SearchWebCommand(string arguments, SettingsManager settingsManager)
|
||||
{
|
||||
Arguments = arguments;
|
||||
BrowserInfo.UpdateIfTimePassed();
|
||||
Icon = IconHelpers.FromRelativePath("Assets\\WebSearch.png");
|
||||
Name = Properties.Resources.open_in_default_browser;
|
||||
_settingsManager = settingsManager;
|
||||
}
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
if (!ShellHelpers.OpenCommandInShell(BrowserInfo.Path, BrowserInfo.ArgumentsPattern, $"? {Arguments}"))
|
||||
{
|
||||
// TODO GH# 138 --> actually display feedback from the extension somewhere.
|
||||
return CommandResult.KeepOpen();
|
||||
}
|
||||
|
||||
if (_settingsManager.ShowHistory != Resources.history_none)
|
||||
{
|
||||
_settingsManager.SaveHistory(new HistoryItem(Arguments, DateTime.Now));
|
||||
}
|
||||
|
||||
return CommandResult.Dismiss();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// 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.Globalization;
|
||||
using System.Text;
|
||||
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.Commands;
|
||||
|
||||
internal sealed partial class FallbackExecuteSearchItem : FallbackCommandItem
|
||||
{
|
||||
private readonly SearchWebCommand _executeItem;
|
||||
private static readonly CompositeFormat PluginOpen = System.Text.CompositeFormat.Parse(Properties.Resources.plugin_open);
|
||||
|
||||
public FallbackExecuteSearchItem(SettingsManager settings)
|
||||
: base(new SearchWebCommand(string.Empty, settings), Resources.command_item_title)
|
||||
{
|
||||
_executeItem = (SearchWebCommand)this.Command!;
|
||||
Title = string.Empty;
|
||||
_executeItem.Name = string.Empty;
|
||||
Subtitle = string.Format(CultureInfo.CurrentCulture, PluginOpen, BrowserInfo.Name ?? BrowserInfo.MSEdgeName);
|
||||
Icon = IconHelpers.FromRelativePath("Assets\\WebSearch.png");
|
||||
}
|
||||
|
||||
public override void UpdateQuery(string query)
|
||||
{
|
||||
_executeItem.Arguments = query;
|
||||
_executeItem.Name = string.IsNullOrEmpty(query) ? string.Empty : Properties.Resources.open_in_default_browser;
|
||||
Title = query;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
// 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.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.WebSearch.Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// Contains information (e.g. path to executable, name...) about the default browser.
|
||||
/// </summary>
|
||||
public static class DefaultBrowserInfo
|
||||
{
|
||||
private static readonly Lock _updateLock = new();
|
||||
|
||||
/// <summary>Gets the path to the MS Edge browser executable.</summary>
|
||||
public static string MSEdgePath => System.IO.Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
|
||||
@"Microsoft\Edge\Application\msedge.exe");
|
||||
|
||||
/// <summary>Gets the command line pattern of the MS Edge.</summary>
|
||||
public const string MSEdgeArgumentsPattern = "--single-argument %1";
|
||||
|
||||
public const string MSEdgeName = "Microsoft Edge";
|
||||
|
||||
/// <summary>Gets the path to default browser's executable.</summary>
|
||||
public static string? Path { get; private set; }
|
||||
|
||||
/// <summary>Gets <see cref="Path"/> since the icon is embedded in the executable.</summary>
|
||||
public static string? IconPath => Path;
|
||||
|
||||
/// <summary>Gets the user-friendly name of the default browser.</summary>
|
||||
public static string? Name { get; private set; }
|
||||
|
||||
/// <summary>Gets the command line pattern of the default browser.</summary>
|
||||
public static string? ArgumentsPattern { get; private set; }
|
||||
|
||||
public static bool IsDefaultBrowserSet => !string.IsNullOrEmpty(Path);
|
||||
|
||||
public const long UpdateTimeout = 300;
|
||||
|
||||
private static long _lastUpdateTickCount = -UpdateTimeout;
|
||||
|
||||
private static bool _updatedOnce;
|
||||
private static bool _errorLogged;
|
||||
|
||||
/// <summary>
|
||||
/// Updates only if at least more than 300ms has passed since the last update, to avoid multiple calls to <see cref="Update"/>.
|
||||
/// (because of multiple plugins calling update at the same time.)
|
||||
/// </summary>
|
||||
public static void UpdateIfTimePassed()
|
||||
{
|
||||
var curTickCount = Environment.TickCount64;
|
||||
if (curTickCount - _lastUpdateTickCount >= UpdateTimeout)
|
||||
{
|
||||
_lastUpdateTickCount = curTickCount;
|
||||
Update();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Consider using <see cref="UpdateIfTimePassed"/> to avoid updating multiple times.
|
||||
/// (because of multiple plugins calling update at the same time.)
|
||||
/// </summary>
|
||||
public static void Update()
|
||||
{
|
||||
lock (_updateLock)
|
||||
{
|
||||
if (!_updatedOnce)
|
||||
{
|
||||
// Log.Info("I've tried updating the chosen Web Browser info at least once.", typeof(DefaultBrowserInfo));
|
||||
_updatedOnce = true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var progId = GetRegistryValue(
|
||||
@"HKEY_CURRENT_USER\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice",
|
||||
"ProgId");
|
||||
var appName = GetRegistryValue($@"HKEY_CLASSES_ROOT\{progId}\Application", "ApplicationName")
|
||||
?? GetRegistryValue($@"HKEY_CLASSES_ROOT\{progId}", "FriendlyTypeName");
|
||||
|
||||
if (appName != null)
|
||||
{
|
||||
// Handle indirect strings:
|
||||
if (appName.StartsWith('@'))
|
||||
{
|
||||
appName = GetIndirectString(appName);
|
||||
}
|
||||
|
||||
appName = appName
|
||||
.Replace("URL", null, StringComparison.OrdinalIgnoreCase)
|
||||
.Replace("HTML", null, StringComparison.OrdinalIgnoreCase)
|
||||
.Replace("Document", null, StringComparison.OrdinalIgnoreCase)
|
||||
.Replace("Web", null, StringComparison.OrdinalIgnoreCase)
|
||||
.TrimEnd();
|
||||
}
|
||||
|
||||
Name = appName;
|
||||
|
||||
var commandPattern = GetRegistryValue($@"HKEY_CLASSES_ROOT\{progId}\shell\open\command", null);
|
||||
|
||||
if (string.IsNullOrEmpty(commandPattern))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(
|
||||
nameof(commandPattern),
|
||||
"Default browser program command is not specified.");
|
||||
}
|
||||
|
||||
if (commandPattern.StartsWith('@'))
|
||||
{
|
||||
commandPattern = GetIndirectString(commandPattern);
|
||||
}
|
||||
|
||||
// HACK: for firefox installed through Microsoft store
|
||||
// When installed through Microsoft Firefox the commandPattern does not have
|
||||
// quotes for the path. As the Program Files does have a space
|
||||
// the extracted path would be invalid, here we add the quotes to fix it
|
||||
const string FirefoxExecutableName = "firefox.exe";
|
||||
if (commandPattern.Contains(FirefoxExecutableName) && commandPattern.Contains(@"\WindowsApps\") && (!commandPattern.StartsWith('\"')))
|
||||
{
|
||||
var pathEndIndex = commandPattern.IndexOf(FirefoxExecutableName, StringComparison.Ordinal) + FirefoxExecutableName.Length;
|
||||
commandPattern = commandPattern.Insert(pathEndIndex, "\"");
|
||||
commandPattern = commandPattern.Insert(0, "\"");
|
||||
}
|
||||
|
||||
if (commandPattern.StartsWith('\"'))
|
||||
{
|
||||
var endQuoteIndex = commandPattern.IndexOf('\"', 1);
|
||||
if (endQuoteIndex != -1)
|
||||
{
|
||||
Path = commandPattern.Substring(1, endQuoteIndex - 1);
|
||||
ArgumentsPattern = commandPattern.Substring(endQuoteIndex + 1).Trim();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var spaceIndex = commandPattern.IndexOf(' ');
|
||||
if (spaceIndex != -1)
|
||||
{
|
||||
Path = commandPattern.Substring(0, spaceIndex);
|
||||
ArgumentsPattern = commandPattern.Substring(spaceIndex + 1).Trim();
|
||||
}
|
||||
}
|
||||
|
||||
// Packaged applications could be an URI. Example: shell:AppsFolder\Microsoft.MicrosoftEdge.Stable_8wekyb3d8bbwe!App
|
||||
if (!System.IO.Path.Exists(Path) && !Uri.TryCreate(Path, UriKind.Absolute, out _))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"Command validation failed: {commandPattern}",
|
||||
nameof(commandPattern));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(Path))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(
|
||||
nameof(Path),
|
||||
"Default browser program path could not be determined.");
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Fallback to MS Edge
|
||||
Path = MSEdgePath;
|
||||
Name = MSEdgeName;
|
||||
ArgumentsPattern = MSEdgeArgumentsPattern;
|
||||
|
||||
if (!_errorLogged)
|
||||
{
|
||||
// Log.Exception("Exception when retrieving browser path/name. Path and Name are set to use Microsoft Edge.", e, typeof(DefaultBrowserInfo));
|
||||
_errorLogged = true;
|
||||
}
|
||||
}
|
||||
|
||||
string? GetRegistryValue(string registryLocation, string? valueName)
|
||||
{
|
||||
return Microsoft.Win32.Registry.GetValue(registryLocation, valueName, null) as string;
|
||||
}
|
||||
|
||||
string GetIndirectString(string str)
|
||||
{
|
||||
var stringBuilder = new StringBuilder(128);
|
||||
unsafe
|
||||
{
|
||||
var buffer = stackalloc char[128];
|
||||
var capacity = 128;
|
||||
void* reserved = null;
|
||||
|
||||
// S_OK == 0
|
||||
if (global::Windows.Win32.PInvoke.SHLoadIndirectString(
|
||||
str,
|
||||
buffer,
|
||||
(uint)capacity,
|
||||
ref reserved)
|
||||
== 0)
|
||||
{
|
||||
return new string(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
throw new ArgumentNullException(nameof(str), "Could not load indirect string.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// 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.Text.Json;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.WebSearch.Helpers;
|
||||
|
||||
public class HistoryItem(string searchString, DateTime timestamp)
|
||||
{
|
||||
public string SearchString { get; private set; } = searchString;
|
||||
|
||||
public DateTime Timestamp { get; private set; } = timestamp;
|
||||
|
||||
public string ToJson() => JsonSerializer.Serialize(this);
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
// 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.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Microsoft.CmdPal.Ext.WebSearch.Commands;
|
||||
using Microsoft.CmdPal.Ext.WebSearch.Properties;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.WebSearch.Helpers;
|
||||
|
||||
public class SettingsManager : JsonSettingsManager
|
||||
{
|
||||
private readonly string _historyPath;
|
||||
|
||||
private static readonly string _namespace = "websearch";
|
||||
|
||||
private static string Namespaced(string propertyName) => $"{_namespace}.{propertyName}";
|
||||
|
||||
private static readonly List<ChoiceSetSetting.Choice> _choices =
|
||||
[
|
||||
new ChoiceSetSetting.Choice(Resources.history_none, Resources.history_none),
|
||||
new ChoiceSetSetting.Choice(Resources.history_1, Resources.history_1),
|
||||
new ChoiceSetSetting.Choice(Resources.history_5, Resources.history_5),
|
||||
new ChoiceSetSetting.Choice(Resources.history_10, Resources.history_10),
|
||||
new ChoiceSetSetting.Choice(Resources.history_20, Resources.history_20),
|
||||
];
|
||||
|
||||
private readonly ToggleSetting _globalIfURI = new(
|
||||
Namespaced(nameof(GlobalIfURI)),
|
||||
Resources.plugin_global_if_uri,
|
||||
Resources.plugin_global_if_uri,
|
||||
false);
|
||||
|
||||
private readonly ChoiceSetSetting _showHistory = new(
|
||||
Namespaced(nameof(ShowHistory)),
|
||||
Resources.plugin_show_history,
|
||||
Resources.plugin_show_history,
|
||||
_choices);
|
||||
|
||||
public bool GlobalIfURI => _globalIfURI.Value;
|
||||
|
||||
public string ShowHistory => _showHistory.Value ?? string.Empty;
|
||||
|
||||
internal static string SettingsJsonPath()
|
||||
{
|
||||
var directory = Utilities.BaseSettingsPath("Microsoft.CmdPal");
|
||||
Directory.CreateDirectory(directory);
|
||||
|
||||
// now, the state is just next to the exe
|
||||
return Path.Combine(directory, "settings.json");
|
||||
}
|
||||
|
||||
internal static string HistoryStateJsonPath()
|
||||
{
|
||||
var directory = Utilities.BaseSettingsPath("Microsoft.CmdPal");
|
||||
Directory.CreateDirectory(directory);
|
||||
|
||||
// now, the state is just next to the exe
|
||||
return Path.Combine(directory, "websearch_history.json");
|
||||
}
|
||||
|
||||
public void SaveHistory(HistoryItem historyItem)
|
||||
{
|
||||
if (historyItem == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
List<HistoryItem> historyItems;
|
||||
|
||||
// Check if the file exists and load existing history
|
||||
if (File.Exists(_historyPath))
|
||||
{
|
||||
var existingContent = File.ReadAllText(_historyPath);
|
||||
historyItems = JsonSerializer.Deserialize<List<HistoryItem>>(existingContent) ?? [];
|
||||
}
|
||||
else
|
||||
{
|
||||
historyItems = [];
|
||||
}
|
||||
|
||||
// Add the new history item
|
||||
historyItems.Add(historyItem);
|
||||
|
||||
// Determine the maximum number of items to keep based on ShowHistory
|
||||
if (int.TryParse(ShowHistory, out var maxHistoryItems) && maxHistoryItems > 0)
|
||||
{
|
||||
// Keep only the most recent `maxHistoryItems` items
|
||||
while (historyItems.Count > maxHistoryItems)
|
||||
{
|
||||
historyItems.RemoveAt(0); // Remove the oldest item
|
||||
}
|
||||
}
|
||||
|
||||
// Serialize the updated list back to JSON and save it
|
||||
var historyJson = JsonSerializer.Serialize(historyItems);
|
||||
File.WriteAllText(_historyPath, historyJson);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExtensionHost.LogMessage(new LogMessage() { Message = ex.ToString() });
|
||||
}
|
||||
}
|
||||
|
||||
public List<ListItem> LoadHistory()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!File.Exists(_historyPath))
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
// Read and deserialize JSON into a list of HistoryItem objects
|
||||
var fileContent = File.ReadAllText(_historyPath);
|
||||
var historyItems = JsonSerializer.Deserialize<List<HistoryItem>>(fileContent) ?? [];
|
||||
|
||||
// Convert each HistoryItem to a ListItem
|
||||
var listItems = new List<ListItem>();
|
||||
foreach (var historyItem in historyItems)
|
||||
{
|
||||
listItems.Add(new ListItem(new SearchWebCommand(historyItem.SearchString, this))
|
||||
{
|
||||
Title = historyItem.SearchString,
|
||||
Subtitle = historyItem.Timestamp.ToString("g", CultureInfo.InvariantCulture), // Ensures consistent formatting
|
||||
});
|
||||
}
|
||||
|
||||
listItems.Reverse();
|
||||
return listItems;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExtensionHost.LogMessage(new LogMessage() { Message = ex.ToString() });
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public SettingsManager()
|
||||
{
|
||||
FilePath = SettingsJsonPath();
|
||||
_historyPath = HistoryStateJsonPath();
|
||||
|
||||
Settings.Add(_globalIfURI);
|
||||
Settings.Add(_showHistory);
|
||||
|
||||
// Load settings from file upon initialization
|
||||
LoadSettings();
|
||||
|
||||
Settings.SettingsChanged += (s, a) => this.SaveSettings();
|
||||
}
|
||||
|
||||
private void ClearHistory()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(_historyPath))
|
||||
{
|
||||
// Delete the history file
|
||||
File.Delete(_historyPath);
|
||||
|
||||
// Log that the history was successfully cleared
|
||||
ExtensionHost.LogMessage(new LogMessage() { Message = "History cleared successfully." });
|
||||
}
|
||||
else
|
||||
{
|
||||
// Log that there was no history file to delete
|
||||
ExtensionHost.LogMessage(new LogMessage() { Message = "No history file found to clear." });
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Log any exception that occurs
|
||||
ExtensionHost.LogMessage(new LogMessage() { Message = $"Failed to clear history: {ex}" });
|
||||
}
|
||||
}
|
||||
|
||||
public override void SaveSettings()
|
||||
{
|
||||
base.SaveSettings();
|
||||
try
|
||||
{
|
||||
if (ShowHistory == Resources.history_none)
|
||||
{
|
||||
ClearHistory();
|
||||
}
|
||||
else if (int.TryParse(ShowHistory, out var maxHistoryItems) && maxHistoryItems > 0)
|
||||
{
|
||||
// Trim the history file if there are more items than the new limit
|
||||
if (File.Exists(_historyPath))
|
||||
{
|
||||
var existingContent = File.ReadAllText(_historyPath);
|
||||
var historyItems = JsonSerializer.Deserialize<List<HistoryItem>>(existingContent) ?? [];
|
||||
|
||||
// Check if trimming is needed
|
||||
if (historyItems.Count > maxHistoryItems)
|
||||
{
|
||||
// Trim the list to keep only the most recent `maxHistoryItems` items
|
||||
historyItems = historyItems.Skip(historyItems.Count - maxHistoryItems).ToList();
|
||||
|
||||
// Save the trimmed history back to the file
|
||||
var trimmedHistoryJson = JsonSerializer.Serialize(historyItems);
|
||||
File.WriteAllText(_historyPath, trimmedHistoryJson);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExtensionHost.LogMessage(new LogMessage() { Message = ex.ToString() });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<PropertyGroup>
|
||||
<RootNamespace>Microsoft.CmdPal.Ext.WebSearch</RootNamespace>
|
||||
<Nullable>enable</Nullable>
|
||||
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\CmdPal</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\extensionsdk\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Assets\WebSearch.svg" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Update="Assets\WebSearch.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\WebSearch.svg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1 @@
|
||||
SHLoadIndirectString
|
||||
@@ -0,0 +1,99 @@
|
||||
// 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.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
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;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using BrowserInfo = Microsoft.CmdPal.Ext.WebSearch.Helpers.DefaultBrowserInfo;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.WebSearch.Pages;
|
||||
|
||||
internal sealed partial class WebSearchListPage : DynamicListPage
|
||||
{
|
||||
private readonly string _iconPath = string.Empty;
|
||||
private readonly List<ListItem>? _historyItems;
|
||||
private readonly SettingsManager _settingsManager;
|
||||
private static readonly CompositeFormat PluginInBrowserName = System.Text.CompositeFormat.Parse(Properties.Resources.plugin_in_browser_name);
|
||||
private static readonly CompositeFormat PluginOpen = System.Text.CompositeFormat.Parse(Properties.Resources.plugin_open);
|
||||
private List<ListItem> allItems;
|
||||
|
||||
public WebSearchListPage(SettingsManager settingsManager)
|
||||
{
|
||||
Name = Resources.command_item_title;
|
||||
Title = Resources.command_item_title;
|
||||
PlaceholderText = Resources.plugin_description;
|
||||
Icon = IconHelpers.FromRelativePath("Assets\\WebSearch.png");
|
||||
allItems = [new(new NoOpCommand())
|
||||
{
|
||||
Icon = IconHelpers.FromRelativePath("Assets\\WebSearch.png"),
|
||||
Title = Properties.Resources.plugin_description,
|
||||
Subtitle = string.Format(CultureInfo.CurrentCulture, PluginOpen, BrowserInfo.Name ?? BrowserInfo.MSEdgeName),
|
||||
}
|
||||
];
|
||||
Id = "com.microsoft.cmdpal.websearch";
|
||||
_settingsManager = settingsManager;
|
||||
_historyItems = _settingsManager.ShowHistory != Resources.history_none ? _settingsManager.LoadHistory() : null;
|
||||
if (_historyItems != null)
|
||||
{
|
||||
allItems.AddRange(_historyItems);
|
||||
}
|
||||
}
|
||||
|
||||
public List<ListItem> Query(string query)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(query);
|
||||
IEnumerable<ListItem>? filteredHistoryItems = null;
|
||||
|
||||
if (_historyItems != null)
|
||||
{
|
||||
filteredHistoryItems = _settingsManager.ShowHistory != Resources.history_none ? ListHelpers.FilterList(_historyItems, query).OfType<ListItem>() : null;
|
||||
}
|
||||
|
||||
var results = new List<ListItem>();
|
||||
|
||||
// empty query
|
||||
if (string.IsNullOrEmpty(query))
|
||||
{
|
||||
results.Add(new ListItem(new SearchWebCommand(string.Empty, _settingsManager))
|
||||
{
|
||||
Title = Properties.Resources.plugin_description,
|
||||
Subtitle = string.Format(CultureInfo.CurrentCulture, PluginInBrowserName, BrowserInfo.Name ?? BrowserInfo.MSEdgeName),
|
||||
Icon = new IconInfo(_iconPath),
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
var searchTerm = query;
|
||||
var result = new ListItem(new SearchWebCommand(searchTerm, _settingsManager))
|
||||
{
|
||||
Title = searchTerm,
|
||||
Subtitle = string.Format(CultureInfo.CurrentCulture, PluginOpen, BrowserInfo.Name ?? BrowserInfo.MSEdgeName),
|
||||
Icon = new IconInfo(_iconPath),
|
||||
};
|
||||
results.Add(result);
|
||||
}
|
||||
|
||||
if (filteredHistoryItems != null)
|
||||
{
|
||||
results.AddRange(filteredHistoryItems);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public override void UpdateSearchText(string oldSearch, string newSearch)
|
||||
{
|
||||
allItems = [.. Query(newSearch)];
|
||||
RaiseItemsChanged(0);
|
||||
}
|
||||
|
||||
public override IListItem[] GetItems() => [.. allItems];
|
||||
}
|
||||
243
src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Properties/Resources.Designer.cs
generated
Normal file
243
src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Properties/Resources.Designer.cs
generated
Normal file
@@ -0,0 +1,243 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.WebSearch.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
public class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
public static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.CmdPal.Ext.WebSearch.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
public static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Search the Web.
|
||||
/// </summary>
|
||||
public static string command_item_title {
|
||||
get {
|
||||
return ResourceManager.GetString("command_item_title", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Web Search.
|
||||
/// </summary>
|
||||
public static string extension_name {
|
||||
get {
|
||||
return ResourceManager.GetString("extension_name", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to 1.
|
||||
/// </summary>
|
||||
public static string history_1 {
|
||||
get {
|
||||
return ResourceManager.GetString("history_1", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to 10.
|
||||
/// </summary>
|
||||
public static string history_10 {
|
||||
get {
|
||||
return ResourceManager.GetString("history_10", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to 20.
|
||||
/// </summary>
|
||||
public static string history_20 {
|
||||
get {
|
||||
return ResourceManager.GetString("history_20", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to 5.
|
||||
/// </summary>
|
||||
public static string history_5 {
|
||||
get {
|
||||
return ResourceManager.GetString("history_5", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to None.
|
||||
/// </summary>
|
||||
public static string history_none {
|
||||
get {
|
||||
return ResourceManager.GetString("history_none", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Open in default browser.
|
||||
/// </summary>
|
||||
public static string open_in_default_browser {
|
||||
get {
|
||||
return ResourceManager.GetString("open_in_default_browser", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to the default browser.
|
||||
/// </summary>
|
||||
public static string plugin_browser {
|
||||
get {
|
||||
return ResourceManager.GetString("plugin_browser", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Searches the web with your default search engine.
|
||||
/// </summary>
|
||||
public static string plugin_description {
|
||||
get {
|
||||
return ResourceManager.GetString("plugin_description", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Don't include in global results on queries that are URIs.
|
||||
/// </summary>
|
||||
public static string plugin_global_if_uri {
|
||||
get {
|
||||
return ResourceManager.GetString("plugin_global_if_uri", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to In the default browser.
|
||||
/// </summary>
|
||||
public static string plugin_in_browser {
|
||||
get {
|
||||
return ResourceManager.GetString("plugin_in_browser", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to In {0}.
|
||||
/// </summary>
|
||||
public static string plugin_in_browser_name {
|
||||
get {
|
||||
return ResourceManager.GetString("plugin_in_browser_name", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Web Search.
|
||||
/// </summary>
|
||||
public static string plugin_name {
|
||||
get {
|
||||
return ResourceManager.GetString("plugin_name", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Search the web in {0}.
|
||||
/// </summary>
|
||||
public static string plugin_open {
|
||||
get {
|
||||
return ResourceManager.GetString("plugin_open", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Open "{0}".
|
||||
/// </summary>
|
||||
public static string plugin_open_url {
|
||||
get {
|
||||
return ResourceManager.GetString("plugin_open_url", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Open url in {0}.
|
||||
/// </summary>
|
||||
public static string plugin_open_url_in_browser {
|
||||
get {
|
||||
return ResourceManager.GetString("plugin_open_url_in_browser", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Failed to open {0}..
|
||||
/// </summary>
|
||||
public static string plugin_search_failed {
|
||||
get {
|
||||
return ResourceManager.GetString("plugin_search_failed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Determines the number of history items to show from previous searches.
|
||||
/// </summary>
|
||||
public static string plugin_show_history {
|
||||
get {
|
||||
return ResourceManager.GetString("plugin_show_history", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Settings.
|
||||
/// </summary>
|
||||
public static string settings_page_name {
|
||||
get {
|
||||
return ResourceManager.GetString("settings_page_name", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="command_item_title" xml:space="preserve">
|
||||
<value>Search the Web</value>
|
||||
</data>
|
||||
<data name="extension_name" xml:space="preserve">
|
||||
<value>Web Search</value>
|
||||
</data>
|
||||
<data name="history_1" xml:space="preserve">
|
||||
<value>1</value>
|
||||
</data>
|
||||
<data name="history_10" xml:space="preserve">
|
||||
<value>10</value>
|
||||
</data>
|
||||
<data name="history_20" xml:space="preserve">
|
||||
<value>20</value>
|
||||
</data>
|
||||
<data name="history_5" xml:space="preserve">
|
||||
<value>5</value>
|
||||
</data>
|
||||
<data name="history_none" xml:space="preserve">
|
||||
<value>None</value>
|
||||
</data>
|
||||
<data name="open_in_default_browser" xml:space="preserve">
|
||||
<value>Open in default browser</value>
|
||||
</data>
|
||||
<data name="plugin_browser" xml:space="preserve">
|
||||
<value>the default browser</value>
|
||||
</data>
|
||||
<data name="plugin_description" xml:space="preserve">
|
||||
<value>Searches the web with your default search engine</value>
|
||||
</data>
|
||||
<data name="plugin_global_if_uri" xml:space="preserve">
|
||||
<value>Don't include in global results on queries that are URIs</value>
|
||||
</data>
|
||||
<data name="plugin_in_browser" xml:space="preserve">
|
||||
<value>In the default browser</value>
|
||||
</data>
|
||||
<data name="plugin_in_browser_name" xml:space="preserve">
|
||||
<value>In {0}</value>
|
||||
<comment>Like "Search the web in {the browser name}"</comment>
|
||||
</data>
|
||||
<data name="plugin_name" xml:space="preserve">
|
||||
<value>Web Search</value>
|
||||
</data>
|
||||
<data name="plugin_open" xml:space="preserve">
|
||||
<value>Search the web in {0}</value>
|
||||
</data>
|
||||
<data name="plugin_open_url" xml:space="preserve">
|
||||
<value>Open "{0}"</value>
|
||||
</data>
|
||||
<data name="plugin_open_url_in_browser" xml:space="preserve">
|
||||
<value>Open url in {0}</value>
|
||||
</data>
|
||||
<data name="plugin_search_failed" xml:space="preserve">
|
||||
<value>Failed to open {0}.</value>
|
||||
</data>
|
||||
<data name="plugin_show_history" xml:space="preserve">
|
||||
<value>Determines the number of history items to show from previous searches</value>
|
||||
</data>
|
||||
<data name="settings_page_name" xml:space="preserve">
|
||||
<value>Settings</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -0,0 +1,42 @@
|
||||
// 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.Commands;
|
||||
using Microsoft.CmdPal.Ext.WebSearch.Helpers;
|
||||
using Microsoft.CmdPal.Ext.WebSearch.Properties;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.WebSearch;
|
||||
|
||||
public partial class WebSearchCommandsProvider : CommandProvider
|
||||
{
|
||||
private readonly SettingsManager _settingsManager = new();
|
||||
private readonly FallbackExecuteSearchItem _fallbackItem;
|
||||
private readonly FallbackOpenURLItem _openUrlFallbackItem;
|
||||
|
||||
public WebSearchCommandsProvider()
|
||||
{
|
||||
Id = "WebSearch";
|
||||
DisplayName = Resources.extension_name;
|
||||
Icon = IconHelpers.FromRelativePath("Assets\\WebSearch.png");
|
||||
Settings = _settingsManager.Settings;
|
||||
|
||||
_fallbackItem = new FallbackExecuteSearchItem(_settingsManager);
|
||||
_openUrlFallbackItem = new FallbackOpenURLItem(_settingsManager);
|
||||
}
|
||||
|
||||
public override ICommandItem[] TopLevelCommands()
|
||||
{
|
||||
return [new WebSearchTopLevelCommandItem(_settingsManager)
|
||||
{
|
||||
MoreCommands = [
|
||||
new CommandContextItem(Settings!.SettingsPage),
|
||||
],
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
public override IFallbackCommandItem[]? FallbackCommands() => [_openUrlFallbackItem, _fallbackItem];
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// 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.IO;
|
||||
using Microsoft.CmdPal.Ext.WebSearch.Commands;
|
||||
using Microsoft.CmdPal.Ext.WebSearch.Helpers;
|
||||
using Microsoft.CmdPal.Ext.WebSearch.Pages;
|
||||
using Microsoft.CmdPal.Ext.WebSearch.Properties;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.WebSearch;
|
||||
|
||||
public partial class WebSearchTopLevelCommandItem : CommandItem, IFallbackHandler
|
||||
{
|
||||
private readonly SettingsManager _settingsManager;
|
||||
|
||||
public WebSearchTopLevelCommandItem(SettingsManager settingsManager)
|
||||
: base(new WebSearchListPage(settingsManager))
|
||||
{
|
||||
Icon = IconHelpers.FromRelativePath("Assets\\WebSearch.png");
|
||||
SetDefaultTitle();
|
||||
_settingsManager = settingsManager;
|
||||
}
|
||||
|
||||
private void SetDefaultTitle() => Title = Resources.command_item_title;
|
||||
|
||||
public void UpdateQuery(string query)
|
||||
{
|
||||
if (string.IsNullOrEmpty(query))
|
||||
{
|
||||
SetDefaultTitle();
|
||||
Command = new WebSearchListPage(_settingsManager);
|
||||
}
|
||||
else
|
||||
{
|
||||
Title = query;
|
||||
Command = new SearchWebCommand(query, _settingsManager);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user