Skip to content

Commit 00fa61a

Browse files
feat: enhance default entries in Storage with new search providers
1 parent 119c14c commit 00fa61a

4 files changed

Lines changed: 114 additions & 21 deletions

File tree

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Copilot Instructions for PowerToys-Run-WebSearchShortcut
2+
3+
This repository contains a **Windows App SDK** project that implements a **Command Palette Extension** (likely for PowerToys Run or similar hosts). It runs as a **COM Server**.
4+
5+
## Project Architecture
6+
7+
- **Core Framework**: .NET 9.0, Windows App SDK, `Microsoft.CommandPalette.Extensions`.
8+
- **Entry Point**: `WebSearchShortcut/Program.cs` initializes the COM server (`Shmuelie.WinRTServer`) and registers the `WebSearchShortcut` extension.
9+
- **Extension Logic**:
10+
- `WebSearchShortcut.cs`: Implements `IExtension`. Serves as the main bridge between the host and the plugin.
11+
- `WebSearchShortcutCommandsProvider.cs`: Implements `CommandProvider`. Manages the list of available commands (shortcuts) displayed in the palette.
12+
- **Data & Persistence**:
13+
- `Storage.cs`: Handles loading/saving shortcut configurations (JSON). Contains default providers (Google, Bing, etc.).
14+
- `WebSearchShortcutDataEntry.cs`: Model for a single shortcut entry.
15+
- **Search Suggestions**:
16+
- `SuggestionsProviders/`: Contains implementations of `ISuggestionsProvider` for different services (Google, Bing, YouTube, etc.).
17+
- **Pattern**: Each provider implements `GetSuggestionsAsync` to fetch autocomplete results via HTTP.
18+
- **Browser Integration**: `Browser/` folder handles detecting and launching the default browser.
19+
20+
## Key Patterns & Conventions
21+
22+
- **COM Server Lifetime**: The application runs as a local COM server. `Program.cs` manages the lifetime using `ManualResetEvent`.
23+
- **Toolkit Usage**: Heavily relies on `Microsoft.CommandPalette.Extensions.Toolkit` for implementing interfaces like `ICommandItem` and `CommandProvider`.
24+
- **Async/Await**: All I/O (network requests, file access) must be asynchronous.
25+
- **JSON Handling**: Use `System.Text.Json` for parsing API responses and storage files.
26+
- **Error Handling**: Suggestion providers should catch exceptions and log them via `ExtensionHost.LogMessage` (or return empty results) to avoid crashing the host.
27+
28+
## Development Workflows
29+
30+
- **Build**: Standard `dotnet build`.
31+
- **Debugging**:
32+
- The app expects to be launched with `-RegisterProcessAsComServer` by the host.
33+
- To debug, you may need to attach to the process or configure the host to launch your debug build.
34+
- **Adding a New Search Provider**:
35+
1. Create a new class in `SuggestionsProviders/` implementing `ISuggestionsProvider`.
36+
2. Implement `GetSuggestionsAsync` to parse the specific API response.
37+
3. (Optional) Add a default entry in `Storage.cs`.
38+
39+
## File Structure Highlights
40+
41+
- `WebSearchShortcut/`: Main application project.
42+
- `WebSearchShortcut.Tests/`: Unit tests (MSTest).
43+
- `WebSearchShortcut/SuggestionsProviders/`: Search engine implementations.
44+
- `WebSearchShortcut/Commands/`: Command implementations (e.g., `SearchWebCommand`).

CmdPalWebSearchShortcut/WebSearchShortcut/Services/IconService.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ internal static class IconService
2626
private static readonly HashSet<string> FaviconBlacklist = new(StringComparer.OrdinalIgnoreCase)
2727
{
2828
"youtube.com",
29-
"www.youtube.com"
29+
"www.youtube.com",
30+
"www.npmjs.com",
3031
};
3132

3233
/// <summary>

CmdPalWebSearchShortcut/WebSearchShortcut/Storage.cs

