Skip to content
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
using Microsoft.CommandPalette.Extensions.Toolkit;
using WebSearchShortcut.Browsers;
using WebSearchShortcut.Helpers;
using WebSearchShortcut.Properties;

namespace WebSearchShortcut.Commands;

Expand All @@ -12,8 +10,8 @@ internal sealed partial class OpenHomePageCommand : InvokableCommand

internal OpenHomePageCommand(WebSearchShortcutDataEntry shortcut)
{
Name = StringFormatter.Format(Resources.OpenHomePage_NameTemplate, new() { ["engine"] = shortcut.Name });
Icon = Icons.Search;
Name = $"[UNBOUND] {nameof(OpenHomePageCommand)}.{nameof(Name)} required - shortcut='{shortcut.Name}'";

_shortcut = shortcut;
_browserInfo = new BrowserExecutionInfo(shortcut);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using Microsoft.CommandPalette.Extensions.Toolkit;
using WebSearchShortcut.Browsers;
using WebSearchShortcut.Helpers;
using WebSearchShortcut.Properties;
using WebSearchShortcut.History;

namespace WebSearchShortcut.Commands;

Expand All @@ -14,12 +13,11 @@ internal sealed partial class SearchWebCommand : InvokableCommand

public SearchWebCommand(WebSearchShortcutDataEntry shortcut, string query)
{
Name = StringFormatter.Format(Resources.SearchQuery_NameTemplate, new() { ["engine"] = shortcut.Name, ["query"] = query });
Icon = Icons.Search;
Name = $"[UNBOUND] {nameof(SearchWebCommand)}.{nameof(Name)} required - shortcut='{shortcut.Name}', query='{query}'";

_query = query;
_shortcut = shortcut;
_browserInfo = new BrowserExecutionInfo(shortcut);
// _settingsManager = settingsManager;
}

public override CommandResult Invoke()
Expand All @@ -30,10 +28,8 @@ public override CommandResult Invoke()
return CommandResult.KeepOpen();
}

// if (_settingsManager.ShowHistory != Resources.history_none)
// {
// _settingsManager.SaveHistory(new HistoryItem(Arguments, DateTime.Now));
// }
if (_shortcut.RecordHistory ?? true)
HistoryService.Add(_shortcut.Name, _query);

return CommandResult.Dismiss();
}
Expand Down
81 changes: 46 additions & 35 deletions CmdPalWebSearchShortcut/WebSearchShortcut/Forms/AddShortcutForm.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using System;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Nodes;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Windows.Foundation;
using Microsoft.CommandPalette.Extensions.Toolkit;
using WebSearchShortcut.Browsers;
using WebSearchShortcut.Properties;

