mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-04 18:26:39 +02:00
Add the Command Palette module (#37908)
Windows Command Palette ("CmdPal") is the next iteration of PowerToys Run. With extensibility at its core, the Command Palette is your one-stop launcher to start _anything_.
By default, CmdPal is bound to <kbd>Win+Alt+Space</kbd>.


----
This brings the current preview version of CmdPal into the upstream PowerToys repo. There are still lots of bugs to work out, but it's reached the state we're ready to start sharing it with the world. From here, we can further collaborate with the community on the features that are important, and ensuring that we've got a most robust API to enable developers to build whatever extensions they want.
Most of the built-in PT Run modules have already been ported to CmdPal's extension API. Those include:
* Installed apps
* Shell commands
* File search (powered by the indexer)
* Windows Registry search
* Web search
* Windows Terminal Profiles
* Windows Services
* Windows settings
There are a couple new extensions built-in
* You can now search for packages on `winget` and install them right from the palette. This also powers searching for extensions for the palette
* The calculator has an entirely new implementation. This is currently less feature complete than the original PT Run one - we're looking forward to updating it to be more complete for future ingestion in Windows
* "Bookmarks" allow you to save shortcuts to files, folders, and webpages as top-level commands in the palette.
We've got a bunch of other samples too, in this repo and elsewhere
### PowerToys specific notes
CmdPal will eventually graduate out of PowerToys to live as its own application, which is why it's implemented just a little differently than most other modules. Enabling CmdPal will install its `msix` package.
The CI was minorly changed to support CmdPal version numbers independent of PowerToys itself. It doesn't make sense for us to start CmdPal at v0.90, and in the future, we want to be able to rev CmdPal independently of PT itself.
Closes #3200, closes #3600, closes #7770, closes #34273, closes #36471, closes #20976, closes #14495
-----
TODOs et al
**Blocking:**
- [ ] Images and descriptions in Settings and OOBE need to be properly defined, as mentioned before
- [ ] Niels is on it
- [x] Doesn't start properly from PowerToys unless the fix PR is merged.
- https://github.com/zadjii-msft/PowerToys/pull/556 merged
- [x] I seem to lose focus a lot when I press on some limits, like between the search bar and the results.
- This is https://github.com/zadjii-msft/PowerToys/issues/427
- [x] Turned off an extension like Calculator and it was still working.
- Need to get rid of that toggle, it doesn't do anything currently
- [x] `ListViewModel.<FetchItems>` crash
- Pretty confident that was fixed in https://github.com/zadjii-msft/PowerToys/pull/553
**Not blocking / improvements:**
- Show the shortcut through settings, as mentioned before, or create a button that would open CmdPalette settings.
- When PowerToys starts, CmdPalette is always shown if enabled. That's weird when just starting PowerToys/ logging in to the computer with PowerToys auto-start activated. I think this should at least be a setting.
- Needing to double press a result for it to do the default action seems quirky. If one is already selected, I think just pressing should be enough for it to do the action.
- This is currently a setting, though we're thinking of changing the setting even more: https://github.com/zadjii-msft/PowerToys/issues/392
- There's no URI extension. Was surprised when typing a URL that it only proposed a web search.
- [x] There's no System commands extension. Was expecting to be able to quickly restart the computer by typing restart but it wasn't there.
- This is in PR https://github.com/zadjii-msft/PowerToys/pull/452
---------
Co-authored-by: joadoumie <98557455+joadoumie@users.noreply.github.com>
Co-authored-by: Jordi Adoumie <jordiadoumie@microsoft.com>
Co-authored-by: Mike Griese <zadjii@gmail.com>
Co-authored-by: Niels Laute <niels.laute@live.nl>
Co-authored-by: Michael Hawker <24302614+michael-hawker@users.noreply.github.com>
Co-authored-by: Stefan Markovic <57057282+stefansjfw@users.noreply.github.com>
Co-authored-by: Seraphima <zykovas91@gmail.com>
Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
Co-authored-by: Kristen Schau <47155823+krschau@users.noreply.github.com>
Co-authored-by: Eric Johnson <ericjohnson327@gmail.com>
Co-authored-by: Ethan Fang <ethanfang@microsoft.com>
Co-authored-by: Yu Leng (from Dev Box) <yuleng@microsoft.com>
Co-authored-by: Clint Rutkas <clint@rutkas.com>
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public sealed partial class AnonymousCommand : InvokableCommand
|
||||
{
|
||||
private readonly Action? _action;
|
||||
|
||||
public ICommandResult Result { get; set; } = CommandResult.Dismiss();
|
||||
|
||||
public AnonymousCommand(Action? action)
|
||||
{
|
||||
Name = "Invoke";
|
||||
_action = action;
|
||||
}
|
||||
|
||||
public override ICommandResult Invoke()
|
||||
{
|
||||
if (_action != null)
|
||||
{
|
||||
_action();
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// 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 Windows.Foundation;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
// TODO! We probably want to have OnPropertyChanged raise the event
|
||||
// asynchronously, so as to not block the extension app while it's being
|
||||
// processed in the host app.
|
||||
// (also consider this for ItemsChanged in ListPage)
|
||||
public partial class BaseObservable : INotifyPropChanged
|
||||
{
|
||||
public event TypedEventHandler<object, IPropChangedEventArgs>? PropChanged;
|
||||
|
||||
protected void OnPropertyChanged(string propertyName)
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO #181 - This is dangerous! If the original host goes away,
|
||||
// this can crash as we try to invoke the handlers from that process.
|
||||
// However, just catching it seems to still raise the event on the
|
||||
// new host?
|
||||
PropChanged?.Invoke(this, new PropChangedEventArgs(propertyName));
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
// 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.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public sealed class ChoiceSetSetting : Setting<string>
|
||||
{
|
||||
public partial class Choice
|
||||
{
|
||||
[JsonPropertyName("value")]
|
||||
public string Value { get; set; }
|
||||
|
||||
[JsonPropertyName("title")]
|
||||
public string Title { get; set; }
|
||||
|
||||
public Choice(string title, string value)
|
||||
{
|
||||
Value = value;
|
||||
Title = title;
|
||||
}
|
||||
}
|
||||
|
||||
public List<Choice> Choices { get; set; }
|
||||
|
||||
private ChoiceSetSetting()
|
||||
: base()
|
||||
{
|
||||
Choices = [];
|
||||
}
|
||||
|
||||
public ChoiceSetSetting(string key, List<Choice> choices)
|
||||
: base(key, choices.ElementAt(0).Value)
|
||||
{
|
||||
Choices = choices;
|
||||
}
|
||||
|
||||
public ChoiceSetSetting(string key, string label, string description, List<Choice> choices)
|
||||
: base(key, label, description, choices.ElementAt(0).Value)
|
||||
{
|
||||
Choices = choices;
|
||||
}
|
||||
|
||||
public override Dictionary<string, object> ToDictionary()
|
||||
{
|
||||
return new Dictionary<string, object>
|
||||
{
|
||||
{ "type", "Input.ChoiceSet" },
|
||||
{ "title", Label },
|
||||
{ "id", Key },
|
||||
{ "label", Description },
|
||||
{ "choices", Choices },
|
||||
{ "value", Value ?? string.Empty },
|
||||
{ "isRequired", IsRequired },
|
||||
{ "errorMessage", ErrorMessage },
|
||||
};
|
||||
}
|
||||
|
||||
public static ChoiceSetSetting LoadFromJson(JsonObject jsonObject) => new() { Value = jsonObject["value"]?.GetValue<string>() ?? string.Empty };
|
||||
|
||||
public override void Update(JsonObject payload)
|
||||
{
|
||||
// If the key doesn't exist in the payload, don't do anything
|
||||
if (payload[Key] != null)
|
||||
{
|
||||
Value = payload[Key]?.GetValue<string>();
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToState() => $"\"{Key}\": {JsonSerializer.Serialize(Value, JsonSerializationContext.Default.String)}";
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
// 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.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
// shamelessly from https://github.com/PowerShell/PowerShell/blob/master/src/Microsoft.PowerShell.Commands.Management/commands/management/Clipboard.cs
|
||||
public static partial class ClipboardHelper
|
||||
{
|
||||
private static readonly bool? _clipboardSupported = true;
|
||||
|
||||
// Used if an external clipboard is not available, e.g. if xclip is missing.
|
||||
// This is useful for testing in CI as well.
|
||||
private static string? _internalClipboard;
|
||||
|
||||
public static string GetText()
|
||||
{
|
||||
if (_clipboardSupported == false)
|
||||
{
|
||||
return _internalClipboard ?? string.Empty;
|
||||
}
|
||||
|
||||
var tool = string.Empty;
|
||||
var args = string.Empty;
|
||||
var clipboardText = string.Empty;
|
||||
|
||||
ExecuteOnStaThread(() => GetTextImpl(out clipboardText));
|
||||
return clipboardText;
|
||||
}
|
||||
|
||||
public static void SetText(string text)
|
||||
{
|
||||
if (_clipboardSupported == false)
|
||||
{
|
||||
_internalClipboard = text;
|
||||
return;
|
||||
}
|
||||
|
||||
var tool = string.Empty;
|
||||
var args = string.Empty;
|
||||
ExecuteOnStaThread(() => SetClipboardData(Tuple.Create(text, CF_UNICODETEXT)));
|
||||
return;
|
||||
}
|
||||
|
||||
public static void SetRtf(string plainText, string rtfText)
|
||||
{
|
||||
if (s_CF_RTF == 0)
|
||||
{
|
||||
s_CF_RTF = RegisterClipboardFormat("Rich Text Format");
|
||||
}
|
||||
|
||||
ExecuteOnStaThread(() => SetClipboardData(
|
||||
Tuple.Create(plainText, CF_UNICODETEXT),
|
||||
Tuple.Create(rtfText, s_CF_RTF)));
|
||||
}
|
||||
|
||||
#pragma warning disable SA1310 // Field names should not contain underscore
|
||||
private const uint GMEM_MOVEABLE = 0x0002;
|
||||
private const uint GMEM_ZEROINIT = 0x0040;
|
||||
#pragma warning restore SA1310 // Field names should not contain underscore
|
||||
private const uint GHND = GMEM_MOVEABLE | GMEM_ZEROINIT;
|
||||
|
||||
[LibraryImport("kernel32.dll")]
|
||||
private static partial IntPtr GlobalAlloc(uint flags, UIntPtr dwBytes);
|
||||
|
||||
[LibraryImport("kernel32.dll")]
|
||||
private static partial IntPtr GlobalFree(IntPtr hMem);
|
||||
|
||||
[LibraryImport("kernel32.dll")]
|
||||
private static partial IntPtr GlobalLock(IntPtr hMem);
|
||||
|
||||
[LibraryImport("kernel32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static partial bool GlobalUnlock(IntPtr hMem);
|
||||
|
||||
[LibraryImport("kernel32.dll", EntryPoint = "RtlMoveMemory")]
|
||||
private static partial void CopyMemory(IntPtr dest, IntPtr src, uint count);
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static partial bool IsClipboardFormatAvailable(uint format);
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static partial bool OpenClipboard(IntPtr hWndNewOwner);
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static partial bool CloseClipboard();
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static partial bool EmptyClipboard();
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
private static partial IntPtr GetClipboardData(uint format);
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
private static partial IntPtr SetClipboardData(uint format, IntPtr data);
|
||||
|
||||
[LibraryImport("user32.dll", StringMarshalling = StringMarshalling.Utf16)]
|
||||
private static partial uint RegisterClipboardFormat(string lpszFormat);
|
||||
|
||||
#pragma warning disable SA1310 // Field names should not contain underscore
|
||||
private const uint CF_TEXT = 1;
|
||||
private const uint CF_UNICODETEXT = 13;
|
||||
|
||||
#pragma warning disable SA1308 // Variable names should not be prefixed
|
||||
private static uint s_CF_RTF;
|
||||
#pragma warning restore SA1308 // Variable names should not be prefixed
|
||||
#pragma warning restore SA1310 // Field names should not contain underscore
|
||||
|
||||
private static bool GetTextImpl(out string text)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (IsClipboardFormatAvailable(CF_UNICODETEXT))
|
||||
{
|
||||
if (OpenClipboard(IntPtr.Zero))
|
||||
{
|
||||
var data = GetClipboardData(CF_UNICODETEXT);
|
||||
if (data != IntPtr.Zero)
|
||||
{
|
||||
data = GlobalLock(data);
|
||||
text = Marshal.PtrToStringUni(data) ?? string.Empty;
|
||||
GlobalUnlock(data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (IsClipboardFormatAvailable(CF_TEXT))
|
||||
{
|
||||
if (OpenClipboard(IntPtr.Zero))
|
||||
{
|
||||
var data = GetClipboardData(CF_TEXT);
|
||||
if (data != IntPtr.Zero)
|
||||
{
|
||||
data = GlobalLock(data);
|
||||
text = Marshal.PtrToStringAnsi(data) ?? string.Empty;
|
||||
GlobalUnlock(data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore exceptions
|
||||
}
|
||||
finally
|
||||
{
|
||||
CloseClipboard();
|
||||
}
|
||||
|
||||
text = string.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool SetClipboardData(params Tuple<string, uint>[] data)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!OpenClipboard(IntPtr.Zero))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
EmptyClipboard();
|
||||
|
||||
foreach (var d in data)
|
||||
{
|
||||
if (!SetSingleClipboardData(d.Item1, d.Item2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
CloseClipboard();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool SetSingleClipboardData(string text, uint format)
|
||||
{
|
||||
var hGlobal = IntPtr.Zero;
|
||||
var data = IntPtr.Zero;
|
||||
|
||||
try
|
||||
{
|
||||
uint bytes;
|
||||
if (format == s_CF_RTF || format == CF_TEXT)
|
||||
{
|
||||
bytes = (uint)(text.Length + 1);
|
||||
data = Marshal.StringToHGlobalAnsi(text);
|
||||
}
|
||||
else if (format == CF_UNICODETEXT)
|
||||
{
|
||||
bytes = (uint)((text.Length + 1) * 2);
|
||||
data = Marshal.StringToHGlobalUni(text);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not yet supported format.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data == IntPtr.Zero)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
hGlobal = GlobalAlloc(GHND, (UIntPtr)bytes);
|
||||
if (hGlobal == IntPtr.Zero)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var dataCopy = GlobalLock(hGlobal);
|
||||
if (dataCopy == IntPtr.Zero)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CopyMemory(dataCopy, data, bytes);
|
||||
GlobalUnlock(hGlobal);
|
||||
|
||||
if (SetClipboardData(format, hGlobal) != IntPtr.Zero)
|
||||
{
|
||||
// The clipboard owns this memory now, so don't free it.
|
||||
hGlobal = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore failures
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (data != IntPtr.Zero)
|
||||
{
|
||||
Marshal.FreeHGlobal(data);
|
||||
}
|
||||
|
||||
if (hGlobal != IntPtr.Zero)
|
||||
{
|
||||
GlobalFree(hGlobal);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void ExecuteOnStaThread(Func<bool> action)
|
||||
{
|
||||
const int RetryCount = 5;
|
||||
var tries = 0;
|
||||
|
||||
if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
|
||||
{
|
||||
while (tries++ < RetryCount && !action())
|
||||
{
|
||||
// wait until RetryCount or action
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Exception? exception = null;
|
||||
var thread = new Thread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
while (tries++ < RetryCount && !action())
|
||||
{
|
||||
// wait until RetryCount or action
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
exception = e;
|
||||
}
|
||||
});
|
||||
|
||||
thread.SetApartmentState(ApartmentState.STA);
|
||||
thread.Start();
|
||||
thread.Join();
|
||||
|
||||
if (exception != null)
|
||||
{
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public sealed class ColorHelpers
|
||||
{
|
||||
public static OptionalColor FromArgb(byte a, byte r, byte g, byte b) => new(true, new(r, g, b, a));
|
||||
|
||||
public static OptionalColor FromRgb(byte r, byte g, byte b) => new(true, new(r, g, b, 255));
|
||||
|
||||
public static OptionalColor Transparent() => new(true, new(0, 0, 0, 0));
|
||||
|
||||
public static OptionalColor NoColor() => new(false, new(0, 0, 0, 0));
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class Command : BaseObservable, ICommand
|
||||
{
|
||||
public virtual string Name
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(Name));
|
||||
}
|
||||
}
|
||||
|
||||
= string.Empty;
|
||||
|
||||
public virtual string Id { get; protected set; } = string.Empty;
|
||||
|
||||
public virtual IconInfo Icon
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(Icon));
|
||||
}
|
||||
}
|
||||
|
||||
= new();
|
||||
|
||||
IIconInfo ICommand.Icon => Icon;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class CommandContextItem : CommandItem, ICommandContextItem
|
||||
{
|
||||
public virtual bool IsCritical { get; set; }
|
||||
|
||||
public virtual KeyChord RequestedShortcut { get; set; }
|
||||
|
||||
public CommandContextItem(ICommand command)
|
||||
: base(command)
|
||||
{
|
||||
}
|
||||
|
||||
public CommandContextItem(
|
||||
string title,
|
||||
string subtitle = "",
|
||||
string name = "",
|
||||
Action? action = null,
|
||||
ICommandResult? result = null)
|
||||
{
|
||||
var c = new AnonymousCommand(action);
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
{
|
||||
c.Name = name;
|
||||
}
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
c.Result = result;
|
||||
}
|
||||
|
||||
Command = c;
|
||||
|
||||
Title = title;
|
||||
Subtitle = subtitle;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class CommandItem : BaseObservable, ICommandItem
|
||||
{
|
||||
private ICommand? _command;
|
||||
|
||||
public virtual IIconInfo? Icon
|
||||
{
|
||||
get => field;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(Icon));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string Title
|
||||
{
|
||||
get => !string.IsNullOrEmpty(field) ? field : _command?.Name ?? string.Empty;
|
||||
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(Title));
|
||||
}
|
||||
}
|
||||
|
||||
= string.Empty;
|
||||
|
||||
public virtual string Subtitle
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(Subtitle));
|
||||
}
|
||||
}
|
||||
|
||||
= string.Empty;
|
||||
|
||||
public virtual ICommand? Command
|
||||
{
|
||||
get => _command;
|
||||
set
|
||||
{
|
||||
_command = value;
|
||||
OnPropertyChanged(nameof(Command));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IContextItem[] MoreCommands
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(MoreCommands));
|
||||
}
|
||||
}
|
||||
|
||||
= [];
|
||||
|
||||
public CommandItem()
|
||||
: this(new NoOpCommand())
|
||||
{
|
||||
}
|
||||
|
||||
public CommandItem(ICommand command)
|
||||
{
|
||||
Command = command;
|
||||
Title = command.Name;
|
||||
}
|
||||
|
||||
public CommandItem(ICommandItem other)
|
||||
{
|
||||
Command = other.Command;
|
||||
Title = other.Title;
|
||||
Subtitle = other.Subtitle;
|
||||
Icon = (IconInfo?)other.Icon;
|
||||
MoreCommands = other.MoreCommands;
|
||||
}
|
||||
|
||||
public CommandItem(
|
||||
string title,
|
||||
string subtitle = "",
|
||||
string name = "",
|
||||
Action? action = null,
|
||||
ICommandResult? result = null)
|
||||
{
|
||||
var c = new AnonymousCommand(action);
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
{
|
||||
c.Name = name;
|
||||
}
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
c.Result = result;
|
||||
}
|
||||
|
||||
Command = c;
|
||||
|
||||
Title = title;
|
||||
Subtitle = subtitle;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// 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 Windows.Foundation;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public abstract partial class CommandProvider : ICommandProvider
|
||||
{
|
||||
public virtual string Id { get; protected set; } = string.Empty;
|
||||
|
||||
public virtual string DisplayName { get; protected set; } = string.Empty;
|
||||
|
||||
public virtual IconInfo Icon { get; protected set; } = new IconInfo();
|
||||
|
||||
public event TypedEventHandler<object, IItemsChangedEventArgs>? ItemsChanged;
|
||||
|
||||
public abstract ICommandItem[] TopLevelCommands();
|
||||
|
||||
public virtual IFallbackCommandItem[]? FallbackCommands() => null;
|
||||
|
||||
public virtual ICommand? GetCommand(string id) => null;
|
||||
|
||||
public virtual ICommandSettings? Settings { get; protected set; }
|
||||
|
||||
public virtual bool Frozen { get; protected set; } = true;
|
||||
|
||||
IIconInfo ICommandProvider.Icon => Icon;
|
||||
|
||||
public virtual void InitializeWithHost(IExtensionHost host) => ExtensionHost.Initialize(host);
|
||||
|
||||
#pragma warning disable CA1816 // Dispose methods should call SuppressFinalize
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
#pragma warning restore CA1816 // Dispose methods should call SuppressFinalize
|
||||
|
||||
protected void RaiseItemsChanged(int totalItems = -1)
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO #181 - This is the same thing that BaseObservable has to deal with.
|
||||
ItemsChanged?.Invoke(this, new ItemsChangedEventArgs(totalItems));
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class CommandResult : ICommandResult
|
||||
{
|
||||
public ICommandResultArgs? Args { get; private set; }
|
||||
|
||||
public CommandResultKind Kind { get; private set; } = CommandResultKind.Dismiss;
|
||||
|
||||
public static CommandResult Dismiss()
|
||||
{
|
||||
return new CommandResult()
|
||||
{
|
||||
Kind = CommandResultKind.Dismiss,
|
||||
};
|
||||
}
|
||||
|
||||
public static CommandResult GoHome()
|
||||
{
|
||||
return new CommandResult()
|
||||
{
|
||||
Kind = CommandResultKind.GoHome,
|
||||
Args = null,
|
||||
};
|
||||
}
|
||||
|
||||
public static CommandResult GoBack()
|
||||
{
|
||||
return new CommandResult()
|
||||
{
|
||||
Kind = CommandResultKind.GoBack,
|
||||
Args = null,
|
||||
};
|
||||
}
|
||||
|
||||
public static CommandResult Hide()
|
||||
{
|
||||
return new CommandResult()
|
||||
{
|
||||
Kind = CommandResultKind.Hide,
|
||||
Args = null,
|
||||
};
|
||||
}
|
||||
|
||||
public static CommandResult KeepOpen()
|
||||
{
|
||||
return new CommandResult()
|
||||
{
|
||||
Kind = CommandResultKind.KeepOpen,
|
||||
Args = null,
|
||||
};
|
||||
}
|
||||
|
||||
public static CommandResult GoToPage(GoToPageArgs args)
|
||||
{
|
||||
return new CommandResult()
|
||||
{
|
||||
Kind = CommandResultKind.GoToPage,
|
||||
Args = args,
|
||||
};
|
||||
}
|
||||
|
||||
public static CommandResult ShowToast(ToastArgs args)
|
||||
{
|
||||
return new CommandResult()
|
||||
{
|
||||
Kind = CommandResultKind.ShowToast,
|
||||
Args = args,
|
||||
};
|
||||
}
|
||||
|
||||
public static CommandResult ShowToast(string message)
|
||||
{
|
||||
return new CommandResult()
|
||||
{
|
||||
Kind = CommandResultKind.ShowToast,
|
||||
Args = new ToastArgs() { Message = message },
|
||||
};
|
||||
}
|
||||
|
||||
public static CommandResult Confirm(ConfirmationArgs args)
|
||||
{
|
||||
return new CommandResult()
|
||||
{
|
||||
Kind = CommandResultKind.Confirm,
|
||||
Args = args,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class ConfirmationArgs : IConfirmationArgs
|
||||
{
|
||||
public virtual string? Title { get; set; }
|
||||
|
||||
public virtual string? Description { get; set; }
|
||||
|
||||
public virtual ICommand? PrimaryCommand { get; set; }
|
||||
|
||||
public virtual bool IsPrimaryCommandCritical { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// 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 Windows.Foundation;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public abstract partial class ContentPage : Page, IContentPage
|
||||
{
|
||||
public event TypedEventHandler<object, IItemsChangedEventArgs>? ItemsChanged;
|
||||
|
||||
public virtual IDetails? Details
|
||||
{
|
||||
get => field;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(Details));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IContextItem[] Commands { get; set; } = [];
|
||||
|
||||
public abstract IContent[] GetContent();
|
||||
|
||||
protected void RaiseItemsChanged(int totalItems = -1)
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO #181 - This is the same thing that BaseObservable has to deal with.
|
||||
ItemsChanged?.Invoke(this, new ItemsChangedEventArgs(totalItems));
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class CopyTextCommand : InvokableCommand
|
||||
{
|
||||
public virtual string Text { get; set; }
|
||||
|
||||
public virtual CommandResult Result { get; set; } = CommandResult.ShowToast("Copied to clipboard");
|
||||
|
||||
public CopyTextCommand(string text)
|
||||
{
|
||||
Text = text;
|
||||
Name = "Copy";
|
||||
Icon = new IconInfo("\uE8C8");
|
||||
}
|
||||
|
||||
public override ICommandResult Invoke()
|
||||
{
|
||||
ClipboardHelper.SetText(Text);
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class Details : BaseObservable, IDetails
|
||||
{
|
||||
public virtual IIconInfo HeroImage
|
||||
{
|
||||
get => field;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(HeroImage));
|
||||
}
|
||||
}
|
||||
|
||||
= new IconInfo();
|
||||
|
||||
public virtual string Title
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(Title));
|
||||
}
|
||||
}
|
||||
|
||||
= string.Empty;
|
||||
|
||||
public virtual string Body
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(Body));
|
||||
}
|
||||
}
|
||||
|
||||
= string.Empty;
|
||||
|
||||
public virtual IDetailsElement[] Metadata
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(Metadata));
|
||||
}
|
||||
}
|
||||
|
||||
= [];
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class DetailsCommand : IDetailsCommand
|
||||
{
|
||||
public ICommand? Command { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class DetailsElement : IDetailsElement
|
||||
{
|
||||
public virtual string Key { get; set; } = string.Empty;
|
||||
|
||||
public virtual IDetailsData? Data { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class DetailsLink : IDetailsLink
|
||||
{
|
||||
public virtual Uri? Link { get; set; }
|
||||
|
||||
public virtual string Text { get; set; } = string.Empty;
|
||||
|
||||
public DetailsLink()
|
||||
{
|
||||
}
|
||||
|
||||
public DetailsLink(string url)
|
||||
: this(url, url)
|
||||
{
|
||||
}
|
||||
|
||||
public DetailsLink(string url, string text)
|
||||
{
|
||||
if (Uri.TryCreate(url, default(UriCreationOptions), out var newUri))
|
||||
{
|
||||
Link = newUri;
|
||||
}
|
||||
|
||||
Text = text;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class DetailsSeparator : IDetailsSeparator
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class DetailsTags : IDetailsTags
|
||||
{
|
||||
public ITag[] Tags { get; set; } = [];
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public abstract class DynamicListPage : ListPage, IDynamicListPage
|
||||
{
|
||||
public override string SearchText
|
||||
{
|
||||
get => base.SearchText;
|
||||
set
|
||||
{
|
||||
var oldSearch = base.SearchText;
|
||||
base.SearchText = value;
|
||||
UpdateSearchText(oldSearch, value);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void UpdateSearchText(string oldSearch, string newSearch);
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class ExtensionHost
|
||||
{
|
||||
public static IExtensionHost? Host { get; private set; }
|
||||
|
||||
public static void Initialize(IExtensionHost host) => Host = host;
|
||||
|
||||
/// <summary>
|
||||
/// Fire-and-forget a log message to the Command Palette host app. Since
|
||||
/// the host is in another process, we do this in a try/catch in a
|
||||
/// background thread, as to not block the calling thread, nor explode if
|
||||
/// the host app is gone.
|
||||
/// </summary>
|
||||
/// <param name="message">The log message to send</param>
|
||||
public static void LogMessage(ILogMessage message)
|
||||
{
|
||||
if (Host != null)
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await Host.LogMessage(message);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void LogMessage(string message)
|
||||
{
|
||||
var logMessage = new LogMessage() { Message = message };
|
||||
LogMessage(logMessage);
|
||||
}
|
||||
|
||||
public static void ShowStatus(IStatusMessage message, StatusContext context)
|
||||
{
|
||||
if (Host != null)
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await Host.ShowStatus(message, context);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void HideStatus(IStatusMessage message)
|
||||
{
|
||||
if (Host != null)
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await Host.HideStatus(message);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
using System.Runtime.InteropServices.Marshalling;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using WinRT;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions;
|
||||
|
||||
[ComVisible(true)]
|
||||
[GeneratedComClass]
|
||||
internal sealed partial class ExtensionInstanceManager : IClassFactory
|
||||
{
|
||||
#pragma warning disable SA1310 // Field names should not contain underscore
|
||||
|
||||
private const int E_NOINTERFACE = unchecked((int)0x80004002);
|
||||
|
||||
private const int CLASS_E_NOAGGREGATION = unchecked((int)0x80040110);
|
||||
|
||||
private const int E_ACCESSDENIED = unchecked((int)0x80070005);
|
||||
|
||||
// Known constant ignored by win32metadata and cswin32 projections.
|
||||
// https://github.com/microsoft/win32metadata/blob/main/generation/WinSDK/RecompiledIdlHeaders/um/processthreadsapi.h
|
||||
private static readonly HANDLE CURRENT_THREAD_PSEUDO_HANDLE = (HANDLE)(IntPtr)(-6);
|
||||
|
||||
private static readonly Guid IID_IUnknown = Guid.Parse("00000000-0000-0000-C000-000000000046");
|
||||
|
||||
#pragma warning restore SA1310 // Field names should not contain underscore
|
||||
|
||||
private readonly Func<IExtension> _createExtension;
|
||||
|
||||
private readonly bool _restrictToMicrosoftExtensionHosts;
|
||||
|
||||
private readonly Guid _clsid;
|
||||
|
||||
public ExtensionInstanceManager(Func<IExtension> createExtension, bool restrictToMicrosoftExtensionHosts, Guid clsid)
|
||||
{
|
||||
_createExtension = createExtension;
|
||||
_restrictToMicrosoftExtensionHosts = restrictToMicrosoftExtensionHosts;
|
||||
_clsid = clsid;
|
||||
}
|
||||
|
||||
public void CreateInstance(
|
||||
[MarshalAs(UnmanagedType.Interface)] object pUnkOuter,
|
||||
Guid riid,
|
||||
out IntPtr ppvObject)
|
||||
{
|
||||
if (_restrictToMicrosoftExtensionHosts && !IsMicrosoftExtensionHost())
|
||||
{
|
||||
Marshal.ThrowExceptionForHR(E_ACCESSDENIED);
|
||||
}
|
||||
|
||||
ppvObject = IntPtr.Zero;
|
||||
|
||||
if (pUnkOuter != null)
|
||||
{
|
||||
Marshal.ThrowExceptionForHR(CLASS_E_NOAGGREGATION);
|
||||
}
|
||||
|
||||
if (riid == _clsid || riid == IID_IUnknown)
|
||||
{
|
||||
// Create the instance of the .NET object
|
||||
var managed = _createExtension();
|
||||
var ins = MarshalInspectable<object>.FromManaged(managed);
|
||||
ppvObject = ins;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The object that ppvObject points to does not support the
|
||||
// interface identified by riid.
|
||||
Marshal.ThrowExceptionForHR(E_NOINTERFACE);
|
||||
}
|
||||
}
|
||||
|
||||
public void LockServer([MarshalAs(UnmanagedType.Bool)] bool fLock)
|
||||
{
|
||||
}
|
||||
|
||||
private unsafe bool IsMicrosoftExtensionHost()
|
||||
{
|
||||
if (PInvoke.CoImpersonateClient() != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint buffer = 0;
|
||||
if (PInvoke.GetPackageFamilyNameFromToken(CURRENT_THREAD_PSEUDO_HANDLE, &buffer, null) != WIN32_ERROR.ERROR_INSUFFICIENT_BUFFER)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var value = new char[buffer];
|
||||
fixed (char* p = value)
|
||||
{
|
||||
if (PInvoke.GetPackageFamilyNameFromToken(CURRENT_THREAD_PSEUDO_HANDLE, &buffer, p) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (PInvoke.CoRevertToSelf() != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var valueStr = new string(value);
|
||||
return valueStr switch
|
||||
{
|
||||
"Microsoft.Windows.CmdPal_8wekyb3d8bbwe\0" or "Microsoft.Windows.CmdPal.Canary_8wekyb3d8bbwe\0" or "Microsoft.Windows.CmdPal.Dev_8wekyb3d8bbwe\0" or "Microsoft.Windows.DevHome_8wekyb3d8bbwe\0" or "Microsoft.Windows.DevHome.Canary_8wekyb3d8bbwe\0" or "Microsoft.Windows.DevHome.Dev_8wekyb3d8bbwe\0" or "Microsoft.WindowsTerminal\0" or "Microsoft.WindowsTerminal_8wekyb3d8bbwe\0" or "WindowsTerminalDev_8wekyb3d8bbwe\0" or "Microsoft.WindowsTerminalPreview_8wekyb3d8bbwe\0" => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.microsoft.com/windows/win32/api/unknwn/nn-unknwn-iclassfactory
|
||||
[GeneratedComInterface]
|
||||
[Guid("00000001-0000-0000-C000-000000000046")]
|
||||
internal partial interface IClassFactory
|
||||
{
|
||||
void CreateInstance(
|
||||
[MarshalAs(UnmanagedType.Interface)] object pUnkOuter,
|
||||
Guid riid,
|
||||
out IntPtr ppvObject);
|
||||
|
||||
void LockServer([MarshalAs(UnmanagedType.Bool)] bool fLock);
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
// 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.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.InteropServices.Marshalling;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions;
|
||||
|
||||
public sealed partial class ExtensionServer : IDisposable
|
||||
{
|
||||
private readonly HashSet<int> registrationCookies = [];
|
||||
private ExtensionInstanceManager? _extensionInstanceManager;
|
||||
private ComWrappers? _comWrappers;
|
||||
|
||||
public void RegisterExtension<T>(Func<T> createExtension, bool restrictToMicrosoftExtensionHosts = false)
|
||||
where T : IExtension
|
||||
{
|
||||
Trace.WriteLine($"Registering class object:");
|
||||
Trace.Indent();
|
||||
Trace.WriteLine($"CLSID: {typeof(T).GUID:B}");
|
||||
Trace.WriteLine($"Type: {typeof(T)}");
|
||||
|
||||
int cookie;
|
||||
var clsid = typeof(T).GUID;
|
||||
var wrappedCallback = () => (IExtension)createExtension();
|
||||
_extensionInstanceManager ??= new ExtensionInstanceManager(wrappedCallback, restrictToMicrosoftExtensionHosts, typeof(T).GUID);
|
||||
_comWrappers ??= new StrategyBasedComWrappers();
|
||||
|
||||
var f = _comWrappers.GetOrCreateComInterfaceForObject(_extensionInstanceManager, CreateComInterfaceFlags.None);
|
||||
|
||||
var hr = Ole32.CoRegisterClassObject(
|
||||
ref clsid,
|
||||
f,
|
||||
Ole32.CLSCTX_LOCAL_SERVER,
|
||||
Ole32.REGCLS_MULTIPLEUSE | Ole32.REGCLS_SUSPENDED,
|
||||
out cookie);
|
||||
|
||||
if (hr < 0)
|
||||
{
|
||||
Marshal.ThrowExceptionForHR(hr);
|
||||
}
|
||||
|
||||
registrationCookies.Add(cookie);
|
||||
Trace.WriteLine($"Cookie: {cookie}");
|
||||
Trace.Unindent();
|
||||
|
||||
hr = Ole32.CoResumeClassObjects();
|
||||
if (hr < 0)
|
||||
{
|
||||
Marshal.ThrowExceptionForHR(hr);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable CA1822 // Mark members as static
|
||||
public void Run() =>
|
||||
|
||||
// TODO : We need to handle lifetime management of the server.
|
||||
// For details around ref counting and locking of out-of-proc COM servers, see
|
||||
// https://docs.microsoft.com/windows/win32/com/out-of-process-server-implementation-helpers
|
||||
Console.ReadLine();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Trace.WriteLine($"Revoking class object registrations:");
|
||||
Trace.Indent();
|
||||
foreach (var cookie in registrationCookies)
|
||||
{
|
||||
Trace.WriteLine($"Cookie: {cookie}");
|
||||
var hr = Ole32.CoRevokeClassObject(cookie);
|
||||
Debug.Assert(hr >= 0, $"CoRevokeClassObject failed ({hr:x}). Cookie: {cookie}");
|
||||
}
|
||||
|
||||
Trace.Unindent();
|
||||
}
|
||||
|
||||
private sealed class Ole32
|
||||
{
|
||||
#pragma warning disable SA1310 // Field names should not contain underscore
|
||||
// https://docs.microsoft.com/windows/win32/api/wtypesbase/ne-wtypesbase-clsctx
|
||||
public const int CLSCTX_LOCAL_SERVER = 0x4;
|
||||
|
||||
// https://docs.microsoft.com/windows/win32/api/combaseapi/ne-combaseapi-regcls
|
||||
public const int REGCLS_MULTIPLEUSE = 1;
|
||||
public const int REGCLS_SUSPENDED = 4;
|
||||
#pragma warning restore SA1310 // Field names should not contain underscore
|
||||
|
||||
// https://docs.microsoft.com/windows/win32/api/combaseapi/nf-combaseapi-coregisterclassobject
|
||||
[DllImport(nameof(Ole32))]
|
||||
public static extern int CoRegisterClassObject(ref Guid guid, IntPtr obj, int context, int flags, out int register);
|
||||
|
||||
// https://docs.microsoft.com/windows/win32/api/combaseapi/nf-combaseapi-coresumeclassobjects
|
||||
[DllImport(nameof(Ole32))]
|
||||
public static extern int CoResumeClassObjects();
|
||||
|
||||
// https://docs.microsoft.com/windows/win32/api/combaseapi/nf-combaseapi-corevokeclassobject
|
||||
[DllImport(nameof(Ole32))]
|
||||
public static extern int CoRevokeClassObject(int register);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class FallbackCommandItem : CommandItem, IFallbackCommandItem, IFallbackHandler
|
||||
{
|
||||
private IFallbackHandler? _fallbackHandler;
|
||||
|
||||
public FallbackCommandItem(ICommand command, string displayTitle)
|
||||
: base(command)
|
||||
{
|
||||
DisplayTitle = displayTitle;
|
||||
if (command is IFallbackHandler f)
|
||||
{
|
||||
_fallbackHandler = f;
|
||||
}
|
||||
}
|
||||
|
||||
public IFallbackHandler? FallbackHandler
|
||||
{
|
||||
get => _fallbackHandler ?? this;
|
||||
init => _fallbackHandler = value;
|
||||
}
|
||||
|
||||
public virtual string DisplayTitle { get; }
|
||||
|
||||
public virtual void UpdateQuery(string query) => _fallbackHandler?.UpdateQuery(query);
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class Filter : BaseObservable, IFilter
|
||||
{
|
||||
public virtual IIconInfo Icon
|
||||
{
|
||||
get => field;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(Icon));
|
||||
}
|
||||
}
|
||||
|
||||
= new IconInfo();
|
||||
|
||||
public virtual string Id
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(Id));
|
||||
}
|
||||
}
|
||||
|
||||
= string.Empty;
|
||||
|
||||
public virtual string Name
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(Name));
|
||||
}
|
||||
}
|
||||
|
||||
= string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class FormContent : BaseObservable, IFormContent
|
||||
{
|
||||
public virtual string DataJson
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(DataJson));
|
||||
}
|
||||
}
|
||||
|
||||
= string.Empty;
|
||||
|
||||
public virtual string StateJson
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(StateJson));
|
||||
}
|
||||
}
|
||||
|
||||
= string.Empty;
|
||||
|
||||
public virtual string TemplateJson
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(TemplateJson));
|
||||
}
|
||||
}
|
||||
|
||||
= string.Empty;
|
||||
|
||||
public virtual ICommandResult SubmitForm(string inputs, string data) => SubmitForm(inputs);
|
||||
|
||||
public virtual ICommandResult SubmitForm(string inputs) => CommandResult.KeepOpen();
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// 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.Diagnostics.CodeAnalysis;
|
||||
|
||||
[assembly: SuppressMessage("Usage", "CsWinRT1028:Class is not marked partial", Justification = "Type is not passed across the WinRT ABI", Scope = "type", Target = "~T:WinRT._EventSource_global__Windows_Foundation_TypedEventHandler_object__global__Microsoft_CommandPalette_Extensions_IPropChangedEventArgs_.EventState")]
|
||||
[assembly: SuppressMessage("Usage", "CsWinRT1028:Class is not marked partial", Justification = "Type is not passed across the WinRT ABI", Scope = "type", Target = "~T:WinRT._EventSource_global__Windows_Foundation_TypedEventHandler_object__global__Microsoft_CommandPalette_Extensions_IItemsChangedEventArgs_.EventState")]
|
||||
@@ -0,0 +1,12 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class GoToPageArgs : IGoToPageArgs
|
||||
{
|
||||
public required string PageId { get; set; }
|
||||
|
||||
public NavigationMode NavigationMode { get; set; } = NavigationMode.Push;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// 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.Text.Json.Nodes;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
internal interface ISettingsForm
|
||||
{
|
||||
public string ToForm();
|
||||
|
||||
public void Update(JsonObject payload);
|
||||
|
||||
public Dictionary<string, object> ToDictionary();
|
||||
|
||||
public string ToDataIdentifier();
|
||||
|
||||
public string ToState();
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// 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 Windows.Storage.Streams;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class IconData : IIconData
|
||||
{
|
||||
public IRandomAccessStreamReference? Data { get; set; }
|
||||
|
||||
public string? Icon { get; set; } = string.Empty;
|
||||
|
||||
public IconData(string? icon)
|
||||
{
|
||||
Icon = icon;
|
||||
}
|
||||
|
||||
public IconData(IRandomAccessStreamReference data)
|
||||
{
|
||||
Data = data;
|
||||
}
|
||||
|
||||
internal IconData()
|
||||
: this(string.Empty)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public sealed class IconHelpers
|
||||
{
|
||||
public static IconInfo FromRelativePath(string path) => new(Path.Combine(AppDomain.CurrentDomain.BaseDirectory.ToString(), path));
|
||||
|
||||
public static IconInfo FromRelativePaths(string lightIcon, string darkIcon) =>
|
||||
new(
|
||||
new(Path.Combine(AppDomain.CurrentDomain.BaseDirectory.ToString(), lightIcon)),
|
||||
new(Path.Combine(AppDomain.CurrentDomain.BaseDirectory.ToString(), darkIcon)));
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// 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 Windows.Storage.Streams;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class IconInfo : IIconInfo
|
||||
{
|
||||
public virtual IconData Dark { get; set; } = new();
|
||||
|
||||
public virtual IconData Light { get; set; } = new();
|
||||
|
||||
IIconData IIconInfo.Dark => Dark;
|
||||
|
||||
IIconData IIconInfo.Light => Light;
|
||||
|
||||
public IconInfo(string? icon)
|
||||
{
|
||||
Dark = Light = new(icon);
|
||||
}
|
||||
|
||||
public IconInfo(IconData light, IconData dark)
|
||||
{
|
||||
Light = light;
|
||||
Dark = dark;
|
||||
}
|
||||
|
||||
internal IconInfo()
|
||||
: this(string.Empty)
|
||||
{
|
||||
}
|
||||
|
||||
public static IconInfo FromStream(IRandomAccessStream stream)
|
||||
{
|
||||
var data = new IconData(RandomAccessStreamReference.CreateFromStream(stream));
|
||||
return new IconInfo(data, data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public abstract partial class InvokableCommand : Command, IInvokableCommand
|
||||
{
|
||||
public virtual ICommandResult Invoke() => CommandResult.KeepOpen();
|
||||
|
||||
public virtual ICommandResult Invoke(object? sender) => Invoke();
|
||||
}
|
||||
@@ -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 Windows.Foundation;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class ItemsChangedEventArgs : IItemsChangedEventArgs
|
||||
{
|
||||
public int TotalItems { get; protected set; }
|
||||
|
||||
public ItemsChangedEventArgs(int totalItems = -1)
|
||||
{
|
||||
TotalItems = totalItems;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// 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.Text.Json.Serialization;
|
||||
using static Microsoft.CommandPalette.Extensions.Toolkit.ChoiceSetSetting;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
[JsonSerializable(typeof(float))]
|
||||
[JsonSerializable(typeof(int))]
|
||||
[JsonSerializable(typeof(string))]
|
||||
[JsonSerializable(typeof(bool))]
|
||||
[JsonSerializable(typeof(Choice))]
|
||||
[JsonSerializable(typeof(List<Choice>))]
|
||||
[JsonSerializable(typeof(List<ChoiceSetSetting>))]
|
||||
[JsonSerializable(typeof(Dictionary<string, object>), TypeInfoPropertyName = "Dictionary")]
|
||||
[JsonSourceGenerationOptions(UseStringEnumConverter = true, WriteIndented = true)]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "Just used here")]
|
||||
internal partial class JsonSerializationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
// 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.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public abstract class JsonSettingsManager
|
||||
{
|
||||
public Settings Settings { get; } = new();
|
||||
|
||||
public string FilePath { get; init; } = string.Empty;
|
||||
|
||||
private static readonly JsonSerializerOptions _serializerOptions = new()
|
||||
{
|
||||
WriteIndented = true,
|
||||
};
|
||||
|
||||
public virtual void LoadSettings()
|
||||
{
|
||||
if (string.IsNullOrEmpty(FilePath))
|
||||
{
|
||||
throw new InvalidOperationException($"You must set a valid {nameof(FilePath)} before calling {nameof(LoadSettings)}");
|
||||
}
|
||||
|
||||
var filePath = FilePath;
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
ExtensionHost.LogMessage(new LogMessage() { Message = "The provided settings file does not exist" });
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Read the JSON content from the file
|
||||
var jsonContent = File.ReadAllText(filePath);
|
||||
|
||||
// Is it valid JSON?
|
||||
if (JsonNode.Parse(jsonContent) is JsonObject savedSettings)
|
||||
{
|
||||
Settings.Update(jsonContent);
|
||||
}
|
||||
else
|
||||
{
|
||||
ExtensionHost.LogMessage(new LogMessage() { Message = "Failed to parse settings file as JsonObject." });
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExtensionHost.LogMessage(new LogMessage() { Message = ex.ToString() });
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void SaveSettings()
|
||||
{
|
||||
if (string.IsNullOrEmpty(FilePath))
|
||||
{
|
||||
throw new InvalidOperationException($"You must set a valid {nameof(FilePath)} before calling {nameof(SaveSettings)}");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Serialize the main dictionary to JSON and save it to the file
|
||||
var settingsJson = Settings.ToJson();
|
||||
|
||||
// Is it valid JSON?
|
||||
if (JsonNode.Parse(settingsJson) is JsonObject newSettings)
|
||||
{
|
||||
// Now, read the existing content from the file
|
||||
var oldContent = File.Exists(FilePath) ? File.ReadAllText(FilePath) : "{}";
|
||||
|
||||
// Is it valid JSON?
|
||||
if (JsonNode.Parse(oldContent) is JsonObject savedSettings)
|
||||
{
|
||||
foreach (var item in newSettings)
|
||||
{
|
||||
savedSettings[item.Key] = item.Value != null ? item.Value.DeepClone() : null;
|
||||
}
|
||||
|
||||
var serialized = savedSettings.ToJsonString(_serializerOptions);
|
||||
File.WriteAllText(FilePath, serialized);
|
||||
}
|
||||
else
|
||||
{
|
||||
ExtensionHost.LogMessage(new LogMessage() { Message = "Failed to parse settings file as JsonObject." });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ExtensionHost.LogMessage(new LogMessage() { Message = "Failed to parse settings file as JsonObject." });
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExtensionHost.LogMessage(new LogMessage() { Message = ex.ToString() });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// 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 Windows.Foundation;
|
||||
using Windows.System;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class KeyChordHelpers
|
||||
{
|
||||
public static KeyChord FromModifiers(bool ctrl, bool alt, bool shift, bool win, int vkey, int scanCode)
|
||||
{
|
||||
var modifiers = (ctrl ? VirtualKeyModifiers.Control : VirtualKeyModifiers.None)
|
||||
| (alt ? VirtualKeyModifiers.Menu : VirtualKeyModifiers.None)
|
||||
| (shift ? VirtualKeyModifiers.Shift : VirtualKeyModifiers.None)
|
||||
| (win ? VirtualKeyModifiers.Windows : VirtualKeyModifiers.None)
|
||||
;
|
||||
return new(modifiers, vkey, scanCode);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class ListHelpers
|
||||
{
|
||||
// Generate a score for a list item.
|
||||
public static int ScoreListItem(string query, ICommandItem listItem)
|
||||
{
|
||||
if (string.IsNullOrEmpty(query) || string.IsNullOrWhiteSpace(query))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(listItem.Title))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var nameMatch = StringMatcher.FuzzySearch(query, listItem.Title);
|
||||
|
||||
// var locNameMatch = StringMatcher.FuzzySearch(query, NameLocalized);
|
||||
var descriptionMatch = StringMatcher.FuzzySearch(query, listItem.Subtitle);
|
||||
|
||||
// var executableNameMatch = StringMatcher.FuzzySearch(query, ExePath);
|
||||
// var locExecutableNameMatch = StringMatcher.FuzzySearch(query, ExecutableNameLocalized);
|
||||
// var lnkResolvedExecutableNameMatch = StringMatcher.FuzzySearch(query, LnkResolvedExecutableName);
|
||||
// var locLnkResolvedExecutableNameMatch = StringMatcher.FuzzySearch(query, LnkResolvedExecutableNameLocalized);
|
||||
// var score = new[] { nameMatch.Score, (descriptionMatch.Score - 4) / 2, executableNameMatch.Score }.Max();
|
||||
return new[] { nameMatch.Score, (descriptionMatch.Score - 4) / 2, 0 }.Max();
|
||||
}
|
||||
|
||||
public static IEnumerable<IListItem> FilterList(IEnumerable<IListItem> items, string query)
|
||||
{
|
||||
var scores = items
|
||||
.Select(li => new ScoredListItem() { ListItem = li, Score = ScoreListItem(query, li) })
|
||||
.Where(score => score.Score > 0)
|
||||
.OrderByDescending(score => score.Score);
|
||||
return scores
|
||||
.Select(score => score.ListItem);
|
||||
}
|
||||
|
||||
public static IEnumerable<T> FilterList<T>(IEnumerable<T> items, string query, Func<string, T, int> scoreFunction)
|
||||
where T : class
|
||||
{
|
||||
var scores = items
|
||||
.Select(li => new Scored<T>() { Item = li, Score = scoreFunction(query, li) })
|
||||
.Where(score => score.Score > 0)
|
||||
.OrderByDescending(score => score.Score);
|
||||
return scores
|
||||
.Select(score => score.Item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modifies the contents of `original` in-place, to match those of
|
||||
/// `newContents`. The canonical use being:
|
||||
/// ```cs
|
||||
/// ListHelpers.InPlaceUpdateList(FilteredItems, FilterList(ItemsToFilter, TextToFilterOn));
|
||||
/// ```
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Any type that can be compared for equality</typeparam>
|
||||
/// <param name="original">Collection to modify</param>
|
||||
/// <param name="newContents">The enumerable which `original` should match</param>
|
||||
public static void InPlaceUpdateList<T>(IList<T> original, IEnumerable<T> newContents)
|
||||
where T : class
|
||||
{
|
||||
// we're not changing newContents - stash this so we don't re-evaluate it every time
|
||||
var numberOfNew = newContents.Count();
|
||||
|
||||
// Short circuit - new contents should just be empty
|
||||
if (numberOfNew == 0)
|
||||
{
|
||||
original.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
foreach (var newItem in newContents)
|
||||
{
|
||||
if (i >= original.Count)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
for (var j = i; j < original.Count; j++)
|
||||
{
|
||||
var og_2 = original[j];
|
||||
var areEqual_2 = og_2?.Equals(newItem) ?? false;
|
||||
if (areEqual_2)
|
||||
{
|
||||
for (var k = i; k < j; k++)
|
||||
{
|
||||
// This item from the original list was not in the new list. Remove it.
|
||||
original.RemoveAt(i);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var og = original[i];
|
||||
var areEqual = og?.Equals(newItem) ?? false;
|
||||
|
||||
// Is this new item already in the list?
|
||||
if (areEqual)
|
||||
{
|
||||
// It is already in the list
|
||||
}
|
||||
else
|
||||
{
|
||||
// it isn't. Add it.
|
||||
original.Insert(i, newItem);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
// Remove any extra trailing items from the destination
|
||||
while (original.Count > numberOfNew)
|
||||
{
|
||||
// RemoveAtEnd
|
||||
original.RemoveAt(original.Count - 1);
|
||||
}
|
||||
|
||||
// Add any extra trailing items from the source
|
||||
if (original.Count < numberOfNew)
|
||||
{
|
||||
var remaining = newContents.Skip(original.Count);
|
||||
foreach (var item in remaining)
|
||||
{
|
||||
original.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct ScoredListItem
|
||||
{
|
||||
public int Score;
|
||||
public IListItem ListItem;
|
||||
}
|
||||
|
||||
public struct Scored<T>
|
||||
{
|
||||
public int Score;
|
||||
public T Item;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class ListItem : CommandItem, IListItem
|
||||
{
|
||||
private ITag[] _tags = [];
|
||||
private IDetails? _details;
|
||||
|
||||
private string _section = string.Empty;
|
||||
private string _textToSuggest = string.Empty;
|
||||
|
||||
public virtual ITag[] Tags
|
||||
{
|
||||
get => _tags;
|
||||
set
|
||||
{
|
||||
_tags = value;
|
||||
OnPropertyChanged(nameof(Tags));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IDetails? Details
|
||||
{
|
||||
get => _details;
|
||||
set
|
||||
{
|
||||
_details = value;
|
||||
OnPropertyChanged(nameof(Details));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string Section
|
||||
{
|
||||
get => _section;
|
||||
set
|
||||
{
|
||||
_section = value;
|
||||
OnPropertyChanged(nameof(Section));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string TextToSuggest
|
||||
{
|
||||
get => _textToSuggest;
|
||||
set
|
||||
{
|
||||
_textToSuggest = value;
|
||||
OnPropertyChanged(nameof(TextToSuggest));
|
||||
}
|
||||
}
|
||||
|
||||
public ListItem(ICommand command)
|
||||
: base(command)
|
||||
{
|
||||
}
|
||||
|
||||
public ListItem(ICommandItem command)
|
||||
: base(command)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
// 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 Windows.Foundation;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class ListPage : Page, IListPage
|
||||
{
|
||||
private string _placeholderText = string.Empty;
|
||||
private string _searchText = string.Empty;
|
||||
private bool _showDetails;
|
||||
private bool _hasMore;
|
||||
private IFilters? _filters;
|
||||
private IGridProperties? _gridProperties;
|
||||
private ICommandItem? _emptyContent;
|
||||
|
||||
public event TypedEventHandler<object, IItemsChangedEventArgs>? ItemsChanged;
|
||||
|
||||
public virtual string PlaceholderText
|
||||
{
|
||||
get => _placeholderText;
|
||||
set
|
||||
{
|
||||
_placeholderText = value;
|
||||
OnPropertyChanged(nameof(PlaceholderText));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string SearchText
|
||||
{
|
||||
get => _searchText;
|
||||
set
|
||||
{
|
||||
_searchText = value;
|
||||
OnPropertyChanged(nameof(SearchText));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool ShowDetails
|
||||
{
|
||||
get => _showDetails;
|
||||
set
|
||||
{
|
||||
_showDetails = value;
|
||||
OnPropertyChanged(nameof(ShowDetails));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool HasMoreItems
|
||||
{
|
||||
get => _hasMore;
|
||||
set
|
||||
{
|
||||
_hasMore = value;
|
||||
OnPropertyChanged(nameof(HasMoreItems));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IFilters? Filters
|
||||
{
|
||||
get => _filters;
|
||||
set
|
||||
{
|
||||
_filters = value;
|
||||
OnPropertyChanged(nameof(Filters));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IGridProperties? GridProperties
|
||||
{
|
||||
get => _gridProperties;
|
||||
set
|
||||
{
|
||||
_gridProperties = value;
|
||||
OnPropertyChanged(nameof(GridProperties));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual ICommandItem? EmptyContent
|
||||
{
|
||||
get => _emptyContent;
|
||||
set
|
||||
{
|
||||
_emptyContent = value;
|
||||
OnPropertyChanged(nameof(EmptyContent));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IListItem[] GetItems() => [];
|
||||
|
||||
public virtual void LoadMore()
|
||||
{
|
||||
}
|
||||
|
||||
protected void RaiseItemsChanged(int totalItems = -1)
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO #181 - This is the same thing that BaseObservable has to deal with.
|
||||
ItemsChanged?.Invoke(this, new ItemsChangedEventArgs(totalItems));
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class LogMessage : BaseObservable, ILogMessage
|
||||
{
|
||||
private MessageState _messageState = MessageState.Info;
|
||||
|
||||
private string _message = string.Empty;
|
||||
|
||||
public string Message
|
||||
{
|
||||
get => _message;
|
||||
set
|
||||
{
|
||||
_message = value;
|
||||
OnPropertyChanged(nameof(Message));
|
||||
}
|
||||
}
|
||||
|
||||
public MessageState State
|
||||
{
|
||||
get => _messageState;
|
||||
set
|
||||
{
|
||||
_messageState = value;
|
||||
OnPropertyChanged(nameof(State));
|
||||
}
|
||||
}
|
||||
|
||||
public LogMessage(string message = "")
|
||||
{
|
||||
_message = message;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class MarkdownContent : BaseObservable, IMarkdownContent
|
||||
{
|
||||
public virtual string Body
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(Body));
|
||||
}
|
||||
}
|
||||
|
||||
= string.Empty;
|
||||
|
||||
public MarkdownContent()
|
||||
{
|
||||
}
|
||||
|
||||
public MarkdownContent(string body)
|
||||
{
|
||||
Body = body;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// 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.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.Plugin.Program.UnitTests")]
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class MatchOption
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets prefix of match char, use for highlight
|
||||
/// </summary>
|
||||
[Obsolete("this is never used")]
|
||||
public string Prefix { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets suffix of match char, use for highlight
|
||||
/// </summary>
|
||||
[Obsolete("this is never used")]
|
||||
public string Suffix { get; set; } = string.Empty;
|
||||
|
||||
public bool IgnoreCase { get; set; } = true;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// 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.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.Plugin.Program.UnitTests")]
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class MatchResult
|
||||
{
|
||||
public MatchResult(bool success, SearchPrecisionScore searchPrecision)
|
||||
{
|
||||
Success = success;
|
||||
SearchPrecision = searchPrecision;
|
||||
}
|
||||
|
||||
public MatchResult(bool success, SearchPrecisionScore searchPrecision, List<int> matchData, int rawScore)
|
||||
{
|
||||
Success = success;
|
||||
SearchPrecision = searchPrecision;
|
||||
MatchData = matchData;
|
||||
RawScore = rawScore;
|
||||
}
|
||||
|
||||
public bool Success { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the final score of the match result with search precision filters applied.
|
||||
/// </summary>
|
||||
public int Score { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The raw calculated search score without any search precision filtering applied.
|
||||
/// </summary>
|
||||
private int _rawScore;
|
||||
|
||||
public int RawScore
|
||||
{
|
||||
get => _rawScore;
|
||||
|
||||
set
|
||||
{
|
||||
_rawScore = value;
|
||||
Score = ScoreAfterSearchPrecisionFilter(_rawScore);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets matched data to highlight.
|
||||
/// </summary>
|
||||
public List<int> MatchData { get; private set; } = new();
|
||||
|
||||
public SearchPrecisionScore SearchPrecision { get; set; }
|
||||
|
||||
public bool IsSearchPrecisionScoreMet()
|
||||
{
|
||||
return IsSearchPrecisionScoreMet(_rawScore);
|
||||
}
|
||||
|
||||
private bool IsSearchPrecisionScoreMet(int rawScore)
|
||||
{
|
||||
return rawScore >= (int)SearchPrecision;
|
||||
}
|
||||
|
||||
private int ScoreAfterSearchPrecisionFilter(int rawScore)
|
||||
{
|
||||
return IsSearchPrecisionScoreMet(rawScore) ? rawScore : 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\..\..\..\Common.Dotnet.AotCompatibility.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\Microsoft.CommandPalette.Extensions.Toolkit</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<!-- MRT from windows app sdk will search for a pri file with the same name of the module before defaulting to resources.pri -->
|
||||
<ProjectPriFileName>Microsoft.CommandPalette.Extensions.Toolkit.pri</ProjectPriFileName>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<CsWinRTIncludes>Microsoft.CommandPalette.Extensions</CsWinRTIncludes>
|
||||
<CsWinRTGeneratedFilesDir>$(OutDir)</CsWinRTGeneratedFilesDir>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<ControlFlowGuard>Guard</ControlFlowGuard>
|
||||
<SpectreMitigation>Spectre</SpectreMitigation>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" />
|
||||
<PackageReference Include="Microsoft.Web.WebView2" />
|
||||
<PackageReference Include="System.Drawing.Common" />
|
||||
<!-- This line forces the WebView2 version used by Windows App SDK to be the one we expect from Directory.Packages.props . -->
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Microsoft.CommandPalette.Extensions\Microsoft.CommandPalette.Extensions.vcxproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="$(SolutionDir)$(Platform)\$(Configuration)\Microsoft.CommandPalette.Extensions\Microsoft.CommandPalette.Extensions.winmd" Link="Microsoft.CommandPalette.Extensions.winmd" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<PublishTrimmed>True</PublishTrimmed>
|
||||
<PublishAot>True</PublishAot>
|
||||
|
||||
<!-- Suppress DynamicallyAccessedMemberTypes.PublicParameterlessConstructor in fallback code path of Windows SDK projection -->
|
||||
<WarningsNotAsErrors>IL2081</WarningsNotAsErrors>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
internal sealed class NativeMethods
|
||||
{
|
||||
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
|
||||
internal static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbFileInfo, uint uFlags);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
internal struct SHFILEINFO
|
||||
{
|
||||
#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter
|
||||
public IntPtr hIcon;
|
||||
public int iIcon;
|
||||
public uint dwAttributes;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
|
||||
public string szDisplayName;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
|
||||
public string szTypeName;
|
||||
#pragma warning restore SA1307 // Accessible fields should begin with upper-case letter
|
||||
}
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
|
||||
internal static extern bool DestroyIcon(IntPtr hIcon);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
CoImpersonateClient
|
||||
GetCurrentThread
|
||||
OpenThreadToken
|
||||
GetPackageFamilyNameFromToken
|
||||
CoRevertToSelf
|
||||
SHGetKnownFolderPath
|
||||
KNOWN_FOLDER_FLAG
|
||||
GetCurrentPackageId
|
||||
@@ -0,0 +1,10 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class NoOpCommand : InvokableCommand
|
||||
{
|
||||
public override ICommandResult Invoke() => CommandResult.KeepOpen();
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public sealed partial class OpenUrlCommand : InvokableCommand
|
||||
{
|
||||
private readonly string _target;
|
||||
|
||||
public CommandResult Result { get; set; } = CommandResult.KeepOpen();
|
||||
|
||||
public OpenUrlCommand(string target)
|
||||
{
|
||||
_target = target;
|
||||
Name = "Open";
|
||||
Icon = new IconInfo("\uE8A7");
|
||||
}
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
ShellHelpers.OpenInShell(_target);
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class Page : Command, IPage
|
||||
{
|
||||
private bool _loading;
|
||||
private string _title = string.Empty;
|
||||
private OptionalColor _accentColor;
|
||||
|
||||
public virtual bool IsLoading
|
||||
{
|
||||
get => _loading;
|
||||
set
|
||||
{
|
||||
_loading = value;
|
||||
OnPropertyChanged(nameof(IsLoading));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string Title
|
||||
{
|
||||
get => _title;
|
||||
set
|
||||
{
|
||||
_title = value;
|
||||
OnPropertyChanged(nameof(Title));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual OptionalColor AccentColor
|
||||
{
|
||||
get => _accentColor;
|
||||
set
|
||||
{
|
||||
_accentColor = value;
|
||||
OnPropertyChanged(nameof(AccentColor));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class ProgressState : BaseObservable, IProgressState
|
||||
{
|
||||
private bool _isIndeterminate;
|
||||
|
||||
private uint _progressPercent;
|
||||
|
||||
public virtual bool IsIndeterminate
|
||||
{
|
||||
get => _isIndeterminate;
|
||||
set
|
||||
{
|
||||
_isIndeterminate = value;
|
||||
OnPropertyChanged(nameof(IsIndeterminate));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual uint ProgressPercent
|
||||
{
|
||||
get => _progressPercent;
|
||||
set
|
||||
{
|
||||
_progressPercent = value;
|
||||
OnPropertyChanged(nameof(ProgressPercent));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 Windows.Foundation;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class PropChangedEventArgs : IPropChangedEventArgs
|
||||
{
|
||||
public string PropertyName { get; private set; }
|
||||
|
||||
public PropChangedEventArgs(string propertyName)
|
||||
{
|
||||
PropertyName = propertyName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public enum SearchPrecisionScore
|
||||
{
|
||||
Regular = 50,
|
||||
Low = 20,
|
||||
None = 0,
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
// 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.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public abstract class Setting<T> : ISettingsForm
|
||||
{
|
||||
private static readonly JsonSerializerOptions _jsonSerializerOptions = new() { WriteIndented = true };
|
||||
|
||||
public T? Value { get; set; }
|
||||
|
||||
public string Key { get; }
|
||||
|
||||
public bool IsRequired { get; set; }
|
||||
|
||||
public string ErrorMessage { get; set; } = string.Empty;
|
||||
|
||||
public string Label { get; set; } = string.Empty;
|
||||
|
||||
public string Description { get; set; } = string.Empty;
|
||||
|
||||
protected Setting()
|
||||
{
|
||||
Value = default;
|
||||
Key = string.Empty;
|
||||
}
|
||||
|
||||
public Setting(string key, T defaultValue)
|
||||
{
|
||||
Key = key;
|
||||
Value = defaultValue;
|
||||
}
|
||||
|
||||
public Setting(string key, string label, string description, T defaultValue)
|
||||
{
|
||||
Key = key;
|
||||
Value = defaultValue;
|
||||
Label = label;
|
||||
Description = description;
|
||||
}
|
||||
|
||||
public abstract Dictionary<string, object> ToDictionary();
|
||||
|
||||
public string ToDataIdentifier() => $"\"{Key}\": \"{Key}\"";
|
||||
|
||||
public string ToForm()
|
||||
{
|
||||
var bodyJson = JsonSerializer.Serialize(ToDictionary(), JsonSerializationContext.Default.Dictionary);
|
||||
var dataJson = $"\"{Key}\": \"{Key}\"";
|
||||
|
||||
var json = $$"""
|
||||
{
|
||||
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
|
||||
"type": "AdaptiveCard",
|
||||
"version": "1.5",
|
||||
"body": [
|
||||
{{bodyJson}}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"type": "Action.Submit",
|
||||
"title": "Save",
|
||||
"data": {
|
||||
{{dataJson}}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
""";
|
||||
return json;
|
||||
}
|
||||
|
||||
public abstract void Update(JsonObject payload);
|
||||
|
||||
public abstract string ToState();
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
// 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.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public sealed partial class Settings : ICommandSettings
|
||||
{
|
||||
private readonly Dictionary<string, object> _settings = [];
|
||||
private static readonly JsonSerializerOptions _jsonSerializerOptions = new() { WriteIndented = true };
|
||||
|
||||
public event TypedEventHandler<object, Settings>? SettingsChanged;
|
||||
|
||||
public void Add<T>(Setting<T> s) => _settings.Add(s.Key, s);
|
||||
|
||||
public T? GetSetting<T>(string key) => _settings[key] is Setting<T> s ? s.Value : default;
|
||||
|
||||
public bool TryGetSetting<T>(string key, out T? val)
|
||||
{
|
||||
object? o;
|
||||
if (_settings.TryGetValue(key, out o))
|
||||
{
|
||||
if (o is Setting<T> s)
|
||||
{
|
||||
val = s.Value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
val = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
internal string ToFormJson()
|
||||
{
|
||||
var settings = _settings
|
||||
.Values
|
||||
.Where(s => s is ISettingsForm)
|
||||
.Select(s => s as ISettingsForm)
|
||||
.Where(s => s != null)
|
||||
.Select(s => s!);
|
||||
|
||||
var bodies = string.Join(",", settings
|
||||
.Select(s => JsonSerializer.Serialize(s.ToDictionary(), JsonSerializationContext.Default.Dictionary)));
|
||||
|
||||
var datas = string.Join(",", settings.Select(s => s.ToDataIdentifier()));
|
||||
|
||||
var json = $$"""
|
||||
{
|
||||
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
|
||||
"type": "AdaptiveCard",
|
||||
"version": "1.5",
|
||||
"body": [
|
||||
{{bodies}}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"type": "Action.Submit",
|
||||
"title": "Save",
|
||||
"data": {
|
||||
{{datas}}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
""";
|
||||
return json;
|
||||
}
|
||||
|
||||
public string ToJson()
|
||||
{
|
||||
var settings = _settings
|
||||
.Values
|
||||
.Where(s => s is ISettingsForm)
|
||||
.Select(s => s as ISettingsForm)
|
||||
.Where(s => s != null)
|
||||
.Select(s => s!);
|
||||
var content = string.Join(",\n", settings.Select(s => s.ToState()));
|
||||
return $"{{\n{content}\n}}";
|
||||
}
|
||||
|
||||
public void Update(string data)
|
||||
{
|
||||
var formInput = JsonNode.Parse(data)?.AsObject();
|
||||
if (formInput == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var key in _settings.Keys)
|
||||
{
|
||||
var value = _settings[key];
|
||||
if (value is ISettingsForm f)
|
||||
{
|
||||
f.Update(formInput);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void RaiseSettingsChanged()
|
||||
{
|
||||
var handlers = SettingsChanged;
|
||||
handlers?.Invoke(this, this);
|
||||
}
|
||||
|
||||
private sealed partial class SettingsContentPage : ContentPage
|
||||
{
|
||||
private readonly Settings _settings;
|
||||
|
||||
public override IContent[] GetContent() => _settings.ToContent();
|
||||
|
||||
public SettingsContentPage(Settings settings)
|
||||
{
|
||||
_settings = settings;
|
||||
Name = "Settings";
|
||||
Icon = new IconInfo("\uE713"); // Settings icon
|
||||
}
|
||||
}
|
||||
|
||||
public IContentPage SettingsPage => new SettingsContentPage(this);
|
||||
|
||||
public IContent[] ToContent() => [new SettingsForm(this)];
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// 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.Text.Json.Nodes;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class SettingsForm : FormContent
|
||||
{
|
||||
private readonly Settings _settings;
|
||||
|
||||
internal SettingsForm(Settings settings)
|
||||
{
|
||||
_settings = settings;
|
||||
TemplateJson = _settings.ToFormJson();
|
||||
}
|
||||
|
||||
public override ICommandResult SubmitForm(string inputs, string data)
|
||||
{
|
||||
var formInput = JsonNode.Parse(inputs)?.AsObject();
|
||||
if (formInput == null)
|
||||
{
|
||||
return CommandResult.KeepOpen();
|
||||
}
|
||||
|
||||
_settings.Update(inputs);
|
||||
_settings.RaiseSettingsChanged();
|
||||
|
||||
return CommandResult.GoHome();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// 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.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public static class ShellHelpers
|
||||
{
|
||||
public static bool OpenCommandInShell(string? path, string? pattern, string? arguments, string? workingDir = null, ShellRunAsType runAs = ShellRunAsType.None, bool runWithHiddenWindow = false)
|
||||
{
|
||||
if (string.IsNullOrEmpty(pattern))
|
||||
{
|
||||
// Log.Warn($"Trying to run OpenCommandInShell with an empty pattern. The default browser definition might have issues. Path: '${path ?? string.Empty}' ; Arguments: '${arguments ?? string.Empty}' ; Working Directory: '${workingDir ?? string.Empty}'", typeof(ShellHelpers));
|
||||
}
|
||||
else if (pattern.Contains("%1", StringComparison.Ordinal))
|
||||
{
|
||||
arguments = pattern.Replace("%1", arguments);
|
||||
}
|
||||
|
||||
return OpenInShell(path, arguments, workingDir, runAs, runWithHiddenWindow);
|
||||
}
|
||||
|
||||
public static bool OpenInShell(string? path, string? arguments = null, string? workingDir = null, ShellRunAsType runAs = ShellRunAsType.None, bool runWithHiddenWindow = false)
|
||||
{
|
||||
using var process = new Process();
|
||||
process.StartInfo.FileName = path;
|
||||
process.StartInfo.WorkingDirectory = string.IsNullOrWhiteSpace(workingDir) ? string.Empty : workingDir;
|
||||
process.StartInfo.Arguments = string.IsNullOrWhiteSpace(arguments) ? string.Empty : arguments;
|
||||
process.StartInfo.WindowStyle = runWithHiddenWindow ? ProcessWindowStyle.Hidden : ProcessWindowStyle.Normal;
|
||||
process.StartInfo.UseShellExecute = true;
|
||||
|
||||
if (runAs == ShellRunAsType.Administrator)
|
||||
{
|
||||
process.StartInfo.Verb = "RunAs";
|
||||
}
|
||||
else if (runAs == ShellRunAsType.OtherUser)
|
||||
{
|
||||
process.StartInfo.Verb = "RunAsUser";
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
process.Start();
|
||||
return true;
|
||||
}
|
||||
catch (Win32Exception)
|
||||
{
|
||||
// Log.Exception($"Unable to open {path}: {ex.Message}", ex, MethodBase.GetCurrentMethod().DeclaringType);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public enum ShellRunAsType
|
||||
{
|
||||
None,
|
||||
Administrator,
|
||||
OtherUser,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// 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.Diagnostics;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class ShowFileInFolderCommand : InvokableCommand
|
||||
{
|
||||
private readonly string _path;
|
||||
private static readonly IconInfo Ico = new("\uE838");
|
||||
|
||||
public CommandResult Result { get; set; } = CommandResult.Dismiss();
|
||||
|
||||
public ShowFileInFolderCommand(string path)
|
||||
{
|
||||
_path = path;
|
||||
Name = "Show in folder";
|
||||
Icon = Ico;
|
||||
}
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
if (Path.Exists(_path))
|
||||
{
|
||||
try
|
||||
{
|
||||
var argument = "/select, \"" + _path + "\"";
|
||||
Process.Start("explorer.exe", argument);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class StatusMessage : BaseObservable, IStatusMessage
|
||||
{
|
||||
public virtual string Message
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(Message));
|
||||
}
|
||||
}
|
||||
|
||||
= string.Empty;
|
||||
|
||||
public virtual MessageState State
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(State));
|
||||
}
|
||||
}
|
||||
|
||||
= MessageState.Info;
|
||||
|
||||
public virtual IProgressState? Progress
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(Progress));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,311 @@
|
||||
// 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;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class StringMatcher
|
||||
{
|
||||
private readonly MatchOption _defaultMatchOption = new();
|
||||
|
||||
public SearchPrecisionScore UserSettingSearchPrecision { get; set; }
|
||||
|
||||
// private readonly IAlphabet _alphabet;
|
||||
public StringMatcher(/*IAlphabet alphabet = null*/)
|
||||
{
|
||||
// _alphabet = alphabet;
|
||||
}
|
||||
|
||||
private static StringMatcher? _instance;
|
||||
|
||||
public static StringMatcher Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
_instance ??= new StringMatcher();
|
||||
|
||||
return _instance;
|
||||
}
|
||||
set => _instance = value;
|
||||
}
|
||||
|
||||
private static readonly char[] Separator = new[] { ' ' };
|
||||
|
||||
public static MatchResult FuzzySearch(string query, string stringToCompare)
|
||||
{
|
||||
return Instance.FuzzyMatch(query, stringToCompare);
|
||||
}
|
||||
|
||||
public MatchResult FuzzyMatch(string query, string stringToCompare)
|
||||
{
|
||||
try
|
||||
{
|
||||
return FuzzyMatch(query, stringToCompare, _defaultMatchOption);
|
||||
}
|
||||
catch (IndexOutOfRangeException)
|
||||
{
|
||||
return new MatchResult(false, UserSettingSearchPrecision);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Current method:
|
||||
/// Character matching + substring matching;
|
||||
/// 1. Query search string is split into substrings, separator is whitespace.
|
||||
/// 2. Check each query substring's characters against full compare string,
|
||||
/// 3. if a character in the substring is matched, loop back to verify the previous character.
|
||||
/// 4. If previous character also matches, and is the start of the substring, update list.
|
||||
/// 5. Once the previous character is verified, move on to the next character in the query substring.
|
||||
/// 6. Move onto the next substring's characters until all substrings are checked.
|
||||
/// 7. Consider success and move onto scoring if every char or substring without whitespaces matched
|
||||
/// </summary>
|
||||
public MatchResult FuzzyMatch(string query, string stringToCompare, MatchOption opt)
|
||||
{
|
||||
if (string.IsNullOrEmpty(stringToCompare))
|
||||
{
|
||||
return new MatchResult(false, UserSettingSearchPrecision);
|
||||
}
|
||||
|
||||
var bestResult = new MatchResult(false, UserSettingSearchPrecision);
|
||||
|
||||
for (var startIndex = 0; startIndex < stringToCompare.Length; startIndex++)
|
||||
{
|
||||
MatchResult result = FuzzyMatch(query, stringToCompare, opt, startIndex);
|
||||
if (result.Success && (!bestResult.Success || result.Score > bestResult.Score))
|
||||
{
|
||||
bestResult = result;
|
||||
}
|
||||
}
|
||||
|
||||
return bestResult;
|
||||
}
|
||||
|
||||
private MatchResult FuzzyMatch(string query, string stringToCompare, MatchOption opt, int startIndex)
|
||||
{
|
||||
if (string.IsNullOrEmpty(stringToCompare) || string.IsNullOrEmpty(query))
|
||||
{
|
||||
return new MatchResult(false, UserSettingSearchPrecision);
|
||||
}
|
||||
|
||||
ArgumentNullException.ThrowIfNull(opt);
|
||||
|
||||
query = query.Trim();
|
||||
|
||||
// if (_alphabet != null)
|
||||
// {
|
||||
// query = _alphabet.Translate(query);
|
||||
// stringToCompare = _alphabet.Translate(stringToCompare);
|
||||
// }
|
||||
|
||||
// Using InvariantCulture since this is internal
|
||||
var fullStringToCompareWithoutCase = opt.IgnoreCase ? stringToCompare.ToUpper(CultureInfo.InvariantCulture) : stringToCompare;
|
||||
var queryWithoutCase = opt.IgnoreCase ? query.ToUpper(CultureInfo.InvariantCulture) : query;
|
||||
|
||||
var querySubstrings = queryWithoutCase.Split(Separator, StringSplitOptions.RemoveEmptyEntries);
|
||||
var currentQuerySubstringIndex = 0;
|
||||
var currentQuerySubstring = querySubstrings[currentQuerySubstringIndex];
|
||||
var currentQuerySubstringCharacterIndex = 0;
|
||||
|
||||
var firstMatchIndex = -1;
|
||||
var firstMatchIndexInWord = -1;
|
||||
var lastMatchIndex = 0;
|
||||
var allQuerySubstringsMatched = false;
|
||||
var matchFoundInPreviousLoop = false;
|
||||
var allSubstringsContainedInCompareString = true;
|
||||
|
||||
var indexList = new List<int>();
|
||||
List<int> spaceIndices = new List<int>();
|
||||
|
||||
for (var compareStringIndex = startIndex; compareStringIndex < fullStringToCompareWithoutCase.Length; compareStringIndex++)
|
||||
{
|
||||
// To maintain a list of indices which correspond to spaces in the string to compare
|
||||
// To populate the list only for the first query substring
|
||||
if (fullStringToCompareWithoutCase[compareStringIndex].Equals(' ') && currentQuerySubstringIndex == 0)
|
||||
{
|
||||
spaceIndices.Add(compareStringIndex);
|
||||
}
|
||||
|
||||
bool compareResult;
|
||||
if (opt.IgnoreCase)
|
||||
{
|
||||
var fullStringToCompare = fullStringToCompareWithoutCase[compareStringIndex].ToString();
|
||||
var querySubstring = currentQuerySubstring[currentQuerySubstringCharacterIndex].ToString();
|
||||
#pragma warning disable CA1309 // Use ordinal string comparison (We are looking for a fuzzy match here)
|
||||
compareResult = string.Compare(fullStringToCompare, querySubstring, CultureInfo.CurrentCulture, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace) != 0;
|
||||
#pragma warning restore CA1309 // Use ordinal string comparison
|
||||
}
|
||||
else
|
||||
{
|
||||
compareResult = fullStringToCompareWithoutCase[compareStringIndex] != currentQuerySubstring[currentQuerySubstringCharacterIndex];
|
||||
}
|
||||
|
||||
if (compareResult)
|
||||
{
|
||||
matchFoundInPreviousLoop = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (firstMatchIndex < 0)
|
||||
{
|
||||
// first matched char will become the start of the compared string
|
||||
firstMatchIndex = compareStringIndex;
|
||||
}
|
||||
|
||||
if (currentQuerySubstringCharacterIndex == 0)
|
||||
{
|
||||
// first letter of current word
|
||||
matchFoundInPreviousLoop = true;
|
||||
firstMatchIndexInWord = compareStringIndex;
|
||||
}
|
||||
else if (!matchFoundInPreviousLoop)
|
||||
{
|
||||
// we want to verify that there is not a better match if this is not a full word
|
||||
// in order to do so we need to verify all previous chars are part of the pattern
|
||||
var startIndexToVerify = compareStringIndex - currentQuerySubstringCharacterIndex;
|
||||
|
||||
if (AllPreviousCharsMatched(startIndexToVerify, currentQuerySubstringCharacterIndex, fullStringToCompareWithoutCase, currentQuerySubstring))
|
||||
{
|
||||
matchFoundInPreviousLoop = true;
|
||||
|
||||
// if it's the beginning character of the first query substring that is matched then we need to update start index
|
||||
firstMatchIndex = currentQuerySubstringIndex == 0 ? startIndexToVerify : firstMatchIndex;
|
||||
|
||||
indexList = GetUpdatedIndexList(startIndexToVerify, currentQuerySubstringCharacterIndex, firstMatchIndexInWord, indexList);
|
||||
}
|
||||
}
|
||||
|
||||
lastMatchIndex = compareStringIndex + 1;
|
||||
indexList.Add(compareStringIndex);
|
||||
|
||||
currentQuerySubstringCharacterIndex++;
|
||||
|
||||
// if finished looping through every character in the current substring
|
||||
if (currentQuerySubstringCharacterIndex == currentQuerySubstring.Length)
|
||||
{
|
||||
// if any of the substrings was not matched then consider as all are not matched
|
||||
allSubstringsContainedInCompareString = matchFoundInPreviousLoop && allSubstringsContainedInCompareString;
|
||||
|
||||
currentQuerySubstringIndex++;
|
||||
|
||||
allQuerySubstringsMatched = AllQuerySubstringsMatched(currentQuerySubstringIndex, querySubstrings.Length);
|
||||
if (allQuerySubstringsMatched)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// otherwise move to the next query substring
|
||||
currentQuerySubstring = querySubstrings[currentQuerySubstringIndex];
|
||||
currentQuerySubstringCharacterIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// proceed to calculate score if every char or substring without whitespaces matched
|
||||
if (allQuerySubstringsMatched)
|
||||
{
|
||||
var nearestSpaceIndex = CalculateClosestSpaceIndex(spaceIndices, firstMatchIndex);
|
||||
var score = CalculateSearchScore(query, stringToCompare, firstMatchIndex - nearestSpaceIndex - 1, lastMatchIndex - firstMatchIndex, allSubstringsContainedInCompareString);
|
||||
|
||||
return new MatchResult(true, UserSettingSearchPrecision, indexList, score);
|
||||
}
|
||||
|
||||
return new MatchResult(false, UserSettingSearchPrecision);
|
||||
}
|
||||
|
||||
// To get the index of the closest space which precedes the first matching index
|
||||
private static int CalculateClosestSpaceIndex(List<int> spaceIndices, int firstMatchIndex)
|
||||
{
|
||||
if (spaceIndices.Count == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return spaceIndices.OrderBy(item => (firstMatchIndex - item)).Where(item => firstMatchIndex > item).FirstOrDefault(-1);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool AllPreviousCharsMatched(int startIndexToVerify, int currentQuerySubstringCharacterIndex, string fullStringToCompareWithoutCase, string currentQuerySubstring)
|
||||
{
|
||||
var allMatch = true;
|
||||
for (var indexToCheck = 0; indexToCheck < currentQuerySubstringCharacterIndex; indexToCheck++)
|
||||
{
|
||||
if (fullStringToCompareWithoutCase[startIndexToVerify + indexToCheck] !=
|
||||
currentQuerySubstring[indexToCheck])
|
||||
{
|
||||
allMatch = false;
|
||||
}
|
||||
}
|
||||
|
||||
return allMatch;
|
||||
}
|
||||
|
||||
private static List<int> GetUpdatedIndexList(int startIndexToVerify, int currentQuerySubstringCharacterIndex, int firstMatchIndexInWord, List<int> indexList)
|
||||
{
|
||||
var updatedList = new List<int>();
|
||||
|
||||
indexList.RemoveAll(x => x >= firstMatchIndexInWord);
|
||||
|
||||
updatedList.AddRange(indexList);
|
||||
|
||||
for (var indexToCheck = 0; indexToCheck < currentQuerySubstringCharacterIndex; indexToCheck++)
|
||||
{
|
||||
updatedList.Add(startIndexToVerify + indexToCheck);
|
||||
}
|
||||
|
||||
return updatedList;
|
||||
}
|
||||
|
||||
private static bool AllQuerySubstringsMatched(int currentQuerySubstringIndex, int querySubstringsLength)
|
||||
{
|
||||
return currentQuerySubstringIndex >= querySubstringsLength;
|
||||
}
|
||||
|
||||
private static int CalculateSearchScore(string query, string stringToCompare, int firstIndex, int matchLen, bool allSubstringsContainedInCompareString)
|
||||
{
|
||||
// A match found near the beginning of a string is scored more than a match found near the end
|
||||
// A match is scored more if the characters in the patterns are closer to each other,
|
||||
// while the score is lower if they are more spread out
|
||||
|
||||
// The length of the match is assigned a larger weight factor.
|
||||
// I.e. the length is more important than where in the string a match is found.
|
||||
const int matchLenWeightFactor = 2;
|
||||
|
||||
var score = 100 * (query.Length + 1) * matchLenWeightFactor / ((1 + firstIndex) + (matchLenWeightFactor * (matchLen + 1)));
|
||||
|
||||
// A match with less characters assigning more weights
|
||||
if (stringToCompare.Length - query.Length < 5)
|
||||
{
|
||||
score += 20;
|
||||
}
|
||||
else if (stringToCompare.Length - query.Length < 10)
|
||||
{
|
||||
score += 10;
|
||||
}
|
||||
|
||||
if (allSubstringsContainedInCompareString)
|
||||
{
|
||||
var count = query.Count(c => !char.IsWhiteSpace(c));
|
||||
var threshold = 4;
|
||||
if (count <= threshold)
|
||||
{
|
||||
score += count * 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
score += (threshold * 10) + ((count - threshold) * 5);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable CA1309 // Use ordinal string comparison (Using CurrentCultureIgnoreCase since this relates to queries input by user)
|
||||
if (string.Equals(query, stringToCompare, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
var bonusForExactMatch = 10;
|
||||
score += bonusForExactMatch;
|
||||
}
|
||||
#pragma warning restore CA1309 // Use ordinal string comparison
|
||||
|
||||
return score;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class Tag : BaseObservable, ITag
|
||||
{
|
||||
private OptionalColor _foreground;
|
||||
private OptionalColor _background;
|
||||
private string _text = string.Empty;
|
||||
|
||||
public virtual OptionalColor Foreground
|
||||
{
|
||||
get => _foreground;
|
||||
set
|
||||
{
|
||||
_foreground = value;
|
||||
OnPropertyChanged(nameof(Foreground));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual OptionalColor Background
|
||||
{
|
||||
get => _background;
|
||||
set
|
||||
{
|
||||
_background = value;
|
||||
OnPropertyChanged(nameof(Background));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IIconInfo Icon
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(Icon));
|
||||
}
|
||||
}
|
||||
|
||||
= new IconInfo();
|
||||
|
||||
public virtual string Text
|
||||
{
|
||||
get => _text;
|
||||
set
|
||||
{
|
||||
_text = value;
|
||||
OnPropertyChanged(nameof(Text));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string ToolTip
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(ToolTip));
|
||||
}
|
||||
}
|
||||
|
||||
= string.Empty;
|
||||
|
||||
public Tag()
|
||||
{
|
||||
}
|
||||
|
||||
public Tag(string text)
|
||||
{
|
||||
_text = text;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// 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.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class TextSetting : Setting<string>
|
||||
{
|
||||
public bool Multiline { get; set; }
|
||||
|
||||
public string Placeholder { get; set; } = string.Empty;
|
||||
|
||||
private TextSetting()
|
||||
: base()
|
||||
{
|
||||
Value = string.Empty;
|
||||
}
|
||||
|
||||
public TextSetting(string key, string defaultValue)
|
||||
: base(key, defaultValue)
|
||||
{
|
||||
}
|
||||
|
||||
public TextSetting(string key, string label, string description, string defaultValue)
|
||||
: base(key, label, description, defaultValue)
|
||||
{
|
||||
}
|
||||
|
||||
public override Dictionary<string, object> ToDictionary()
|
||||
{
|
||||
return new Dictionary<string, object>
|
||||
{
|
||||
{ "type", "Input.Text" },
|
||||
{ "title", Label },
|
||||
{ "id", Key },
|
||||
{ "label", Description },
|
||||
{ "value", Value ?? string.Empty },
|
||||
{ "isRequired", IsRequired },
|
||||
{ "errorMessage", ErrorMessage },
|
||||
{ "isMultiline", Multiline },
|
||||
{ "placeholder", Placeholder },
|
||||
};
|
||||
}
|
||||
|
||||
public static TextSetting LoadFromJson(JsonObject jsonObject) => new() { Value = jsonObject["value"]?.GetValue<string>() ?? string.Empty };
|
||||
|
||||
public override void Update(JsonObject payload)
|
||||
{
|
||||
// If the key doesn't exist in the payload, don't do anything
|
||||
if (payload[Key] != null)
|
||||
{
|
||||
Value = payload[Key]?.GetValue<string>();
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToState() => $"\"{Key}\": {JsonSerializer.Serialize(Value, JsonSerializationContext.Default.String)}";
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
// 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.Drawing;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.FileProperties;
|
||||
using Windows.Storage.Streams;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public class ThumbnailHelper
|
||||
{
|
||||
private static readonly string[] ImageExtensions =
|
||||
[
|
||||
".png",
|
||||
".jpg",
|
||||
".jpeg",
|
||||
".gif",
|
||||
".bmp",
|
||||
".tiff",
|
||||
".ico",
|
||||
];
|
||||
|
||||
public static Task<IRandomAccessStream?> GetThumbnail(string path)
|
||||
{
|
||||
var extension = Path.GetExtension(path).ToLower(CultureInfo.InvariantCulture);
|
||||
try
|
||||
{
|
||||
if (ImageExtensions.Contains(extension))
|
||||
{
|
||||
return GetImageThumbnailAsync(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetFileIconStream(path);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
|
||||
return Task.FromResult<IRandomAccessStream?>(null);
|
||||
}
|
||||
|
||||
private const uint SHGFIICON = 0x000000100;
|
||||
private const uint SHGFILARGEICON = 0x000000000;
|
||||
|
||||
private static MemoryStream GetMemoryStreamFromIcon(IntPtr hIcon)
|
||||
{
|
||||
var memoryStream = new MemoryStream();
|
||||
|
||||
// Ensure disposing the icon before freeing the handle
|
||||
using (var icon = Icon.FromHandle(hIcon))
|
||||
{
|
||||
icon.ToBitmap().Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png);
|
||||
}
|
||||
|
||||
// Clean up the unmanaged handle without risking a use-after-free.
|
||||
NativeMethods.DestroyIcon(hIcon);
|
||||
|
||||
memoryStream.Position = 0;
|
||||
return memoryStream;
|
||||
}
|
||||
|
||||
private static async Task<IRandomAccessStream?> GetFileIconStream(string filePath)
|
||||
{
|
||||
var shinfo = default(NativeMethods.SHFILEINFO);
|
||||
var hr = NativeMethods.SHGetFileInfo(filePath, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), SHGFIICON | SHGFILARGEICON);
|
||||
|
||||
if (hr == 0 || shinfo.hIcon == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var stream = new InMemoryRandomAccessStream();
|
||||
|
||||
using var memoryStream = GetMemoryStreamFromIcon(shinfo.hIcon);
|
||||
using var outputStream = stream.GetOutputStreamAt(0);
|
||||
using (var dataWriter = new DataWriter(outputStream))
|
||||
{
|
||||
dataWriter.WriteBytes(memoryStream.ToArray());
|
||||
await dataWriter.StoreAsync();
|
||||
await dataWriter.FlushAsync();
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
private static async Task<IRandomAccessStream?> GetImageThumbnailAsync(string filePath)
|
||||
{
|
||||
var file = await StorageFile.GetFileFromPathAsync(filePath);
|
||||
var thumbnail = await file.GetThumbnailAsync(ThumbnailMode.PicturesView);
|
||||
return thumbnail;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class ToastArgs : IToastArgs
|
||||
{
|
||||
public string? Message { get; set; }
|
||||
|
||||
public ICommandResult? Result { get; set; } = CommandResult.Dismiss();
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class ToastStatusMessage
|
||||
{
|
||||
private readonly Lock _showLock = new();
|
||||
private bool _shown;
|
||||
|
||||
public virtual StatusMessage Message { get; init; }
|
||||
|
||||
public virtual int Duration { get; init; } = 2500;
|
||||
|
||||
public ToastStatusMessage(StatusMessage message)
|
||||
{
|
||||
Message = message;
|
||||
}
|
||||
|
||||
public ToastStatusMessage(string text)
|
||||
{
|
||||
Message = new StatusMessage() { Message = text };
|
||||
}
|
||||
|
||||
public void Show()
|
||||
{
|
||||
lock (_showLock)
|
||||
{
|
||||
if (!_shown)
|
||||
{
|
||||
ExtensionHost.ShowStatus(Message, StatusContext.Extension);
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
Thread.Sleep(Duration);
|
||||
|
||||
lock (_showLock)
|
||||
{
|
||||
_shown = false;
|
||||
ExtensionHost.HideStatus(Message);
|
||||
}
|
||||
});
|
||||
_shown = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// 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.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public sealed class ToggleSetting : Setting<bool>
|
||||
{
|
||||
private ToggleSetting()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
public ToggleSetting(string key, bool defaultValue)
|
||||
: base(key, defaultValue)
|
||||
{
|
||||
}
|
||||
|
||||
public ToggleSetting(string key, string label, string description, bool defaultValue)
|
||||
: base(key, label, description, defaultValue)
|
||||
{
|
||||
}
|
||||
|
||||
public override Dictionary<string, object> ToDictionary()
|
||||
{
|
||||
return new Dictionary<string, object>
|
||||
{
|
||||
{ "type", "Input.Toggle" },
|
||||
{ "title", Label },
|
||||
{ "id", Key },
|
||||
{ "label", Description },
|
||||
{ "value", JsonSerializer.Serialize(Value, JsonSerializationContext.Default.Boolean) },
|
||||
{ "isRequired", IsRequired },
|
||||
{ "errorMessage", ErrorMessage },
|
||||
};
|
||||
}
|
||||
|
||||
public static ToggleSetting LoadFromJson(JsonObject jsonObject) => new() { Value = jsonObject["value"]?.GetValue<bool>() ?? false };
|
||||
|
||||
public override void Update(JsonObject payload)
|
||||
{
|
||||
// If the key doesn't exist in the payload, don't do anything
|
||||
if (payload[Key] != null)
|
||||
{
|
||||
// Adaptive cards returns boolean values as a string "true"/"false", cause of course.
|
||||
var strFromJson = payload[Key]?.GetValue<string>() ?? string.Empty;
|
||||
var val = strFromJson switch { "true" => true, "false" => false, _ => false };
|
||||
Value = val;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToState()
|
||||
{
|
||||
var adaptiveCardsUsesStringsForBools = Value ? "true" : "false";
|
||||
return $"\"{Key}\": \"{adaptiveCardsUsesStringsForBools}\"";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// 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 Windows.Foundation;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class TreeContent : BaseObservable, ITreeContent
|
||||
{
|
||||
public IContent[] Children { get; set; } = [];
|
||||
|
||||
public virtual IContent? RootContent
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(RootContent));
|
||||
}
|
||||
}
|
||||
|
||||
public event TypedEventHandler<object, IItemsChangedEventArgs>? ItemsChanged;
|
||||
|
||||
public virtual IContent[] GetChildren() => Children;
|
||||
|
||||
protected void RaiseItemsChanged(int totalItems = -1)
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO #181 - This is the same thing that BaseObservable has to deal with.
|
||||
ItemsChanged?.Invoke(this, new ItemsChangedEventArgs(totalItems));
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.UI.Shell;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1312:Variable names should begin with lower-case letter", Justification = "This file has more than a couple Windows constants in it, which don't make sense to rename")]
|
||||
public class Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to produce a path to a settings folder which your app can use.
|
||||
/// If your app is running packaged, this will return the redirected local
|
||||
/// app data path (Packages/{your_pfn}/LocalState). If not, it'll return
|
||||
/// %LOCALAPPDATA%\{settingsFolderName}.
|
||||
///
|
||||
/// Does not ensure that the directory exists. Callers should call
|
||||
/// CreateDirectory before writing settings files to this directory.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// var directory = Utilities.BaseSettingsPath("Some.Unique.String.Here");
|
||||
/// Directory.CreateDirectory(directory);
|
||||
/// </example>
|
||||
/// <param name="settingsFolderName">A fallback directory name to use
|
||||
/// inside of %LocalAppData%, in the case this app is not currently running
|
||||
/// in a package context</param>
|
||||
/// <returns>The path to a folder to use for storing settings.</returns>
|
||||
public static string BaseSettingsPath(string settingsFolderName)
|
||||
{
|
||||
// KF_FLAG_FORCE_APP_DATA_REDIRECTION, when engaged, causes SHGet... to return
|
||||
// the new AppModel paths (Packages/xxx/RoamingState, etc.) for standard path requests.
|
||||
// Using this flag allows us to avoid Windows.Storage.ApplicationData completely.
|
||||
var FOLDERID_LocalAppData = new Guid("F1B32785-6FBA-4FCF-9D55-7B8E7F157091");
|
||||
var hr = PInvoke.SHGetKnownFolderPath(
|
||||
FOLDERID_LocalAppData,
|
||||
(uint)KNOWN_FOLDER_FLAG.KF_FLAG_FORCE_APP_DATA_REDIRECTION,
|
||||
null,
|
||||
out var localAppDataFolder);
|
||||
|
||||
if (hr.Succeeded)
|
||||
{
|
||||
var basePath = new string(localAppDataFolder.ToString());
|
||||
if (!IsPackaged())
|
||||
{
|
||||
basePath = Path.Combine(basePath, settingsFolderName);
|
||||
}
|
||||
|
||||
return basePath;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Marshal.GetExceptionForHR(hr.Value)!;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Can be used to quickly determine if this process is running with package identity.
|
||||
/// </summary>
|
||||
/// <returns>true iff the process is running with package identity</returns>
|
||||
public static bool IsPackaged()
|
||||
{
|
||||
uint bufferSize = 0;
|
||||
var bytes = Array.Empty<byte>();
|
||||
|
||||
// CsWinRT apparently won't generate this constant
|
||||
var APPMODEL_ERROR_NO_PACKAGE = (WIN32_ERROR)15700;
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* p = bytes)
|
||||
{
|
||||
// We don't actually need the package ID. We just need to know
|
||||
// if we have a package or not, and APPMODEL_ERROR_NO_PACKAGE
|
||||
// is a quick way to find out.
|
||||
var win32Error = PInvoke.GetCurrentPackageId(ref bufferSize, p);
|
||||
return win32Error != APPMODEL_ERROR_NO_PACKAGE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
EXPORTS
|
||||
DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE
|
||||
DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE
|
||||
@@ -0,0 +1,368 @@
|
||||
namespace Microsoft.CommandPalette.Extensions
|
||||
{
|
||||
[contractversion(1)]
|
||||
apicontract ExtensionsContract {}
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IExtension {
|
||||
IInspectable GetProvider(ProviderType providerType);
|
||||
void Dispose();
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
enum ProviderType {
|
||||
Commands = 0,
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IIconData {
|
||||
String Icon { get; };
|
||||
Windows.Storage.Streams.IRandomAccessStreamReference Data { get; };
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IIconInfo {
|
||||
IIconData Light { get; };
|
||||
IIconData Dark { get; };
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
struct KeyChord
|
||||
{
|
||||
Windows.System.VirtualKeyModifiers Modifiers;
|
||||
Int32 Vkey;
|
||||
Int32 ScanCode;
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface INotifyPropChanged {
|
||||
event Windows.Foundation.TypedEventHandler<Object, IPropChangedEventArgs> PropChanged;
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IPropChangedEventArgs {
|
||||
String PropertyName { get; };
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface INotifyItemsChanged {
|
||||
event Windows.Foundation.TypedEventHandler<Object, IItemsChangedEventArgs> ItemsChanged;
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IItemsChangedEventArgs {
|
||||
Int32 TotalItems { get; };
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface ICommand requires INotifyPropChanged{
|
||||
String Name{ get; };
|
||||
String Id{ get; };
|
||||
IIconInfo Icon{ get; };
|
||||
}
|
||||
|
||||
enum CommandResultKind {
|
||||
Dismiss, // Reset the palette to the main page and dismiss
|
||||
GoHome, // Go back to the main page, but keep it open
|
||||
GoBack, // Go back one level
|
||||
Hide, // Keep this page open, but hide the palette.
|
||||
KeepOpen, // Do nothing.
|
||||
GoToPage, // Go to another page. GoToPageArgs will tell you where.
|
||||
ShowToast, // Display a transient message to the user
|
||||
Confirm, // Display a confirmation dialog
|
||||
};
|
||||
|
||||
enum NavigationMode {
|
||||
Push, // Push the target page onto the navigation stack
|
||||
GoBack, // Go back one page before navigating to the target page
|
||||
GoHome, // Go back to the home page before navigating to the target page
|
||||
};
|
||||
|
||||
[uuid("f9d6423b-bd5e-44bb-a204-2f5c77a72396")]
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface ICommandResultArgs{};
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface ICommandResult {
|
||||
CommandResultKind Kind { get; };
|
||||
ICommandResultArgs Args { get; };
|
||||
}
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IGoToPageArgs requires ICommandResultArgs{
|
||||
String PageId { get; };
|
||||
NavigationMode NavigationMode { get; };
|
||||
}
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IToastArgs requires ICommandResultArgs{
|
||||
String Message { get; };
|
||||
ICommandResult Result { get; };
|
||||
}
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IConfirmationArgs requires ICommandResultArgs{
|
||||
String Title { get; };
|
||||
String Description { get; };
|
||||
ICommand PrimaryCommand { get; };
|
||||
Boolean IsPrimaryCommandCritical { get; };
|
||||
}
|
||||
|
||||
// This is a "leaf" of the UI. This is something that can be "done" by the user.
|
||||
// * A ListPage
|
||||
// * the MoreCommands flyout of for a ListItem or a MarkdownPage
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IInvokableCommand requires ICommand {
|
||||
ICommandResult Invoke(Object sender);
|
||||
}
|
||||
|
||||
|
||||
[uuid("ef5db50c-d26b-4aee-9343-9f98739ab411")]
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IFilterItem {}
|
||||
|
||||
[uuid("0a923c7f-5b7b-431d-9898-3c8c841d02ed")]
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface ISeparatorFilterItem requires IFilterItem {}
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IFilter requires IFilterItem {
|
||||
String Id { get; };
|
||||
String Name { get; };
|
||||
IIconInfo Icon { get; };
|
||||
}
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IFilters {
|
||||
String CurrentFilterId { get; set; };
|
||||
IFilterItem[] Filters();
|
||||
}
|
||||
|
||||
struct Color
|
||||
{
|
||||
UInt8 R;
|
||||
UInt8 G;
|
||||
UInt8 B;
|
||||
UInt8 A;
|
||||
};
|
||||
|
||||
struct OptionalColor
|
||||
{
|
||||
Boolean HasValue;
|
||||
Microsoft.CommandPalette.Extensions.Color Color;
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface ITag {
|
||||
IIconInfo Icon { get; };
|
||||
String Text { get; };
|
||||
OptionalColor Foreground { get; };
|
||||
OptionalColor Background { get; };
|
||||
String ToolTip { get; };
|
||||
};
|
||||
|
||||
[uuid("6a6dd345-37a3-4a1e-914d-4f658a4d583d")]
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IDetailsData {}
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IDetailsElement {
|
||||
String Key { get; };
|
||||
IDetailsData Data { get; };
|
||||
}
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IDetails {
|
||||
IIconInfo HeroImage { get; };
|
||||
String Title { get; };
|
||||
String Body { get; };
|
||||
IDetailsElement[] Metadata { get; };
|
||||
}
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IDetailsTags requires IDetailsData {
|
||||
ITag[] Tags { get; };
|
||||
}
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IDetailsLink requires IDetailsData {
|
||||
Windows.Foundation.Uri Link { get; };
|
||||
String Text { get; };
|
||||
}
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IDetailsCommand requires IDetailsData {
|
||||
ICommand Command { get; };
|
||||
}
|
||||
[uuid("58070392-02bb-4e89-9beb-47ceb8c3d741")]
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IDetailsSeparator requires IDetailsData {}
|
||||
|
||||
enum MessageState
|
||||
{
|
||||
Info = 0,
|
||||
Success,
|
||||
Warning,
|
||||
Error,
|
||||
};
|
||||
|
||||
enum StatusContext
|
||||
{
|
||||
Page,
|
||||
Extension
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IProgressState requires INotifyPropChanged
|
||||
{
|
||||
Boolean IsIndeterminate { get; };
|
||||
UInt32 ProgressPercent { get; };
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IStatusMessage requires INotifyPropChanged
|
||||
{
|
||||
MessageState State { get; };
|
||||
IProgressState Progress { get; };
|
||||
String Message { get; };
|
||||
// TODO! Icon maybe? Work with design on this
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface ILogMessage
|
||||
{
|
||||
MessageState State { get; };
|
||||
String Message { get; };
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IExtensionHost
|
||||
{
|
||||
Windows.Foundation.IAsyncAction ShowStatus(IStatusMessage message, StatusContext context);
|
||||
Windows.Foundation.IAsyncAction HideStatus(IStatusMessage message);
|
||||
|
||||
Windows.Foundation.IAsyncAction LogMessage(ILogMessage message);
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IPage requires ICommand {
|
||||
String Title { get; };
|
||||
Boolean IsLoading { get; };
|
||||
|
||||
OptionalColor AccentColor { get; };
|
||||
}
|
||||
|
||||
[uuid("c78b9851-e76b-43ee-8f76-da5ba14e69a4")]
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IContextItem {}
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface ICommandItem requires INotifyPropChanged {
|
||||
ICommand Command{ get; };
|
||||
IContextItem[] MoreCommands{ get; };
|
||||
IIconInfo Icon{ get; };
|
||||
String Title{ get; };
|
||||
String Subtitle{ get; };
|
||||
}
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface ICommandContextItem requires ICommandItem, IContextItem {
|
||||
Boolean IsCritical { get; }; // READ: "make this red"
|
||||
KeyChord RequestedShortcut { get; };
|
||||
}
|
||||
|
||||
[uuid("924a87fc-32fe-4471-9156-84b3b30275a6")]
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface ISeparatorContextItem requires IContextItem {}
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IListItem requires ICommandItem {
|
||||
ITag[] Tags{ get; };
|
||||
IDetails Details{ get; };
|
||||
String Section { get; };
|
||||
String TextToSuggest { get; };
|
||||
}
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IGridProperties {
|
||||
Windows.Foundation.Size TileSize { get; };
|
||||
}
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IListPage requires IPage, INotifyItemsChanged {
|
||||
// DevPal will be responsible for filtering the list of items, unless the
|
||||
// class implements IDynamicListPage
|
||||
String SearchText { get; };
|
||||
String PlaceholderText { get; };
|
||||
Boolean ShowDetails{ get; };
|
||||
IFilters Filters { get; };
|
||||
IGridProperties GridProperties { get; };
|
||||
Boolean HasMoreItems { get; };
|
||||
ICommandItem EmptyContent { get; };
|
||||
|
||||
IListItem[] GetItems();
|
||||
void LoadMore();
|
||||
}
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IDynamicListPage requires IListPage {
|
||||
String SearchText { set; };
|
||||
}
|
||||
|
||||
[uuid("b64def0f-8911-4afa-8f8f-042bd778d088")]
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IContent requires INotifyPropChanged {
|
||||
}
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IFormContent requires IContent {
|
||||
String TemplateJson { get; };
|
||||
String DataJson { get; };
|
||||
String StateJson { get; };
|
||||
ICommandResult SubmitForm(String inputs, String data);
|
||||
}
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IMarkdownContent requires IContent {
|
||||
String Body { get; };
|
||||
}
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface ITreeContent requires IContent, INotifyItemsChanged {
|
||||
IContent RootContent { get; };
|
||||
IContent[] GetChildren();
|
||||
}
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IContentPage requires IPage, INotifyItemsChanged {
|
||||
IContent[] GetContent();
|
||||
IDetails Details { get; };
|
||||
IContextItem[] Commands { get; };
|
||||
}
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface ICommandSettings {
|
||||
IContentPage SettingsPage { get; };
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IFallbackHandler {
|
||||
void UpdateQuery(String query);
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IFallbackCommandItem requires ICommandItem {
|
||||
IFallbackHandler FallbackHandler{ get; };
|
||||
String DisplayTitle { get; };
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface ICommandProvider requires Windows.Foundation.IClosable, INotifyItemsChanged
|
||||
{
|
||||
String Id { get; };
|
||||
String DisplayName { get; };
|
||||
IIconInfo Icon { get; };
|
||||
ICommandSettings Settings { get; };
|
||||
Boolean Frozen { get; };
|
||||
|
||||
ICommandItem[] TopLevelCommands();
|
||||
IFallbackCommandItem[] FallbackCommands();
|
||||
|
||||
ICommand GetCommand(String id);
|
||||
|
||||
void InitializeWithHost(IExtensionHost host);
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<PathToRoot>..\..\..\..\..\</PathToRoot>
|
||||
<WasdkNuget>$(PathToRoot)packages\Microsoft.WindowsAppSDK.1.6.250205002</WasdkNuget>
|
||||
<CppWinRTNuget>$(PathToRoot)packages\Microsoft.Windows.CppWinRT.2.0.240111.5</CppWinRTNuget>
|
||||
<WindowsSdkBuildToolsNuget>$(PathToRoot)packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428</WindowsSdkBuildToolsNuget>
|
||||
<WebView2Nuget>$(PathToRoot)packages\Microsoft.Web.WebView2.1.0.2903.40</WebView2Nuget>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.props')" />
|
||||
<Import Project="$(CppWinRTNuget)\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('$(CppWinRTNuget)\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<Import Project="$(WindowsSdkBuildToolsNuget)\build\Microsoft.Windows.SDK.BuildTools.props" Condition="Exists('$(WindowsSdkBuildToolsNuget)\build\Microsoft.Windows.SDK.BuildTools.props')" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<CppWinRTOptimized>true</CppWinRTOptimized>
|
||||
<CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge>
|
||||
<CppWinRTGenerateWindowsMetadata>true</CppWinRTGenerateWindowsMetadata>
|
||||
<MinimalCoreWin>true</MinimalCoreWin>
|
||||
<ProjectGuid>{305dd37e-c85d-4b08-aafe-7381fa890463}</ProjectGuid>
|
||||
<ProjectName>Microsoft.CommandPalette.Extensions</ProjectName>
|
||||
<RootNamespace>Microsoft.CommandPalette.Extensions</RootNamespace>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
|
||||
<AppContainerApplication>false</AppContainerApplication>
|
||||
<ApplicationType>Windows Store</ApplicationType>
|
||||
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
|
||||
<WindowsTargetPlatformMinVersion>10.0.19041.0</WindowsTargetPlatformMinVersion>
|
||||
<WindowsTargetPlatformVersion>10.0.22621.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|ARM64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|ARM64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\Microsoft.CommandPalette.Extensions\</OutDir>
|
||||
<IntDir>obj\$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<PlatformToolset Condition="'$(VisualStudioVersion)' == '16.0'">v142</PlatformToolset>
|
||||
<PlatformToolset Condition="'$(VisualStudioVersion)' == '15.0'">v141</PlatformToolset>
|
||||
<PlatformToolset Condition="'$(VisualStudioVersion)' == '14.0'">v140</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
<DesktopCompatible>true</DesktopCompatible>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<ClCompile>
|
||||
<!-- We use MultiThreadedDebug, rather than MultiThreadedDebugDLL, to avoid DLL dependencies on VCRUNTIME140d.dll and MSVCP140d.dll. -->
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<!-- Link statically against the runtime and STL, but link dynamically against the CRT by ignoring the static CRT
|
||||
lib and instead linking against the Universal CRT DLL import library. This "hybrid" linking mechanism is
|
||||
supported according to the CRT maintainer. Dynamic linking against the CRT makes the binaries a bit smaller
|
||||
than they would otherwise be if the CRT, runtime, and STL were all statically linked in. -->
|
||||
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries);libucrtd.lib</IgnoreSpecificDefaultLibraries>
|
||||
<AdditionalOptions>%(AdditionalOptions) /defaultlib:ucrtd.lib /profile /opt:ref /opt:icf</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
|
||||
<ClCompile>
|
||||
<!-- We use MultiThreaded, rather than MultiThreadedDLL, to avoid DLL dependencies on VCRUNTIME140.dll and MSVCP140.dll. -->
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<!-- Link statically against the runtime and STL, but link dynamically against the CRT by ignoring the static CRT
|
||||
lib and instead linking against the Universal CRT DLL import library. This "hybrid" linking mechanism is
|
||||
supported according to the CRT maintainer. Dynamic linking against the CRT makes the binaries a bit smaller
|
||||
than they would otherwise be if the CRT, runtime, and STL were all statically linked in. -->
|
||||
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries);libucrt.lib</IgnoreSpecificDefaultLibraries>
|
||||
<AdditionalOptions>%(AdditionalOptions) /defaultlib:ucrt.lib /profile /opt:ref /opt:icf</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||
<AdditionalOptions>%(AdditionalOptions) /bigobj /Zi</AdditionalOptions>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;WIN32_LEAN_AND_MEAN;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
<ControlFlowGuard>Guard</ControlFlowGuard>
|
||||
<SpectreMitigation>Spectre</SpectreMitigation>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
|
||||
<ModuleDefinitionFile>Microsoft.CommandPalette.Extensions.def</ModuleDefinitionFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<!-- <ClCompile Include="Generated Files\module.g.cpp" /> -->
|
||||
<ClCompile Include="winrt_module.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Midl Include="Microsoft.CommandPalette.Extensions.idl" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
<None Include="Microsoft.CommandPalette.Extensions.def" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="readme.txt">
|
||||
<DeploymentContent>false</DeploymentContent>
|
||||
</Text>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="$(WindowsSdkBuildToolsNuget)\build\Microsoft.Windows.SDK.BuildTools.targets" Condition="Exists('$(WindowsSdkBuildToolsNuget)\build\Microsoft.Windows.SDK.BuildTools.targets')" />
|
||||
<Import Project="$(CppWinRTNuget)\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('$(CppWinRTNuget)\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
<Import Project="$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.targets')" />
|
||||
<Import Project="$(WebView2Nuget)\build\native\Microsoft.Web.WebView2.targets" Condition="Exists('$(WebView2Nuget)\build\native\Microsoft.Web.WebView2.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(WindowsSdkBuildToolsNuget)\build\Microsoft.Windows.SDK.BuildTools.props')" Text="$([System.String]::Format('$(ErrorText)', '$(WindowsSdkBuildToolsNuget)\build\Microsoft.Windows.SDK.BuildTools.props'))" />
|
||||
<Error Condition="!Exists('$(WindowsSdkBuildToolsNuget)\build\Microsoft.Windows.SDK.BuildTools.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(WindowsSdkBuildToolsNuget)\build\Microsoft.Windows.SDK.BuildTools.targets'))" />
|
||||
<Error Condition="!Exists('$(CppWinRTNuget)\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '$(CppWinRTNuget)\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('$(CppWinRTNuget)\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(CppWinRTNuget)\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
<Error Condition="!Exists('$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.props'))" />
|
||||
<Error Condition="!Exists('$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.targets'))" />
|
||||
<Error Condition="!Exists('$(WebView2Nuget)\build\native\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(WebView2Nuget)\build\native\Microsoft.Web.WebView2.targets'))" />
|
||||
</Target>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="version.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\common\version\version.vcxproj">
|
||||
<Project>{cc6e41ac-8174-4e8a-8d22-85dd7f4851df}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Web.WebView2" version="1.0.2903.40" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.SDK.BuildTools" version="10.0.22621.2428" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK" version="1.6.250205002" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -0,0 +1 @@
|
||||
#include "pch.h"
|
||||
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include <winrt/base.h>
|
||||
#include <Windows.h>
|
||||
#include <til/winrt.h>
|
||||
@@ -0,0 +1,2 @@
|
||||
The DLL that this project builds exists only to satisfy the build system, and is otherwise unused.
|
||||
This Project provides winmd to the SDK project for building projections.
|
||||
@@ -0,0 +1,112 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
{
|
||||
template<typename T>
|
||||
struct property
|
||||
{
|
||||
explicit constexpr property(auto&&... args) :
|
||||
_value{ std::forward<decltype(args)>(args)... } {}
|
||||
|
||||
property& operator=(const property& other) = default;
|
||||
|
||||
T operator()() const noexcept
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
void operator()(auto&& arg)
|
||||
{
|
||||
_value = std::forward<decltype(arg)>(arg);
|
||||
}
|
||||
explicit operator bool() const noexcept
|
||||
{
|
||||
#ifdef WINRT_Windows_Foundation_H
|
||||
if constexpr (std::is_same_v<T, winrt::hstring>)
|
||||
{
|
||||
return !_value.empty();
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
}
|
||||
bool operator==(const property& other) const noexcept
|
||||
{
|
||||
return _value == other._value;
|
||||
}
|
||||
bool operator!=(const property& other) const noexcept
|
||||
{
|
||||
return _value != other._value;
|
||||
}
|
||||
bool operator==(const T& other) const noexcept
|
||||
{
|
||||
return _value == other;
|
||||
}
|
||||
bool operator!=(const T& other) const noexcept
|
||||
{
|
||||
return _value != other;
|
||||
}
|
||||
|
||||
private:
|
||||
T _value;
|
||||
};
|
||||
|
||||
#ifdef WINRT_Windows_Foundation_H
|
||||
|
||||
template<typename ArgsT>
|
||||
struct event
|
||||
{
|
||||
event<ArgsT>() = default;
|
||||
winrt::event_token operator()(const ArgsT& handler) { return _handlers.add(handler); }
|
||||
void operator()(const winrt::event_token& token) { _handlers.remove(token); }
|
||||
operator bool() const noexcept { return bool(_handlers); }
|
||||
template<typename... Arg>
|
||||
void raise(auto&&... args)
|
||||
{
|
||||
_handlers(std::forward<decltype(args)>(args)...);
|
||||
}
|
||||
winrt::event<ArgsT> _handlers;
|
||||
};
|
||||
|
||||
template<typename SenderT = winrt::Windows::Foundation::IInspectable, typename ArgsT = winrt::Windows::Foundation::IInspectable>
|
||||
struct typed_event
|
||||
{
|
||||
typed_event<SenderT, ArgsT>() = default;
|
||||
winrt::event_token operator()(const winrt::Windows::Foundation::TypedEventHandler<SenderT, ArgsT>& handler) { return _handlers.add(handler); }
|
||||
void operator()(const winrt::event_token& token) { _handlers.remove(token); }
|
||||
operator bool() const noexcept { return bool(_handlers); }
|
||||
template<typename... Arg>
|
||||
void raise(Arg const&... args)
|
||||
{
|
||||
_handlers(std::forward<decltype(args)>(args)...);
|
||||
}
|
||||
winrt::event<winrt::Windows::Foundation::TypedEventHandler<SenderT, ArgsT>> _handlers;
|
||||
};
|
||||
#endif
|
||||
#ifdef WINRT_Windows_UI_Xaml_Data_H
|
||||
|
||||
using property_changed_event = til::event<winrt::Windows::UI::Xaml::Data::PropertyChangedEventHandler>;
|
||||
// Making a til::observable_property unfortunately doesn't seem feasible.
|
||||
// It's gonna just result in more macros, which no one wants.
|
||||
//
|
||||
// 1. We don't know who the sender is, or would require `this` to always be
|
||||
// the first parameter to one of these observable_property's.
|
||||
//
|
||||
// 2. We don't know what our own name is. We need to actually raise an event
|
||||
// with the name of the variable as the parameter. Only way to do that is
|
||||
// with something like
|
||||
//
|
||||
// til::observable<int> Foo(this, L"Foo", 42)
|
||||
//
|
||||
// which then implies the creation of:
|
||||
//
|
||||
// #define OBSERVABLE(type, name, ...) til::observable_property<type> name{ this, L## #name, this.PropertyChanged, __VA_ARGS__ };
|
||||
//
|
||||
// Which is just silly
|
||||
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#include "winres.h"
|
||||
|
||||
#include "../../../../common/version/version.h"
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION FILE_VERSION
|
||||
PRODUCTVERSION PRODUCT_VERSION
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x40004L
|
||||
FILETYPE 0x0L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Microsoft Corporation"
|
||||
VALUE "FileDescription", "Microsoft.CommandPalette.Extensions.Toolkit"
|
||||
VALUE "FileVersion", FILE_VERSION_STRING
|
||||
VALUE "ProductName", "Microsoft.CommandPalette.Extensions.Toolkit"
|
||||
VALUE "ProductVersion", PRODUCT_VERSION_STRING
|
||||
VALUE "LegalCopyright", "Copyright (c) Microsoft Corporation"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
||||
@@ -0,0 +1,58 @@
|
||||
// TYPICALLY: This file is generated by C++/WinRT (this one was originally from
|
||||
// v2.0.240111.5), and it's included in the dll by way of "Generated
|
||||
// Files\module.g.cpp". However, our project doesn't have any activatable WinRT
|
||||
// `runtimeclass`es in it. But we need something to appease WinRT, so this is
|
||||
// the empty version of this file, without any classes in it.
|
||||
//
|
||||
// Should you need to add a WinRT runtime class to this project, think very
|
||||
// carefully about it first. If that's the only solution, then just delete this
|
||||
// file.
|
||||
|
||||
#include "pch.h"
|
||||
#include "winrt/base.h"
|
||||
|
||||
bool __stdcall winrt_can_unload_now() noexcept
|
||||
{
|
||||
if (winrt::get_module_lock())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
winrt::clear_factory_cache();
|
||||
return true;
|
||||
}
|
||||
|
||||
void* __stdcall winrt_get_activation_factory([[maybe_unused]] std::wstring_view const& name)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int32_t __stdcall WINRT_CanUnloadNow() noexcept
|
||||
{
|
||||
#ifdef _WRL_MODULE_H_
|
||||
if (!::Microsoft::WRL::Module<::Microsoft::WRL::InProc>::GetModule().Terminate())
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return winrt_can_unload_now() ? 0 : 1;
|
||||
}
|
||||
|
||||
int32_t __stdcall WINRT_GetActivationFactory(void* classId, void** factory) noexcept try
|
||||
{
|
||||
std::wstring_view const name{ *reinterpret_cast<winrt::hstring*>(&classId) };
|
||||
*factory = winrt_get_activation_factory(name);
|
||||
|
||||
if (*factory)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef _WRL_MODULE_H_
|
||||
return ::Microsoft::WRL::Module<::Microsoft::WRL::InProc>::GetModule().GetActivationFactory(static_cast<HSTRING>(classId), reinterpret_cast<::IActivationFactory**>(factory));
|
||||
#else
|
||||
return winrt::hresult_class_not_available(name).to_abi();
|
||||
#endif
|
||||
}
|
||||
catch (...) { return winrt::to_hresult(); }
|
||||
18
src/modules/cmdpal/extensionsdk/README.md
Normal file
18
src/modules/cmdpal/extensionsdk/README.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Command Palette Extension Toolkit
|
||||
|
||||
The C# toolkit for building your own extension for the Command Palette.
|
||||
|
||||
The quickest way to get started building an extension is to install the Command
|
||||
Palette, and use the "Create a new extension" command. That will set up a
|
||||
project for you, with the packaging, dependencies, and basic program structure
|
||||
ready to go.
|
||||
|
||||
To view the full docs, you can head over to [our docs site](TODO! docs link here when we have it)
|
||||
|
||||
You can also add extensions to your existing packages. For detailed docs on how,
|
||||
refer to [this docs page](TODO! add docs link)
|
||||
|
||||
There are samples of just about everything you can do in [the samples project].
|
||||
Head over there to see basic usage of the APIs.
|
||||
|
||||
[the samples project]: https://github.com/microsoft/PowerToys/tree/main/src/modules/cmdpal/Exts/SamplePagesExtension
|
||||
102
src/modules/cmdpal/extensionsdk/nuget/BuildSDKHelper.ps1
Normal file
102
src/modules/cmdpal/extensionsdk/nuget/BuildSDKHelper.ps1
Normal file
@@ -0,0 +1,102 @@
|
||||
Param(
|
||||
[string]$Configuration = "release",
|
||||
[string]$VersionOfSDK = "0.0.0",
|
||||
[string]$BuildStep = "all",
|
||||
[switch]$IsAzurePipelineBuild = $false,
|
||||
[switch]$Help = $false
|
||||
)
|
||||
|
||||
$StartTime = Get-Date
|
||||
|
||||
if ($Help) {
|
||||
Write-Host @"
|
||||
Copyright (c) Microsoft Corporation.
|
||||
Licensed under the MIT License.
|
||||
|
||||
Syntax:
|
||||
Build.cmd [options]
|
||||
|
||||
Description:
|
||||
Builds the Command Palette SDK
|
||||
|
||||
Options:
|
||||
|
||||
-Configuration <configuration>
|
||||
Only build the selected configuration(s)
|
||||
Example: -Configuration Release
|
||||
Example: -Configuration "Debug,Release"
|
||||
|
||||
-VersionOfSDK <version>
|
||||
Set the version number of the build sdk nuget package
|
||||
Example: -VersionOfSDK "1.0.0"
|
||||
|
||||
-Help
|
||||
Display this usage message.
|
||||
"@
|
||||
Exit
|
||||
}
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$buildPlatforms = "x64","arm64"
|
||||
|
||||
$msbuildPath = &"${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -prerelease -products * -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe
|
||||
if ($IsAzurePipelineBuild) {
|
||||
$nugetPath = "nuget.exe";
|
||||
} else {
|
||||
$nugetPath = (Join-Path $PSScriptRoot "NugetWrapper.cmd")
|
||||
}
|
||||
|
||||
if (($BuildStep -ieq "all") -Or ($BuildStep -ieq "build")) {
|
||||
& $nugetPath restore (Join-Path $PSScriptRoot "..\..\..\..\..\PowerToys.sln")
|
||||
|
||||
Try {
|
||||
foreach ($config in $Configuration.Split(",")) {
|
||||
foreach ($platform in $buildPlatforms) {
|
||||
$msbuildArgs = @(
|
||||
("$PSScriptRoot\..\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj"),
|
||||
("/p:Platform="+$platform),
|
||||
("/p:Configuration="+$config),
|
||||
("/binaryLogger:CmdPal.Extensions.$platform.$config.binlog"),
|
||||
("/p:VersionNumber="+$VersionOfSDK)
|
||||
)
|
||||
|
||||
& $msbuildPath $msbuildArgs
|
||||
}
|
||||
}
|
||||
} Catch {
|
||||
$formatString = "`n{0}`n`n{1}`n`n"
|
||||
$fields = $_, $_.ScriptStackTrace
|
||||
Write-Host ($formatString -f $fields) -ForegroundColor RED
|
||||
Exit 1
|
||||
}
|
||||
}
|
||||
|
||||
if (($BuildStep -ieq "all") -Or ($BuildStep -ieq "pack")) {
|
||||
foreach ($config in $Configuration.Split(",")) {
|
||||
if ($config -eq "release")
|
||||
{
|
||||
New-Item -ItemType Directory -Force -Path "$PSScriptRoot\..\_build"
|
||||
& $nugetPath pack (Join-Path $PSScriptRoot "Microsoft.CommandPalette.Extensions.SDK.nuspec") -Version $VersionOfSDK -OutputDirectory "$PSScriptRoot\..\_build"
|
||||
} else {
|
||||
Write-Host @"
|
||||
WARNING: You are currently building as '$config' configuration.
|
||||
CmdPalSDK nuget creation only supports 'release' configuration right now.
|
||||
"@ -ForegroundColor YELLOW
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($IsAzurePipelineBuild) {
|
||||
Write-Host "##vso[task.setvariable variable=VersionOfSDK;]$VersionOfSDK"
|
||||
Write-Host "##vso[task.setvariable variable=VersionOfSDK;isOutput=true;]$VersionOfSDK"
|
||||
}
|
||||
|
||||
$TotalTime = (Get-Date)-$StartTime
|
||||
$TotalMinutes = [math]::Floor($TotalTime.TotalMinutes)
|
||||
$TotalSeconds = [math]::Ceiling($TotalTime.TotalSeconds)
|
||||
|
||||
Write-Host @"
|
||||
Total Running Time:
|
||||
$TotalMinutes minutes and $TotalSeconds seconds
|
||||
"@ -ForegroundColor CYAN
|
||||
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
|
||||
<metadata minClientVersion="2.5">
|
||||
<id>Microsoft.CommandPalette.Extensions</id>
|
||||
<version>0.0.0</version>
|
||||
<title>Command Palette Extension SDK</title>
|
||||
<authors>Microsoft</authors>
|
||||
<owners>Microsoft</owners>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<description>Command Palette SDK provides support for creating Command Palette extensions on Windows.</description>
|
||||
<releaseNotes>Release notes are available in the Power Toys repository.</releaseNotes>
|
||||
<tags>Command Palette Extension SDK</tags>
|
||||
<copyright>© Microsoft Corporation. All rights reserved.</copyright>
|
||||
<projectUrl>https://github.com/microsoft/powertoys</projectUrl>
|
||||
<readme>docs\README.md</readme>
|
||||
<dependencies>
|
||||
<group targetFramework="net8.0-windows10.0.19041.0">
|
||||
<dependency id="Microsoft.Windows.CsWinRT" version="2.2.0" />
|
||||
</group>
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
<!-- TODO : Add NOTICE.txt and LICENSE files -->
|
||||
<file src="Microsoft.CommandPalette.Extensions.props" target="build\"/>
|
||||
<file src="Microsoft.CommandPalette.Extensions.targets" target="build\"/>
|
||||
<!-- AnyCPU Managed dlls from SDK.Lib project -->
|
||||
<file src="..\Microsoft.CommandPalette.Extensions.Toolkit\x64\release\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.dll" target="lib\net8.0-windows10.0.19041.0\"/>
|
||||
<file src="..\Microsoft.CommandPalette.Extensions.Toolkit\x64\release\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.deps.json" target="lib\net8.0-windows10.0.19041.0\"/>
|
||||
|
||||
<!-- Native dlls and winmd from SDK cpp project -->
|
||||
<!-- TODO: we may not need this, since there are no implementations in the Microsoft.CommandPalette.Extensions namespace -->
|
||||
<file src="..\Microsoft.CommandPalette.Extensions\x64\release\Microsoft.CommandPalette.Extensions\Microsoft.CommandPalette.Extensions.dll" target="runtimes\win-x64\native\"/>
|
||||
<file src="..\Microsoft.CommandPalette.Extensions\arm64\release\Microsoft.CommandPalette.Extensions\Microsoft.CommandPalette.Extensions.dll" target="runtimes\win-arm64\native\"/>
|
||||
|
||||
<!-- Not putting in the following the lib folder because we don't want plugin project to directly reference the winmd -->
|
||||
<file src="..\Microsoft.CommandPalette.Extensions\x64\release\Microsoft.CommandPalette.Extensions\Microsoft.CommandPalette.Extensions.winmd" target="winmd\"/>
|
||||
|
||||
<file src="..\README.md" target="docs\" />
|
||||
</files>
|
||||
</package>
|
||||
@@ -0,0 +1,5 @@
|
||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<CmdPalSDKPackageDir>$([MSBuild]::NormalizeDirectory('$(MSBuildThisFileDirectory)', '..'))</CmdPalSDKPackageDir>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,7 @@
|
||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<None Include="$(CmdPalSDKPackageDir)\winmd\*.winmd" Visible="false">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
13
src/modules/cmdpal/extensionsdk/nuget/NugetWrapper.cmd
Normal file
13
src/modules/cmdpal/extensionsdk/nuget/NugetWrapper.cmd
Normal file
@@ -0,0 +1,13 @@
|
||||
@echo OFF
|
||||
setlocal
|
||||
|
||||
if "%VisualStudioVersion%" == "" set VisualStudioVersion=15.0
|
||||
|
||||
if not exist %TEMP%\nuget.6.4.0.exe (
|
||||
echo Nuget.exe not found in the temp dir, downloading.
|
||||
powershell -Command "& { Invoke-WebRequest https://dist.nuget.org/win-x86-commandline/v6.4.0/nuget.exe -outfile $env:TEMP\nuget.6.4.0.exe }"
|
||||
)
|
||||
|
||||
%TEMP%\nuget.6.4.0.exe %*
|
||||
|
||||
exit /B %ERRORLEVEL%
|
||||
Reference in New Issue
Block a user