Lines changed: 67 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,33 +10,24 @@ internal sealed class Storage
1010
{
1111
public List<WebSearchShortcutDataEntry> Data { get; set; } = [];
1212

13+
// public List<WebSearchShortcutDataEntry> GetTopLevelShortcuts()
14+
// {
15+
// return Data.FindAll(x => !x.HideWhenEmptyQuery);
16+
// }
17+
18+
// public List<WebSearchShortcutDataEntry> GetFallbackShortcuts()
19+
// {
20+
// return Data.FindAll(x => x.HideWhenEmptyQuery);
21+
// }
22+
1323
public static Storage ReadFromFile(string path)
1424
{
1525
var data = new Storage();
1626

1727
if (!File.Exists(path))
1828
{
1929
var defaultStorage = new Storage();
20-
defaultStorage.Data.AddRange([
21-
new WebSearchShortcutDataEntry
22-
{
23-
Name = "Google",
24-
Url = "https://www.google.com/search?q=%s",
25-
SuggestionProvider = "Google",
26-
},
27-
new WebSearchShortcutDataEntry
28-
{
29-
Name = "Bing",
30-
Url = "https://www.bing.com/search?q=%s",
31-
SuggestionProvider = "Bing",
32-
},
33-
new WebSearchShortcutDataEntry
34-
{
35-
Name = "Youtube",
36-
Url = "https://www.youtube.com/results?search_query=%s",
37-
SuggestionProvider = "YouTube"
38-
},
39-
]);
30+
defaultStorage.Data.AddRange(GetDefaultEntries());
4031
WriteToFile(path, defaultStorage);
4132
}
4233

@@ -50,6 +41,16 @@ public static Storage ReadFromFile(string path)
5041
data = JsonSerializer.Deserialize(jsonStringReading, AppJsonSerializerContext.Default.Storage) ?? new Storage();
5142

5243
bool modified = EnsureIds(data.Data);
44+
45+
foreach (var defaultEntry in GetDefaultEntries())
46+
{
47+
if (!data.Data.Exists(x => x.Name == defaultEntry.Name))
48+
{
49+
data.Data.Add(defaultEntry);
50+
modified = true;
51+
}
52+
}
53+
5354
if (modified)
5455
{
5556
WriteToFile(path, data);
@@ -60,6 +61,52 @@ public static Storage ReadFromFile(string path)
6061
return data;
6162
}
6263

64+
private static List<WebSearchShortcutDataEntry> GetDefaultEntries()
65+
{
66+
return
67+
[
68+
new WebSearchShortcutDataEntry
69+
{
70+
Name = "Google",
71+
Url = "https://www.google.com/search?q=%s",
72+
SuggestionProvider = "Google",
73+
},
74+
new WebSearchShortcutDataEntry
75+
{
76+
Name = "Bing",
77+
Url = "https://www.bing.com/search?q=%s",
78+
SuggestionProvider = "Bing",
79+
},
80+
new WebSearchShortcutDataEntry
81+
{
82+
Name = "Youtube",
83+
Url = "https://www.youtube.com/results?search_query=%s",
84+
SuggestionProvider = "YouTube"
85+
},
86+
new WebSearchShortcutDataEntry
87+
{
88+
Name = "DuckDuckGo",
89+
Url = "https://duckduckgo.com/?q=%s",
90+
SuggestionProvider = "DuckDuckGo",
91+
// HideWhenEmptyQuery = true
92+
},
93+
new WebSearchShortcutDataEntry
94+
{
95+
Name = "Wikipedia",
96+
Url = "https://en.wikipedia.org/w/index.php?search=",
97+
SuggestionProvider = "Wikipedia",
98+
// HideWhenEmptyQuery = true
99+
},
100+
new WebSearchShortcutDataEntry
101+
{
102+
Name = "npm",
103+
Url = "https://www.npmjs.com/search?q=%s",
104+
SuggestionProvider = "Npm",
105+
// HideWhenEmptyQuery = true
106+
}
107+
];
108+
}
109+
63110
public static void WriteToFile(string path, Storage data)
64111
{
65112
EnsureIds(data.Data);

CmdPalWebSearchShortcut/WebSearchShortcut/WebSearchShortcutDataEntry.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ internal sealed class WebSearchShortcutDataEntry
1313
// public string[]? Urls { get; set; }
1414
public string? SuggestionProvider { get; set; }
1515
public string? HomePage { get; set; }
16+
// public bool HideWhenEmptyQuery { get; set; } // TODO: wait powertoys cmdpal support for update with query
1617
public string? IconUrl { get; set; }
1718
public string? ReplaceWhitespace { get; set; }
1819
public string? BrowserPath { get; set; }

0 commit comments

Comments
 (0)