Expand All @@ -19,6 +20,7 @@ public AddShortcutForm(WebSearchShortcutDataEntry? shortcut)
var url = shortcut?.Url ?? string.Empty;
var suggestionProvider = shortcut?.SuggestionProvider ?? string.Empty;
var replaceWhitespace = shortcut?.ReplaceWhitespace ?? string.Empty;
var recordHistory = shortcut?.RecordHistory ?? true;
var homePage = shortcut?.HomePage ?? string.Empty;
var browserPath = shortcut?.BrowserPath ?? string.Empty;
var browserArgs = shortcut?.BrowserArgs ?? string.Empty;
Expand All @@ -32,88 +34,94 @@ public AddShortcutForm(WebSearchShortcutDataEntry? shortcut)
{
"id": "name",
"type": "Input.Text",
"label": {{JsonSerializer.Serialize(Resources.AddShortcutForm_NameLabel, AppJsonSerializerContext.Default.String)}},
"label": {{JsonSerializer.Serialize(Resources.AddShortcutForm_Name_Label, AppJsonSerializerContext.Default.String)}},
"value": {{JsonSerializer.Serialize(name, AppJsonSerializerContext.Default.String)}},
"isRequired": true,
"errorMessage": {{JsonSerializer.Serialize(Resources.AddShortcutForm_NameErrorMessage, AppJsonSerializerContext.Default.String)}}
"errorMessage": {{JsonSerializer.Serialize(Resources.AddShortcutForm_Name_ErrorMessage, AppJsonSerializerContext.Default.String)}}
},
{
"id": "url",
"type": "Input.Text",
"style": "Url",
"label": {{JsonSerializer.Serialize(Resources.AddShortcutForm_UrlLabel, AppJsonSerializerContext.Default.String)}},
"placeholder": {{JsonSerializer.Serialize(Resources.AddShortcutForm_UrlPlaceholder, AppJsonSerializerContext.Default.String)}},
"label": {{JsonSerializer.Serialize(Resources.AddShortcutForm_Url_Label, AppJsonSerializerContext.Default.String)}},
"placeholder": {{JsonSerializer.Serialize(Resources.AddShortcutForm_Url_Placeholder, AppJsonSerializerContext.Default.String)}},
"value": {{JsonSerializer.Serialize(url, AppJsonSerializerContext.Default.String)}},
"isRequired": true,
"errorMessage": {{JsonSerializer.Serialize(Resources.AddShortcutForm_UrlErrorMessage, AppJsonSerializerContext.Default.String)}}
"errorMessage": {{JsonSerializer.Serialize(Resources.AddShortcutForm_Url_ErrorMessage, AppJsonSerializerContext.Default.String)}}
},
{
"id": "suggestionProvider",
"type": "Input.ChoiceSet",
"label": {{JsonSerializer.Serialize(Resources.AddShortcutForm_SuggestionProviderLabel, AppJsonSerializerContext.Default.String)}},
"placeholder": {{JsonSerializer.Serialize(Resources.AddShortcutForm_SuggestionProviderPlaceholder, AppJsonSerializerContext.Default.String)}},
"label": {{JsonSerializer.Serialize(Resources.AddShortcutForm_SuggestionProvider_Label, AppJsonSerializerContext.Default.String)}},
"placeholder": {{JsonSerializer.Serialize(Resources.AddShortcutForm_SuggestionProvider_Placeholder, AppJsonSerializerContext.Default.String)}},
"choices": [
{
"title": {{JsonSerializer.Serialize(Resources.AddShortcutForm_SuggestionProviderNone, AppJsonSerializerContext.Default.String)}},
"title": {{JsonSerializer.Serialize(Resources.AddShortcutForm_SuggestionProvider_None, AppJsonSerializerContext.Default.String)}},
"value": ""
},
{{SuggestionsRegistry.ProviderNames.Select(key => $$"""
{{SuggestionsRegistry.ProviderNames.Select(key => $$"""
{
"title": {{JsonSerializer.Serialize(key, AppJsonSerializerContext.Default.String)}},
"value": {{JsonSerializer.Serialize(key, AppJsonSerializerContext.Default.String)}}
}
""").Aggregate((a, b) => a + "," + b)}}
""")
.Aggregate((a, b) => a + ",\n" + b)}}
],
"value": {{JsonSerializer.Serialize(suggestionProvider, AppJsonSerializerContext.Default.String)}},
"errorMessage": "// Just for space between items"
},
{
"id": "recordHistory",
"type": "Input.Toggle",
"label": {{JsonSerializer.Serialize(Resources.AddShortcutForm_RecordHistory_Label, AppJsonSerializerContext.Default.String)}},
"title": {{JsonSerializer.Serialize(Resources.AddShortcutForm_RecordHistory_Title, AppJsonSerializerContext.Default.String)}},
"value": {{JsonSerializer.Serialize(recordHistory ? "true" : "false", AppJsonSerializerContext.Default.String)}}
},
{
"id": "homePage",
"type": "Input.Text",
"style": "text",
"id": "replaceWhitespace",
"label": {{JsonSerializer.Serialize(Resources.AddShortcutForm_ReplaceWhitespaceLabel, AppJsonSerializerContext.Default.String)}},
"placeholder": {{JsonSerializer.Serialize(Resources.AddShortcutForm_ReplaceWhitespacePlaceholder, AppJsonSerializerContext.Default.String)}},
"value": {{JsonSerializer.Serialize(replaceWhitespace, AppJsonSerializerContext.Default.String)}},
"style": "Url",
"label": {{JsonSerializer.Serialize(Resources.AddShortcutForm_Homepage_Label, AppJsonSerializerContext.Default.String)}},
"placeholder": {{JsonSerializer.Serialize(Resources.AddShortcutForm_Homepage_Placeholder, AppJsonSerializerContext.Default.String)}},
"value": {{JsonSerializer.Serialize(homePage, AppJsonSerializerContext.Default.String)}},
"errorMessage": "// Just for space between items"
},
{
"id": "replaceWhitespace",
"type": "Input.Text",
"style": "text",
"id": "homePage",
"label": {{JsonSerializer.Serialize(Resources.AddShortcutForm_HomepageLabel, AppJsonSerializerContext.Default.String)}},
"placeholder": {{JsonSerializer.Serialize(Resources.AddShortcutForm_HomepagePlaceholder, AppJsonSerializerContext.Default.String)}},
"value": {{JsonSerializer.Serialize(homePage, AppJsonSerializerContext.Default.String)}},
"label": {{JsonSerializer.Serialize(Resources.AddShortcutForm_ReplaceWhitespace_Label, AppJsonSerializerContext.Default.String)}},
"placeholder": {{JsonSerializer.Serialize(Resources.AddShortcutForm_ReplaceWhitespace_Placeholder, AppJsonSerializerContext.Default.String)}},
"value": {{JsonSerializer.Serialize(replaceWhitespace, AppJsonSerializerContext.Default.String)}},
"errorMessage": "// Just for space between items"
},
{
"id": "browserPath",
"type": "Input.ChoiceSet",
"label": {{JsonSerializer.Serialize(Resources.AddShortcutForm_BrowserPathLabel, AppJsonSerializerContext.Default.String)}},
"label": {{JsonSerializer.Serialize(Resources.AddShortcutForm_BrowserPath_Label, AppJsonSerializerContext.Default.String)}},
"placeholder": {{JsonSerializer.Serialize(browserPath, AppJsonSerializerContext.Default.String)}},
"choices": [
{
"title": {{JsonSerializer.Serialize(Resources.AddShortcutForm_BrowserPathDefault, AppJsonSerializerContext.Default.String)}},
"title": {{JsonSerializer.Serialize(Resources.AddShortcutForm_BrowserPath_Default, AppJsonSerializerContext.Default.String)}},
"value": ""
},
{{BrowserDiscovery.GetAllInstalledBrowsers()
.Where(b => !string.IsNullOrWhiteSpace(b.Path))
.Select(b => $$"""
{
"title": {{JsonSerializer.Serialize(b.Name, AppJsonSerializerContext.Default.String)}},
"value": {{JsonSerializer.Serialize(b.Path, AppJsonSerializerContext.Default.String)}}
}
""")
.Aggregate((a, b) => a + "," + b)}}
{{BrowserDiscovery.GetAllInstalledBrowsers()
.Where(b => !string.IsNullOrWhiteSpace(b.Path))
.Select(b => $$"""
{
"title": {{JsonSerializer.Serialize(b.Name, AppJsonSerializerContext.Default.String)}},
"value": {{JsonSerializer.Serialize(b.Path, AppJsonSerializerContext.Default.String)}}
}
""")
.Aggregate((a, b) => a + ",\n" + b)}}
],
"value": {{JsonSerializer.Serialize(browserPath, AppJsonSerializerContext.Default.String)}},
"errorMessage": "// Just for space between items"
},
{
"id": "browserArgs",
"type": "Input.Text",
"style": "text",
"label": {{JsonSerializer.Serialize(Resources.AddShortcutForm_BrowserArgsLabel, AppJsonSerializerContext.Default.String)}},
"placeholder": {{JsonSerializer.Serialize(Resources.AddShortcutForm_BrowserArgsPlaceholder, AppJsonSerializerContext.Default.String)}},
"label": {{JsonSerializer.Serialize(Resources.AddShortcutForm_BrowserArgs_Label, AppJsonSerializerContext.Default.String)}},
"placeholder": {{JsonSerializer.Serialize(Resources.AddShortcutForm_BrowserArgs_Placeholder, AppJsonSerializerContext.Default.String)}},
"value": {{JsonSerializer.Serialize(browserArgs, AppJsonSerializerContext.Default.String)}},
"errorMessage": "// Just for space between items"
}
Expand All @@ -127,6 +135,7 @@ public AddShortcutForm(WebSearchShortcutDataEntry? shortcut)
"url": "url",
"suggestionProvider": "suggestionProvider",
"replaceWhitespace": "replaceWhitespace",
"recordHistory": "recordHistory",
"homePage": "homePage",
"browserPath": "browserPath",
"browserArgs": "browserArgs"
Expand All @@ -149,11 +158,13 @@ public override CommandResult SubmitForm(string inputs)
shortcut.Url = root["url"]?.GetValue<string>() ?? string.Empty;
shortcut.SuggestionProvider = root["suggestionProvider"]?.GetValue<string>() ?? string.Empty;
shortcut.ReplaceWhitespace = root["replaceWhitespace"]?.GetValue<string>() ?? string.Empty;
shortcut.RecordHistory = string.Equals(root["recordHistory"]?.GetValue<string>() ?? "true", "true", StringComparison.OrdinalIgnoreCase);
shortcut.HomePage = root["homePage"]?.GetValue<string>() ?? string.Empty;
shortcut.BrowserPath = root["browserPath"]?.GetValue<string>() ?? string.Empty;
shortcut.BrowserArgs = root["browserArgs"]?.GetValue<string>() ?? string.Empty;

