├── .github
├── FUNDING.yml
└── workflows
│ └── release.yml
├── .gitignore
├── LICENSE
├── README.md
└── src
└── CanIUse
├── CanIUse.sln
├── Community.PowerToys.Run.Plugin.CanIUse.UnitTests
├── Community.PowerToys.Run.Plugin.CanIUse.UnitTests.csproj
└── MainTests.cs
└── Community.PowerToys.Run.Plugin.CanIUse
├── Community.PowerToys.Run.Plugin.CanIUse.csproj
├── Helpers
└── BrowserExtensions.cs
├── Images
├── caniuse.dark.png
└── caniuse.light.png
├── Main.cs
├── Models
├── Browser.cs
├── BrowserStatDictionary.cs
├── BrowserVersionStatDictionary.cs
├── CanIUseData.cs
├── DataMatch.cs
├── Feature.cs
└── Link.cs
├── Repositories
└── DataRepository.cs
├── Settings
└── CanIUseSettings.cs
└── plugin.json
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: [skttl]
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Build and package for release
2 |
3 | on:
4 | release:
5 | types: [published]
6 |
7 | permissions:
8 | contents: write
9 |
10 | jobs:
11 | build:
12 | runs-on: windows-latest
13 |
14 | env:
15 | PRODUCT_NAME: CanIUse
16 | PROJECT_PATH: ./src/CanIUse/Community.PowerToys.Run.Plugin.CanIUse
17 | SLN_PATH: ./src/CanIUse/CanIUse.sln
18 |
19 | steps:
20 |
21 | - name: Checkout repository
22 | uses: actions/checkout@v4
23 | with:
24 | fetch-depth: 0
25 |
26 | - name: Extract version from release tag
27 | id: extract_version
28 | run: |
29 | $version = '${{ github.event.release.tag_name }}'
30 | echo "VERSION=$version" >> $env:GITHUB_ENV
31 | shell: powershell
32 |
33 | - name: Replace version token in plugin.json
34 | run: |
35 | (Get-Content '${{ env.PROJECT_PATH }}/plugin.json') -replace '\$\(Version\)', '${{ env.VERSION }}' | Set-Content '${{ env.PROJECT_PATH }}/plugin.json'
36 | shell: powershell
37 |
38 | - name: Package
39 | run: |
40 | dotnet pack ${{ env.SLN_PATH }} -p:Platform=x64 -p:Version=${{ env.VERSION }}
41 | dotnet pack ${{ env.SLN_PATH }} -p:Platform=ARM64 -p:Version=${{ env.VERSION }}
42 |
43 | - name: Upload x64 to Release Action
44 | uses: Shopify/upload-to-release@v2.0.0
45 | with:
46 | name: ${{ env.PRODUCT_NAME }}-${{ env.VERSION }}-x64.zip
47 | path: ${{ env.PROJECT_PATH }}/${{ env.PRODUCT_NAME }}-${{ env.VERSION }}-x64.zip
48 | content-type: application/zip
49 | repo-token: ${{secrets.GITHUB_TOKEN}}
50 |
51 | - name: Upload ARM64 to Release Action
52 | uses: Shopify/upload-to-release@v2.0.0
53 | with:
54 | name: ${{ env.PRODUCT_NAME }}-${{ env.VERSION }}-ARM64.zip
55 | path: ${{ env.PROJECT_PATH }}/${{ env.PRODUCT_NAME }}-${{ env.VERSION }}-ARM64.zip
56 | content-type: application/zip
57 | repo-token: ${{secrets.GITHUB_TOKEN}}
58 |
59 | # Step to calculate SHA256 for the two files
60 | - name: Calculate SHA-256 Hash for x64 and ARM64
61 | id: calculate_sha
62 | run: |
63 | $x64_hash = Get-FileHash -Path "${{ env.PROJECT_PATH }}/${{ env.PRODUCT_NAME }}-${{ env.VERSION }}-x64.zip" -Algorithm SHA256 | Select-Object -ExpandProperty Hash
64 | $arm64_hash = Get-FileHash -Path "${{ env.PROJECT_PATH }}/${{ env.PRODUCT_NAME }}-${{ env.VERSION }}-ARM64.zip" -Algorithm SHA256 | Select-Object -ExpandProperty Hash
65 | echo "x64_SHA=$x64_hash" >> $env:GITHUB_ENV
66 | echo "ARM64_SHA=$arm64_hash" >> $env:GITHUB_ENV
67 | shell: powershell
68 |
69 | - name: update release
70 | id: update_release
71 | uses: tubone24/update_release@v1.0
72 | env:
73 | GITHUB_TOKEN: ${{ github.token }}
74 | with:
75 | is_append_body: 1
76 | body: |
77 | ## SHA-256 Hashes:
78 | - **x64 Hash**: \`${{ env.x64_SHA }}\`
79 | - **ARM64 Hash**: \`${{ env.ARM64_SHA }}\`
80 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vs
2 | bin
3 | obj
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Søren Kottal
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Can I Use
2 |
3 | PowerToys Run plugin which will search and present results from caniuse.com.
4 |
5 | 
6 |
7 | ## Settings
8 |
9 | You can configure the plugin to show your favorite browsers in the search results under the PowerToys Run settings.
10 |
11 | 
12 |
13 | ## Usage
14 |
15 | 1. Open PowerToys Run (default shortcut is Alt+Space).
16 | 2. Type `caniuse` followed by your search query.
17 | 3. Select a search result and press `Enter` to open it in browser.
18 |
19 | ## Installation
20 |
21 | ### Via [ptr](https://github.com/8LWXpg/ptr)
22 |
23 | ```shell
24 | ptr add CanIUse skttl/ptrun-caniuse
25 | ```
26 |
27 | ### Manual install
28 |
29 | 1. Download the latest release of the CanIUse from the releases page.
30 | 2. Extract the zip file's contents to your PowerToys modules directory for the user (`%LOCALAPPDATA%\Microsoft\PowerToys\PowerToys Run\Plugins`).
31 | 3. Restart PowerToys.
32 |
33 | ## Acknowledgments
34 |
35 | - [Henrik Lau Eriksson](https://github.com/hlaueriksson) - for the awesome [Visual Studio template for building plugins](https://github.com/hlaueriksson/Community.PowerToys.Run.Plugin.Templates)
36 | - [Nick](https://github.com/N0I0C0K) - took inspiration from his GitHub workflows in his [PowerTranslator plugin for PowerToys](https://github.com/N0I0C0K/PowerTranslator)
37 |
--------------------------------------------------------------------------------
/src/CanIUse/CanIUse.sln:
--------------------------------------------------------------------------------
1 | Microsoft Visual Studio Solution File, Format Version 12.00
2 | # Visual Studio Version 17
3 | VisualStudioVersion = 17.10.35027.167
4 | MinimumVisualStudioVersion = 10.0.40219.1
5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Community.PowerToys.Run.Plugin.CanIUse", "Community.PowerToys.Run.Plugin.CanIUse\Community.PowerToys.Run.Plugin.CanIUse.csproj", "{D10D969C-02D1-4E35-8C80-667A43F7E10E}"
6 | EndProject
7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Community.PowerToys.Run.Plugin.CanIUse.UnitTests", "Community.PowerToys.Run.Plugin.CanIUse.UnitTests\Community.PowerToys.Run.Plugin.CanIUse.UnitTests.csproj", "{A56D18DC-C3F2-4F3B-8019-D78EB7E8E435}"
8 | EndProject
9 | Global
10 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
11 | Debug|ARM64 = Debug|ARM64
12 | Debug|x64 = Debug|x64
13 | Release|ARM64 = Release|ARM64
14 | Release|x64 = Release|x64
15 | EndGlobalSection
16 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
17 | {D10D969C-02D1-4E35-8C80-667A43F7E10E}.Debug|ARM64.ActiveCfg = Debug|ARM64
18 | {D10D969C-02D1-4E35-8C80-667A43F7E10E}.Debug|ARM64.Build.0 = Debug|ARM64
19 | {D10D969C-02D1-4E35-8C80-667A43F7E10E}.Debug|x64.ActiveCfg = Debug|x64
20 | {D10D969C-02D1-4E35-8C80-667A43F7E10E}.Debug|x64.Build.0 = Debug|x64
21 | {D10D969C-02D1-4E35-8C80-667A43F7E10E}.Release|ARM64.ActiveCfg = Release|ARM64
22 | {D10D969C-02D1-4E35-8C80-667A43F7E10E}.Release|ARM64.Build.0 = Release|ARM64
23 | {D10D969C-02D1-4E35-8C80-667A43F7E10E}.Release|x64.ActiveCfg = Release|x64
24 | {D10D969C-02D1-4E35-8C80-667A43F7E10E}.Release|x64.Build.0 = Release|x64
25 | {A56D18DC-C3F2-4F3B-8019-D78EB7E8E435}.Debug|ARM64.ActiveCfg = Debug|ARM64
26 | {A56D18DC-C3F2-4F3B-8019-D78EB7E8E435}.Debug|ARM64.Build.0 = Debug|ARM64
27 | {A56D18DC-C3F2-4F3B-8019-D78EB7E8E435}.Debug|x64.ActiveCfg = Debug|x64
28 | {A56D18DC-C3F2-4F3B-8019-D78EB7E8E435}.Debug|x64.Build.0 = Debug|x64
29 | {A56D18DC-C3F2-4F3B-8019-D78EB7E8E435}.Release|ARM64.ActiveCfg = Release|ARM64
30 | {A56D18DC-C3F2-4F3B-8019-D78EB7E8E435}.Release|ARM64.Build.0 = Release|ARM64
31 | {A56D18DC-C3F2-4F3B-8019-D78EB7E8E435}.Release|x64.ActiveCfg = Release|x64
32 | {A56D18DC-C3F2-4F3B-8019-D78EB7E8E435}.Release|x64.Build.0 = Release|x64
33 | EndGlobalSection
34 | GlobalSection(SolutionProperties) = preSolution
35 | HideSolutionNode = FALSE
36 | EndGlobalSection
37 | GlobalSection(ExtensibilityGlobals) = postSolution
38 | SolutionGuid = {674926A0-C170-4B9A-9EAD-5892663DE4F6}
39 | EndGlobalSection
40 | EndGlobal
41 |
--------------------------------------------------------------------------------
/src/CanIUse/Community.PowerToys.Run.Plugin.CanIUse.UnitTests/Community.PowerToys.Run.Plugin.CanIUse.UnitTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0-windows
5 | x64;ARM64
6 | $(Platform)
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/CanIUse/Community.PowerToys.Run.Plugin.CanIUse.UnitTests/MainTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.TestTools.UnitTesting;
2 | using System.Linq;
3 |
4 | namespace Community.PowerToys.Run.Plugin.CanIUse.UnitTests
5 | {
6 | [TestClass]
7 | public class MainTests
8 | {
9 | private Main main;
10 |
11 | [TestInitialize]
12 | public void TestInitialize()
13 | {
14 | main = new Main();
15 | }
16 |
17 | [TestMethod]
18 | public void Query_should_return_results()
19 | {
20 | var results = main.Query(new("search"));
21 |
22 | Assert.IsNotNull(results.First());
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/CanIUse/Community.PowerToys.Run.Plugin.CanIUse/Community.PowerToys.Run.Plugin.CanIUse.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0-windows
5 | true
6 | x64;ARM64
7 | $(Platform)
8 | enable
9 | CanIUse
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | PreserveNewest
19 |
20 |
21 | PreserveNewest
22 |
23 |
24 |
25 |
27 |
28 |
29 |
30 |
32 |
33 |
35 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/src/CanIUse/Community.PowerToys.Run.Plugin.CanIUse/Helpers/BrowserExtensions.cs:
--------------------------------------------------------------------------------
1 | using Community.PowerToys.Run.Plugin.CanIUse.Models;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Community.PowerToys.Run.Plugin.CanIUse.Helpers;
9 |
10 | public static class BrowserExtensions
11 | {
12 | public static Dictionary BrowserNames = new Dictionary
13 | {
14 | { Browser.android, "Android Browser" },
15 | { Browser.and_chr, "Chrome for Android" },
16 | { Browser.and_ff, "Firefox for Android" },
17 | { Browser.and_qq, "QQ Browser" },
18 | { Browser.and_uc, "UC Browser for Android" },
19 | { Browser.baidu, "Baidu" },
20 | { Browser.bb, "BB Browser" },
21 | { Browser.chrome, "Chrome" },
22 | { Browser.edge, "Edge" },
23 | { Browser.firefox, "Firefox" },
24 | { Browser.ie, "IE" },
25 | { Browser.ie_mob, "IE Mobile" },
26 | { Browser.ios_saf, "iOS Safari" },
27 | { Browser.kaios, "KaiOS Browser" },
28 | { Browser.op_mini, "Opera Mini" },
29 | { Browser.op_mob, "Opera Mobile" },
30 | { Browser.opera, "Opera" },
31 | { Browser.safari, "Safari" },
32 | { Browser.samsung, "Samsung Internet" }
33 | };
34 | public static string ToFriendlyName(this Browser browser)
35 | {
36 | return BrowserNames.TryGetValue(browser, out var s) ? s : browser.ToString();
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/CanIUse/Community.PowerToys.Run.Plugin.CanIUse/Images/caniuse.dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skttl/ptrun-caniuse/43ac1d733209859406535b08e39cfa708395681d/src/CanIUse/Community.PowerToys.Run.Plugin.CanIUse/Images/caniuse.dark.png
--------------------------------------------------------------------------------
/src/CanIUse/Community.PowerToys.Run.Plugin.CanIUse/Images/caniuse.light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skttl/ptrun-caniuse/43ac1d733209859406535b08e39cfa708395681d/src/CanIUse/Community.PowerToys.Run.Plugin.CanIUse/Images/caniuse.light.png
--------------------------------------------------------------------------------
/src/CanIUse/Community.PowerToys.Run.Plugin.CanIUse/Main.cs:
--------------------------------------------------------------------------------
1 | using Community.PowerToys.Run.Plugin.CanIUse.Models;
2 | using Community.PowerToys.Run.Plugin.CanIUse.Repositories;
3 | using Community.PowerToys.Run.Plugin.CanIUse.Settings;
4 | using ManagedCommon;
5 | using Microsoft.PowerToys.Settings.UI.Library;
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Linq;
9 | using System.Windows.Controls;
10 | using Wox.Infrastructure;
11 | using Wox.Plugin;
12 | using BrowserInfo = Wox.Plugin.Common.DefaultBrowserInfo;
13 |
14 | namespace Community.PowerToys.Run.Plugin.CanIUse
15 | {
16 | ///
17 | /// Main class of this plugin that implement all used interfaces.
18 | ///
19 | public class Main : IPlugin, ISettingProvider, IDisposable
20 | {
21 | private readonly DataRepository _dataRepository = new DataRepository();
22 |
23 | ///
24 | /// ID of the plugin.
25 | ///
26 | public static string PluginID => "5C1A49E431F74D66BC62FD214D834609";
27 |
28 | ///
29 | /// Name of the plugin.
30 | ///
31 | public string Name => "CanIUse";
32 |
33 | ///
34 | /// Description of the plugin.
35 | ///
36 | public string Description => "Lookup browser support for different web features";
37 |
38 | private PluginInitContext? Context { get; set; }
39 |
40 | private bool Disposed { get; set; }
41 |
42 | public IEnumerable AdditionalOptions => CanIUseSettings.GetAdditionalOptions();
43 |
44 | ///
45 | /// Return a filtered list, based on the given query.
46 | ///
47 | /// The query to filter the list.
48 | /// A filtered list, can be empty when nothing was found.
49 | public List Query(Query query)
50 | {
51 | if (string.IsNullOrWhiteSpace(query.Search))
52 | {
53 | return new List()
54 | {
55 | new Result
56 | {
57 | QueryTextDisplay = string.Empty,
58 | Title = "Search caniuse.com",
59 | SubTitle = Description,
60 | IcoPath = IconPath
61 | }
62 | };
63 | }
64 |
65 | var results = new List();
66 |
67 | var data = _dataRepository.GetData(query.Search.ToLower().Trim())?.Take((int)CanIUseSettings.Instance.MaxResults);
68 |
69 | if (data is not null)
70 | {
71 | results.AddRange(data.Select(CreateResult));
72 | }
73 |
74 | if (results.Count == 0)
75 | {
76 | results.Add(new Result
77 | {
78 | Title = "No results",
79 | SubTitle = $"No results found on caniuse.com for \"{query.Search}\"",
80 | IcoPath = IconPath
81 | });
82 | }
83 |
84 | return results;
85 | }
86 |
87 | ///
88 | /// Initialize the plugin with the given .
89 | ///
90 | /// The for this plugin.
91 | public void Init(PluginInitContext context)
92 | {
93 | Context = context ?? throw new ArgumentNullException(nameof(context));
94 | Context.API.ThemeChanged += OnThemeChanged;
95 | UpdateIconPath(Context.API.GetCurrentTheme());
96 | }
97 |
98 | ///
99 | public void Dispose()
100 | {
101 | Dispose(true);
102 | GC.SuppressFinalize(this);
103 | }
104 |
105 | ///
106 | /// Wrapper method for that dispose additional objects and events form the plugin itself.
107 | ///
108 | /// Indicate that the plugin is disposed.
109 | protected virtual void Dispose(bool disposing)
110 | {
111 | if (Disposed || !disposing)
112 | {
113 | return;
114 | }
115 |
116 | if (Context?.API != null)
117 | {
118 | Context.API.ThemeChanged -= OnThemeChanged;
119 | }
120 |
121 | Disposed = true;
122 | }
123 | private string IconPath { get; set; } = "Images/caniuse.light.png";
124 |
125 | private void UpdateIconPath(Theme theme) => IconPath = theme == Theme.Light || theme == Theme.HighContrastWhite ? "Images/caniuse.light.png" : "Images/caniuse.dark.png";
126 |
127 | private void OnThemeChanged(Theme currentTheme, Theme newTheme) => UpdateIconPath(newTheme);
128 |
129 |
130 | public Result CreateResult(DataMatch item)
131 | {
132 | return new Result
133 | {
134 | QueryTextDisplay = string.Empty,
135 | Title = $"{item.Feature.Title} " +
136 | $"{item.Feature.UsagePercent}%" +
137 | $"{(item.Feature.UsagePercentPartial != 0 ? $" + {item.Feature.UsagePercentPartial}% = {(item.Feature.UsagePercent + item.Feature.UsagePercentPartial)}%" : "")}",
138 | SubTitle = $"{item.Feature.Stats.ToString(CanIUseSettings.Instance.DefaultBrowsers)}{Environment.NewLine}{item.Feature.Description}",
139 | Action = action => Helper.OpenCommandInShell(BrowserInfo.Path, BrowserInfo.ArgumentsPattern, $"https://caniuse.com/#feat={item.Key}"),
140 | IcoPath = IconPath
141 | };
142 | }
143 |
144 | public Control CreateSettingPanel()
145 | {
146 | throw new NotImplementedException();
147 | }
148 |
149 | public void UpdateSettings(PowerLauncherPluginSettings settings)
150 | {
151 | CanIUseSettings.Instance.UpdateSettings(settings);
152 | }
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/src/CanIUse/Community.PowerToys.Run.Plugin.CanIUse/Models/Browser.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Text.Json.Serialization;
6 | using System.Threading.Tasks;
7 |
8 | namespace Community.PowerToys.Run.Plugin.CanIUse.Models;
9 |
10 | [JsonConverter(typeof(JsonStringEnumConverter))]
11 | public enum Browser
12 | {
13 | ie,
14 | edge,
15 | firefox,
16 | chrome,
17 | safari,
18 | opera,
19 | ios_saf,
20 | op_mini,
21 | android,
22 | bb,
23 | op_mob,
24 | and_chr,
25 | and_ff,
26 | ie_mob,
27 | and_uc,
28 | samsung,
29 | and_qq,
30 | baidu,
31 | kaios
32 | }
33 |
--------------------------------------------------------------------------------
/src/CanIUse/Community.PowerToys.Run.Plugin.CanIUse/Models/BrowserStatDictionary.cs:
--------------------------------------------------------------------------------
1 | using Community.PowerToys.Run.Plugin.CanIUse.Helpers;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Community.PowerToys.Run.Plugin.CanIUse.Models;
9 |
10 | public class BrowserStatDictionary : Dictionary
11 | {
12 | public string ToString(Browser[]? browsers = null)
13 | {
14 | browsers = browsers ?? new[] { Browser.chrome, Browser.firefox, Browser.safari, Browser.edge };
15 |
16 | var stats = this.Where(x => browsers.Contains(x.Key));
17 |
18 | return string.Join(", ", stats.Select(x => $"{x.Key.ToFriendlyName()} {x.Value.GetSupport()}"));
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/CanIUse/Community.PowerToys.Run.Plugin.CanIUse/Models/BrowserVersionStatDictionary.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Community.PowerToys.Run.Plugin.CanIUse.Models;
8 |
9 | public class BrowserVersionStatDictionary : Dictionary
10 | {
11 | private string GetSupportedRange(string token)
12 | {
13 |
14 | var supported = this.Where(x => x.Value.StartsWith(token)).ToList();
15 | if (supported.Count > 1)
16 | {
17 | return $"{supported.First().Key}-{supported.Last().Key}{(supported.Last().Key.Equals(this.Last().Key) ? "+" : "")}";
18 | }
19 | else if (supported.Count > 0)
20 | {
21 | return $"{supported.Last().Key}{(supported.Last().Key.Equals(this.Last().Key) ? "+" : "")}";
22 | }
23 |
24 | return "N/A";
25 | }
26 |
27 | public string GetSupportedRange() => GetSupportedRange("y");
28 | public string GetPartiallySupportedRange() => GetSupportedRange("p");
29 |
30 | public string GetSupport()
31 | {
32 | var supported = GetSupportedRange();
33 | var partiallySupported = GetPartiallySupportedRange();
34 | return $"{supported}{(partiallySupported != "N/A" ? $" ({partiallySupported}*)" : "")}";
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/CanIUse/Community.PowerToys.Run.Plugin.CanIUse/Models/CanIUseData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Text.Json.Serialization;
6 | using System.Threading.Tasks;
7 |
8 | namespace Community.PowerToys.Run.Plugin.CanIUse.Models;
9 |
10 | public class CanIUseData
11 | {
12 | [JsonPropertyName("data")]
13 | public Dictionary Data { get; set; } = new Dictionary();
14 | }
15 |
--------------------------------------------------------------------------------
/src/CanIUse/Community.PowerToys.Run.Plugin.CanIUse/Models/DataMatch.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Wox.Infrastructure;
3 |
4 | namespace Community.PowerToys.Run.Plugin.CanIUse.Models;
5 |
6 | public class DataMatch
7 | {
8 | public DataMatch(KeyValuePair item, string search)
9 | {
10 | Key = item.Key;
11 | Feature = item.Value;
12 | MatchResult = StringMatcher.FuzzySearch(search, $"{item.Key} {item.Value.Title} {item.Value.Description}");
13 | }
14 | public string Key { get; set; }
15 | public Feature Feature { get; set; }
16 | public MatchResult MatchResult { get; set; }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/CanIUse/Community.PowerToys.Run.Plugin.CanIUse/Models/Feature.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Text.Json.Serialization;
6 | using System.Threading.Tasks;
7 |
8 | namespace Community.PowerToys.Run.Plugin.CanIUse.Models;
9 |
10 | public class Feature
11 | {
12 | [JsonPropertyName("title")]
13 | public required string Title { get; set; }
14 |
15 | [JsonPropertyName("description")]
16 | public string? Description { get; set; }
17 |
18 | [JsonPropertyName("stats")]
19 | public BrowserStatDictionary Stats { get; set; } = new BrowserStatDictionary();
20 |
21 | [JsonPropertyName("usage_perc_y")]
22 | public decimal UsagePercent { get; set; }
23 |
24 | [JsonPropertyName("usage_perc_a")]
25 | public decimal UsagePercentPartial { get; set; }
26 | }
27 |
--------------------------------------------------------------------------------
/src/CanIUse/Community.PowerToys.Run.Plugin.CanIUse/Models/Link.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Text.Json.Serialization;
6 | using System.Threading.Tasks;
7 |
8 | namespace Community.PowerToys.Run.Plugin.CanIUse.Models;
9 |
10 | public class Link
11 | {
12 | [JsonPropertyName("url")]
13 | public string? Url { get; set; }
14 |
15 | [JsonPropertyName("title")]
16 | public string? Title { get; set; }
17 | }
18 |
--------------------------------------------------------------------------------
/src/CanIUse/Community.PowerToys.Run.Plugin.CanIUse/Repositories/DataRepository.cs:
--------------------------------------------------------------------------------
1 | using Community.PowerToys.Run.Plugin.CanIUse.Models;
2 | using Community.PowerToys.Run.Plugin.CanIUse.Settings;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Net.Http;
8 | using System.Text;
9 | using System.Text.Json;
10 | using System.Threading.Tasks;
11 | using Wox.Infrastructure;
12 |
13 | namespace Community.PowerToys.Run.Plugin.CanIUse.Repositories;
14 |
15 | public class DataRepository
16 | {
17 | private static string absPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) ?? string.Empty;
18 | private static string dataFn = Path.Combine(absPath, "data.json") ?? string.Empty;
19 |
20 | public static void WriteFile(string filename, string data)
21 | {
22 | File.WriteAllText(filename, data);
23 | }
24 |
25 | public static string ReadFile(string filename)
26 | {
27 | return File.ReadAllText(filename);
28 | }
29 |
30 | public IEnumerable? GetData(string search)
31 | {
32 | if (string.IsNullOrWhiteSpace(search))
33 | {
34 | return null;
35 | }
36 |
37 | if (!File.Exists(dataFn) || (File.GetLastWriteTime(dataFn).AddDays(CanIUseSettings.Instance.DataRefreshInterval) < DateTime.Now))
38 | {
39 | DownloadData().GetAwaiter().GetResult();
40 | }
41 |
42 | var data = JsonSerializer.Deserialize(ReadFile(dataFn));
43 |
44 | if (data?.Data == null) {
45 | return null;
46 | }
47 |
48 | var ordered = data.Data
49 | .Select(x => new DataMatch(x, search))
50 | .Where(x => x.MatchResult.IsSearchPrecisionScoreMet())
51 | .OrderByDescending(x => x.MatchResult.RawScore);
52 |
53 | return ordered;
54 | }
55 |
56 | public async Task DownloadData()
57 | {
58 | using (HttpClient client = new HttpClient())
59 | {
60 | HttpResponseMessage response = await client.GetAsync("https://raw.githubusercontent.com/Fyrd/caniuse/master/data.json");
61 | if (response.IsSuccessStatusCode)
62 | {
63 | var jsonData = await response.Content.ReadAsStringAsync();
64 | var data = JsonSerializer.Deserialize(jsonData);
65 | WriteFile(dataFn, JsonSerializer.Serialize(data));
66 | }
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/CanIUse/Community.PowerToys.Run.Plugin.CanIUse/Settings/CanIUseSettings.cs:
--------------------------------------------------------------------------------
1 | using Community.PowerToys.Run.Plugin.CanIUse.Helpers;
2 | using Community.PowerToys.Run.Plugin.CanIUse.Models;
3 | using Microsoft.PowerToys.Settings.UI.Library;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 |
8 | namespace Community.PowerToys.Run.Plugin.CanIUse.Settings;
9 |
10 | internal sealed class CanIUseSettings
11 | {
12 | private readonly bool _initialized;
13 | private static CanIUseSettings? instance;
14 | private static Browser[] FallbackDefaultBrowsers = { Browser.chrome, Browser.edge, Browser.firefox, Browser.safari, Browser.ios_saf };
15 | private static double FallbackDataRefreshInterval = 14;
16 | private static double FallbackMaxResults = 5;
17 | internal Browser[] DefaultBrowsers { get; set; } = FallbackDefaultBrowsers;
18 | internal double DataRefreshInterval { get; set; } = FallbackDataRefreshInterval;
19 | internal double MaxResults { get; set; } = FallbackMaxResults;
20 |
21 | private CanIUseSettings()
22 | {
23 | // Init class properties with default values
24 | UpdateSettings(null);
25 | _initialized = true;
26 | }
27 |
28 | internal static CanIUseSettings Instance
29 | {
30 | get
31 | {
32 | if (instance == null)
33 | {
34 | instance = new CanIUseSettings();
35 | }
36 |
37 | return instance;
38 | }
39 | }
40 | internal static List GetAdditionalOptions()
41 | {
42 |
43 | var optionList = new List()
44 | {
45 | new PluginAdditionalOption()
46 | {
47 | Key = nameof(DataRefreshInterval),
48 | DisplayLabel = "Refresh caniuse.com data if older than",
49 | DisplayDescription = $"Determine the number of days the cache the data from caniuse.com for.",
50 | PluginOptionType = PluginAdditionalOption.AdditionalOptionType.Numberbox,
51 | NumberBoxMin = 0,
52 | NumberValue = FallbackDataRefreshInterval
53 | },
54 | new PluginAdditionalOption()
55 | {
56 | Key = nameof(MaxResults),
57 | DisplayLabel = "Maximum number of results",
58 | DisplayDescription = $"Determine the maximum number of results to show when searching.",
59 | PluginOptionType = PluginAdditionalOption.AdditionalOptionType.Numberbox,
60 | NumberBoxMin = 1,
61 | NumberValue = FallbackMaxResults
62 | }
63 | };
64 |
65 | foreach (var browserValue in Enum.GetValues(typeof(Browser)))
66 | {
67 | var browser = (Browser)browserValue;
68 | optionList.Add(new PluginAdditionalOption()
69 | {
70 | Key = $"{nameof(DefaultBrowsers)}_{browser.ToString()}",
71 | DisplayLabel = $"Show {browser.ToFriendlyName()} support in search results",
72 | PluginOptionType = PluginAdditionalOption.AdditionalOptionType.Checkbox,
73 | TextValue = browser.ToString(),
74 | Value = FallbackDefaultBrowsers.Contains(browser)
75 | });
76 | }
77 |
78 | return optionList;
79 | }
80 | internal void UpdateSettings(PowerLauncherPluginSettings? settings)
81 | {
82 | if ((settings is null || settings.AdditionalOptions is null) & _initialized)
83 | {
84 | return;
85 | }
86 |
87 | DefaultBrowsers = settings?.AdditionalOptions?.Where(x => x.Key.StartsWith(nameof(DefaultBrowsers)) && x.Value).Select(x => (Browser)Enum.Parse(typeof(Browser), x.TextValue)).ToArray()
88 | ?? FallbackDefaultBrowsers;
89 |
90 | DataRefreshInterval = (settings?.AdditionalOptions?.FirstOrDefault(x => x.Key == nameof(DataRefreshInterval))?.NumberValue) ?? FallbackDataRefreshInterval;
91 |
92 | MaxResults = (settings?.AdditionalOptions?.FirstOrDefault(x => x.Key == nameof(MaxResults))?.NumberValue) ?? FallbackMaxResults;
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/CanIUse/Community.PowerToys.Run.Plugin.CanIUse/plugin.json:
--------------------------------------------------------------------------------
1 | {
2 | "ID": "5C1A49E431F74D66BC62FD214D834609",
3 | "ActionKeyword": "caniuse",
4 | "IsGlobal": false,
5 | "Name": "CanIUse",
6 | "Author": "skttl",
7 | "Version": "$(Version)",
8 | "Language": "csharp",
9 | "Website": "https://github.com/skttl/ptrun-caniuse",
10 | "ExecuteFileName": "Community.PowerToys.Run.Plugin.CanIUse.dll",
11 | "IcoPathDark": "Images\\caniuse.dark.png",
12 | "IcoPathLight": "Images\\caniuse.light.png",
13 | "DynamicLoading": false
14 | }
15 |
--------------------------------------------------------------------------------