AddedCommand?.Invoke(this, shortcut);

return CommandResult.GoHome();
}
}
77 changes: 77 additions & 0 deletions CmdPalWebSearchShortcut/WebSearchShortcut/Helpers/IntSetting.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using System.Collections.Generic;
using System.Text.Json.Nodes;

namespace Microsoft.CommandPalette.Extensions.Toolkit;

public sealed class IntSetting : Setting<int>
{
public int? Min { get; set; }
public int? Max { get; set; }
public string Placeholder { get; set; } = string.Empty;

private IntSetting()
: base()
{
Value = 0;
}

public IntSetting(string key, int defaultValue, int? min = null, int? max = null)
: base(key, defaultValue)
{
Min = min;
Max = max;
}

public IntSetting(string key, string label, string description, int defaultValue,
int? min = null, int? max = null)
: base(key, label, description, defaultValue)
{
Min = min;
Max = max;
}

public override Dictionary<string, object> ToDictionary()
{
var dict = new Dictionary<string, object>
{
{ "id", Key },
{ "type", "Input.Number" },
{ "title", Label },
{ "label", Description },
{ "value", Value },
{ "isRequired", IsRequired },
{ "errorMessage", ErrorMessage },
{ "placeholder", Placeholder },
};

if (Min.HasValue) dict["min"] = Min.Value;
if (Max.HasValue) dict["max"] = Max.Value;

return dict;
}

public static IntSetting LoadFromJson(JsonObject jsonObject) => new() { Value = jsonObject["value"]?.GetValue<int>() ?? 0 };

public override void Update(JsonObject payload)
{
if (payload.TryGetPropertyValue(Key, out JsonNode? node) && node is not null)
{
if (node is JsonValue jsonValue && jsonValue.TryGetValue<int>(out var value))
{
Value = value;
}
else if (int.TryParse(node.ToString(), out value))
{
Value = value;
}
}

if (Min.HasValue && Value < Min.Value) Value = Min.Value;
if (Max.HasValue && Value > Max.Value) Value = Max.Value;
}

public override string ToState()
{
return $"\"{Key}\": {Value}";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System.IO;
using Windows.Foundation;
using Microsoft.CommandPalette.Extensions.Toolkit;
using WebSearchShortcut.Properties;

namespace WebSearchShortcut.Helpers;

internal class SettingsManager : JsonSettingsManager
{
private const string _namespace = "WebSearchShortcut";
private static string Namespaced(string propertyName) => $"{_namespace}.{propertyName}";

private const int _defaultMaxDisplayCount = 20;
private const int _defaultMaxHistoryDisplayCount = 3;

private readonly IntSetting _maxDisplayCount = new(
Namespaced(nameof(MaxDisplayCount)),
Resources.Settings_MaxDisplayCount_Label,
Resources.Settings_MaxDisplayCount_Description,
_defaultMaxDisplayCount,
1, null
)
{ ErrorMessage = Resources.Settings_MaxDisplayCount_ErrorMessage };

private readonly IntSetting _maxHistoryDisplayCount = new(
Namespaced(nameof(MaxHistoryDisplayCount)),
Resources.Settings_MaxHistoryDisplayCount_Label,
Resources.Settings_MaxHistoryDisplayCount_Description,
_defaultMaxHistoryDisplayCount,
0, null
)
{ ErrorMessage = Resources.Settings_MaxHistoryDisplayCount_ErrorMessage };

public int MaxDisplayCount => _maxDisplayCount.Value;
public int MaxHistoryDisplayCount => _maxHistoryDisplayCount.Value;

public event TypedEventHandler<object, Settings>? SettingsChanged
{
add => Settings.SettingsChanged += value;
remove => Settings.SettingsChanged -= value;
}

internal static string SettingsJsonPath()
{
var directory = Utilities.BaseSettingsPath("Microsoft.CmdPal");

Directory.CreateDirectory(directory);

return Path.Combine(directory, "settings.json");
}

public SettingsManager()
{
FilePath = SettingsJsonPath();

Settings.Add(_maxDisplayCount);
Settings.Add(_maxHistoryDisplayCount);

LoadSettings();

Settings.SettingsChanged += (s, a) => SaveSettings();
}
}
16 changes: 16 additions & 0 deletions CmdPalWebSearchShortcut/WebSearchShortcut/History/HistoryEntry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;
using System.Text.Json.Serialization;

namespace WebSearchShortcut.History;

internal sealed record HistoryEntry
{
public string Query { get; init; }
public DateTimeOffset Timestamp { get; init; }

[JsonConstructor]
public HistoryEntry(string query, DateTimeOffset timestamp)
=> (Query, Timestamp) = (query, timestamp);

public HistoryEntry(string query) : this(query, DateTimeOffset.UtcNow) { }
}
Loading