Compare commits
10 Commits
yuleng/cmd
...
leilzh/sea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2e0e4553f | ||
|
|
a641b46f57 | ||
|
|
51e9e9d46a | ||
|
|
5157ffc895 | ||
|
|
60bbf070e1 | ||
|
|
d597bd267d | ||
|
|
2623eb10f3 | ||
|
|
8e27940b77 | ||
|
|
c6750d3a62 | ||
|
|
37836c656d |
@@ -706,6 +706,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RegistryPreview.FuzzTests",
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.System", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.System\Microsoft.CmdPal.Ext.System.csproj", "{64B88F02-CD88-4ED8-9624-989A800230F9}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SemanticSearch", "src\common\SemanticSearch\SemanticSearch.csproj", "{5795CC73-53E2-27F4-3E37-21CB85E9E6A2}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|ARM64 = Debug|ARM64
|
||||
@@ -2574,14 +2576,18 @@ Global
|
||||
{64B88F02-CD88-4ED8-9624-989A800230F9}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{64B88F02-CD88-4ED8-9624-989A800230F9}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{64B88F02-CD88-4ED8-9624-989A800230F9}.Debug|x64.Build.0 = Debug|x64
|
||||
{64B88F02-CD88-4ED8-9624-989A800230F9}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{64B88F02-CD88-4ED8-9624-989A800230F9}.Debug|x86.Build.0 = Debug|x64
|
||||
{64B88F02-CD88-4ED8-9624-989A800230F9}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{64B88F02-CD88-4ED8-9624-989A800230F9}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{64B88F02-CD88-4ED8-9624-989A800230F9}.Release|x64.ActiveCfg = Release|x64
|
||||
{64B88F02-CD88-4ED8-9624-989A800230F9}.Release|x64.Build.0 = Release|x64
|
||||
{64B88F02-CD88-4ED8-9624-989A800230F9}.Release|x86.ActiveCfg = Release|x64
|
||||
{64B88F02-CD88-4ED8-9624-989A800230F9}.Release|x86.Build.0 = Release|x64
|
||||
{5795CC73-53E2-27F4-3E37-21CB85E9E6A2}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{5795CC73-53E2-27F4-3E37-21CB85E9E6A2}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{5795CC73-53E2-27F4-3E37-21CB85E9E6A2}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{5795CC73-53E2-27F4-3E37-21CB85E9E6A2}.Debug|x64.Build.0 = Debug|x64
|
||||
{5795CC73-53E2-27F4-3E37-21CB85E9E6A2}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{5795CC73-53E2-27F4-3E37-21CB85E9E6A2}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{5795CC73-53E2-27F4-3E37-21CB85E9E6A2}.Release|x64.ActiveCfg = Release|x64
|
||||
{5795CC73-53E2-27F4-3E37-21CB85E9E6A2}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -2852,6 +2858,7 @@ Global
|
||||
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A0} = {F05E590D-AD46-42BE-9C25-6A63ADD2E3EA}
|
||||
{5702B3CC-8575-48D5-83D8-15BB42269CD3} = {929C1324-22E8-4412-A9A8-80E85F3985A5}
|
||||
{64B88F02-CD88-4ED8-9624-989A800230F9} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
|
||||
{5795CC73-53E2-27F4-3E37-21CB85E9E6A2} = {1AFB6476-670D-4E80-A464-657E01DFF482}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}
|
||||
|
||||
23
src/common/SemanticSearch/EmbeddingModel.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.PowerToys.SemanticSearch
|
||||
{
|
||||
internal class EmbeddingModel : IEmbeddingModel
|
||||
{
|
||||
public float[] ComputeEmbedding(string text)
|
||||
{
|
||||
// Placeholder for the actual implementation
|
||||
return text.Select(c => (float)c % 10).Take(10).ToArray();
|
||||
}
|
||||
|
||||
public bool SupportsSearch => true;
|
||||
}
|
||||
}
|
||||
45
src/common/SemanticSearch/EmbeddingModelFactory.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.PowerToys.SemanticSearch
|
||||
{
|
||||
public static class EmbeddingModelFactory
|
||||
{
|
||||
private static IEmbeddingModel? _currentModel;
|
||||
private static string? _currentModelName;
|
||||
|
||||
public static IEmbeddingModel? GetEmbeddingModel()
|
||||
{
|
||||
CheckAndUpdateModel();
|
||||
return _currentModel;
|
||||
}
|
||||
|
||||
public static void CheckAndUpdateModel()
|
||||
{
|
||||
string configuredModelName = GetConfiguredModelName();
|
||||
if (_currentModelName == configuredModelName)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_currentModel = configuredModelName switch
|
||||
{
|
||||
"Mock" => new EmbeddingModel(),
|
||||
_ => new EmbeddingModel(),
|
||||
};
|
||||
_currentModelName = configuredModelName;
|
||||
}
|
||||
|
||||
private static string GetConfiguredModelName()
|
||||
{
|
||||
return "Mock";
|
||||
}
|
||||
}
|
||||
}
|
||||
19
src/common/SemanticSearch/IEmbeddingModel.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.PowerToys.SemanticSearch
|
||||
{
|
||||
public interface IEmbeddingModel
|
||||
{
|
||||
float[] ComputeEmbedding(string text);
|
||||
|
||||
bool SupportsSearch { get; }
|
||||
}
|
||||
}
|
||||
19
src/common/SemanticSearch/IIndexStore.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.PowerToys.SemanticSearch
|
||||
{
|
||||
public interface IIndexStore
|
||||
{
|
||||
void AddData(List<(string Text, float[] Embedding)> data);
|
||||
|
||||
List<(string Text, float[] Embedding)> GetData();
|
||||
}
|
||||
}
|
||||
19
src/common/SemanticSearch/ISemanticSearchService.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.PowerToys.SemanticSearch
|
||||
{
|
||||
public interface ISemanticSearchService
|
||||
{
|
||||
void IndexData(ModuleName moduleName, List<string> data);
|
||||
|
||||
List<string> Search(ModuleName moduleName, string query, int topK = 5, double threshold = 0.7);
|
||||
}
|
||||
}
|
||||
17
src/common/SemanticSearch/ISimilarityCalculator.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.PowerToys.SemanticSearch
|
||||
{
|
||||
public interface ISimilarityCalculator
|
||||
{
|
||||
double ComputeSimilarity(float[] vectorA, float[] vectorB);
|
||||
}
|
||||
}
|
||||
27
src/common/SemanticSearch/InMemoryIndexStore.cs
Normal file
@@ -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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.PowerToys.SemanticSearch
|
||||
{
|
||||
public class InMemoryIndexStore : IIndexStore
|
||||
{
|
||||
private readonly List<(string Text, float[] Embedding)> _indexedData = new();
|
||||
|
||||
public void AddData(List<(string Text, float[] Embedding)> data)
|
||||
{
|
||||
_indexedData.AddRange(data);
|
||||
}
|
||||
|
||||
public List<(string Text, float[] Embedding)> GetData()
|
||||
{
|
||||
return _indexedData;
|
||||
}
|
||||
}
|
||||
}
|
||||
17
src/common/SemanticSearch/ModuleName.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.PowerToys.SemanticSearch
|
||||
{
|
||||
public enum ModuleName
|
||||
{
|
||||
Settings,
|
||||
}
|
||||
}
|
||||
11
src/common/SemanticSearch/SemanticSearch.csproj
Normal file
@@ -0,0 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<!-- Look at Directory.Build.props in root for common stuff as well -->
|
||||
<Import Project="..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\..\Common.Dotnet.AotCompatibility.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>PowerToys SemanticSearch</Description>
|
||||
<AssemblyName>PowerToys.SemanticSearch</AssemblyName>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
47
src/common/SemanticSearch/SemanticSearchService.cs
Normal file
@@ -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.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.PowerToys.SemanticSearch;
|
||||
|
||||
public abstract class SemanticSearchService
|
||||
{
|
||||
private readonly IIndexStore _indexStore;
|
||||
private readonly IEmbeddingModel _embeddingModel;
|
||||
private readonly ISimilarityCalculator _similarityCalculator;
|
||||
|
||||
protected SemanticSearchService(IIndexStore indexStore, IEmbeddingModel embeddingModel, ISimilarityCalculator similarityCalculator)
|
||||
{
|
||||
_indexStore = indexStore;
|
||||
_embeddingModel = embeddingModel;
|
||||
_similarityCalculator = similarityCalculator;
|
||||
}
|
||||
|
||||
public virtual void IndexData(List<string> data)
|
||||
{
|
||||
var indexedData = data.Select(text => (text, _embeddingModel.ComputeEmbedding(text))).ToList();
|
||||
_indexStore.AddData(indexedData);
|
||||
}
|
||||
|
||||
public virtual List<string> Search(string query, int topK = 5, double threshold = 0.7)
|
||||
{
|
||||
if (!_embeddingModel.SupportsSearch)
|
||||
{
|
||||
return new List<string>();
|
||||
}
|
||||
|
||||
var queryEmbedding = _embeddingModel.ComputeEmbedding(query);
|
||||
var indexedData = _indexStore.GetData();
|
||||
|
||||
return indexedData
|
||||
.Select(entry => (entry.Text, similarity: _similarityCalculator.ComputeSimilarity(queryEmbedding, entry.Embedding)))
|
||||
.Where(entry => entry.similarity >= threshold)
|
||||
.OrderByDescending(entry => entry.similarity)
|
||||
.Take(topK)
|
||||
.Select(entry => entry.Text)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ using System.Linq;
|
||||
using System.ServiceProcess;
|
||||
using Microsoft.CmdPal.Ext.WindowsServices.Commands;
|
||||
using Microsoft.CmdPal.Ext.WindowsServices.Properties;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using Microsoft.Win32;
|
||||
using Windows.System;
|
||||
@@ -44,9 +43,14 @@ public static class ServiceHelper
|
||||
serviceList = servicesStartsWith.Concat(servicesContains);
|
||||
}
|
||||
|
||||
return serviceList.Select(s =>
|
||||
var result = serviceList.Select(s =>
|
||||
{
|
||||
var serviceResult = new ServiceResult(s);
|
||||
var serviceResult = ServiceResult.CreateServiceController(s);
|
||||
if (serviceResult == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ServiceCommand serviceCommand;
|
||||
CommandContextItem[] moreCommands;
|
||||
if (serviceResult.IsRunning)
|
||||
@@ -93,7 +97,9 @@ public static class ServiceHelper
|
||||
// ToolTipData = new ToolTipData(serviceResult.DisplayName, serviceResult.ServiceName),
|
||||
// IcoPath = icoPath,
|
||||
};
|
||||
});
|
||||
}).Where(s => s != null);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO GH #118 IPublicAPI contextAPI isn't used anymore, but we need equivalent ways to show notifications and status
|
||||
|
||||
@@ -17,7 +17,7 @@ public class ServiceResult
|
||||
|
||||
public bool IsRunning { get; }
|
||||
|
||||
public ServiceResult(ServiceController serviceController)
|
||||
private ServiceResult(ServiceController serviceController)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(serviceController);
|
||||
|
||||
@@ -26,4 +26,21 @@ public class ServiceResult
|
||||
StartMode = serviceController.StartType;
|
||||
IsRunning = serviceController.Status != ServiceControllerStatus.Stopped && serviceController.Status != ServiceControllerStatus.StopPending;
|
||||
}
|
||||
|
||||
public static ServiceResult CreateServiceController(ServiceController serviceController)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = new ServiceResult(serviceController);
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// try to log the exception in the future
|
||||
// retrieve properties from serviceController will throw exception. Such as PlatformNotSupportedException.
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// 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.Collections.Immutable;
|
||||
using System.Collections.Specialized;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.CmdPal.Ext.Apps;
|
||||
@@ -101,12 +102,7 @@ public partial class MainListPage : DynamicListPage,
|
||||
var commands = _tlcManager.TopLevelCommands;
|
||||
lock (commands)
|
||||
{
|
||||
// This gets called on a background thread, because ListViewModel
|
||||
// updates the .SearchText of all extensions on a BG thread.
|
||||
foreach (var command in commands)
|
||||
{
|
||||
command.TryUpdateFallbackText(newSearch);
|
||||
}
|
||||
UpdateFallbacks(newSearch, commands.ToImmutableArray());
|
||||
|
||||
// Cleared out the filter text? easy. Reset _filteredItems, and bail out.
|
||||
if (string.IsNullOrEmpty(newSearch))
|
||||
@@ -137,6 +133,26 @@ public partial class MainListPage : DynamicListPage,
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateFallbacks(string newSearch, IReadOnlyList<TopLevelViewModel> commands)
|
||||
{
|
||||
// fire and forget
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
var needsToUpdate = false;
|
||||
|
||||
foreach (var command in commands)
|
||||
{
|
||||
var changedVisibility = command.SafeUpdateFallbackTextSynchronous(newSearch);
|
||||
needsToUpdate = needsToUpdate || changedVisibility;
|
||||
}
|
||||
|
||||
if (needsToUpdate)
|
||||
{
|
||||
RaiseItemsChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private bool ActuallyLoading()
|
||||
{
|
||||
var tlcManager = _serviceProvider.GetService<TopLevelCommandManager>()!;
|
||||
@@ -149,17 +165,14 @@ public partial class MainListPage : DynamicListPage,
|
||||
// _always_ show up first.
|
||||
private int ScoreTopLevelItem(string query, IListItem topLevelOrAppItem)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(query))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
var title = topLevelOrAppItem.Title;
|
||||
if (string.IsNullOrEmpty(title))
|
||||
if (string.IsNullOrWhiteSpace(title))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var isWhiteSpace = string.IsNullOrWhiteSpace(query);
|
||||
|
||||
var isFallback = false;
|
||||
var isAliasSubstringMatch = false;
|
||||
var isAliasMatch = false;
|
||||
@@ -179,17 +192,45 @@ public partial class MainListPage : DynamicListPage,
|
||||
extensionDisplayName = topLevel.ExtensionHost?.Extension?.PackageDisplayName ?? string.Empty;
|
||||
}
|
||||
|
||||
var nameMatch = StringMatcher.FuzzySearch(query, title);
|
||||
var descriptionMatch = StringMatcher.FuzzySearch(query, topLevelOrAppItem.Subtitle);
|
||||
var extensionTitleMatch = StringMatcher.FuzzySearch(query, extensionDisplayName);
|
||||
// StringMatcher.FuzzySearch will absolutely BEEF IT if you give it a
|
||||
// whitespace-only query.
|
||||
//
|
||||
// in that scenario, we'll just use a simple string contains for the
|
||||
// query. Maybe someone is really looking for things with a space in
|
||||
// them, I don't know.
|
||||
|
||||
// Title:
|
||||
// * whitespace query: 1 point
|
||||
// * otherwise full weight match
|
||||
var nameMatch = isWhiteSpace ?
|
||||
(title.Contains(query) ? 1 : 0) :
|
||||
StringMatcher.FuzzySearch(query, title).Score;
|
||||
|
||||
// Subtitle:
|
||||
// * whitespace query: 1/2 point
|
||||
// * otherwise ~half weight match. Minus a bit, because subtitles tend to be longer
|
||||
var descriptionMatch = isWhiteSpace ?
|
||||
(topLevelOrAppItem.Subtitle.Contains(query) ? .5 : 0) :
|
||||
(StringMatcher.FuzzySearch(query, topLevelOrAppItem.Subtitle).Score - 4) / 2.0;
|
||||
|
||||
// Extension title: despite not being visible, give the extension name itself some weight
|
||||
// * whitespace query: 0 points
|
||||
// * otherwise more weight than a subtitle, but not much
|
||||
var extensionTitleMatch = isWhiteSpace ? 0 : StringMatcher.FuzzySearch(query, extensionDisplayName).Score / 1.5;
|
||||
|
||||
var scores = new[]
|
||||
{
|
||||
nameMatch.Score,
|
||||
(descriptionMatch.Score - 4) / 2.0,
|
||||
nameMatch,
|
||||
descriptionMatch,
|
||||
isFallback ? 1 : 0, // Always give fallbacks a chance...
|
||||
};
|
||||
var max = scores.Max();
|
||||
max = max + (extensionTitleMatch.Score / 1.5);
|
||||
|
||||
// _Add_ the extension name. This will bubble items that match both
|
||||
// title and extension name up above ones that just match title.
|
||||
// e.g. "git" will up-weight "GitHub searches" from the GitHub extension
|
||||
// above "git" from "whatever"
|
||||
max = max + extensionTitleMatch;
|
||||
|
||||
// ... but downweight them
|
||||
var matchSomething = (max / (isFallback ? 3 : 1))
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// 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.Collections.Immutable;
|
||||
using System.Collections.ObjectModel;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
@@ -209,7 +210,7 @@ public partial class TopLevelCommandManager : ObservableObject,
|
||||
extensionService.OnExtensionAdded -= ExtensionService_OnExtensionAdded;
|
||||
extensionService.OnExtensionRemoved -= ExtensionService_OnExtensionRemoved;
|
||||
|
||||
var extensions = await extensionService.GetInstalledExtensionsAsync();
|
||||
var extensions = (await extensionService.GetInstalledExtensionsAsync()).ToImmutableList();
|
||||
_extensionCommandProviders.Clear();
|
||||
if (extensions != null)
|
||||
{
|
||||
@@ -241,6 +242,7 @@ public partial class TopLevelCommandManager : ObservableObject,
|
||||
// TODO This most definitely needs a lock
|
||||
foreach (var extension in extensions)
|
||||
{
|
||||
Logger.LogDebug($"Starting {extension.PackageFullName}");
|
||||
try
|
||||
{
|
||||
// start it ...
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
using System.Collections.ObjectModel;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Settings;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
@@ -208,12 +207,11 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem
|
||||
tags.Add(new Tag() { Text = Alias.SearchPrefix });
|
||||
}
|
||||
|
||||
PropChanged?.Invoke(this, new PropChangedEventArgs(nameof(Tags)));
|
||||
|
||||
DoOnUiThread(
|
||||
() =>
|
||||
{
|
||||
ListHelpers.InPlaceUpdateList(Tags, tags);
|
||||
PropChanged?.Invoke(this, new PropChangedEventArgs(nameof(Tags)));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -237,32 +235,46 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem
|
||||
}
|
||||
}
|
||||
|
||||
public void TryUpdateFallbackText(string newQuery)
|
||||
internal bool SafeUpdateFallbackTextSynchronous(string newQuery)
|
||||
{
|
||||
if (!IsFallback)
|
||||
{
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
_ = Task.Run(() =>
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
var model = _commandItemViewModel.Model.Unsafe;
|
||||
if (model is IFallbackCommandItem fallback)
|
||||
{
|
||||
var wasEmpty = string.IsNullOrEmpty(Title);
|
||||
fallback.FallbackHandler.UpdateQuery(newQuery);
|
||||
var isEmpty = string.IsNullOrEmpty(Title);
|
||||
if (wasEmpty != isEmpty)
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send<UpdateFallbackItemsMessage>();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
});
|
||||
return UnsafeUpdateFallbackSynchronous(newQuery);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex.ToString());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls UpdateQuery on our command, if we're a fallback item. This does
|
||||
/// RPC work, so make sure you're calling it on a BG thread.
|
||||
/// </summary>
|
||||
/// <param name="newQuery">The new search text to pass to the extension</param>
|
||||
/// <returns>true if our Title changed across this call</returns>
|
||||
private bool UnsafeUpdateFallbackSynchronous(string newQuery)
|
||||
{
|
||||
var model = _commandItemViewModel.Model.Unsafe;
|
||||
|
||||
// RPC to check type
|
||||
if (model is IFallbackCommandItem fallback)
|
||||
{
|
||||
var wasEmpty = string.IsNullOrEmpty(Title);
|
||||
|
||||
// RPC for method
|
||||
fallback.FallbackHandler.UpdateQuery(newQuery);
|
||||
var isEmpty = string.IsNullOrEmpty(Title);
|
||||
return wasEmpty != isEmpty;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 255 B After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 295 B After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 328 B After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 435 B After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 826 B After Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 162 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 178 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 723 B After Width: | Height: | Size: 660 B |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 723 B After Width: | Height: | Size: 660 B |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 520 B After Width: | Height: | Size: 660 B |
|
After Width: | Height: | Size: 863 B |
|
Before Width: | Height: | Size: 864 B After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 52 KiB |
31
src/modules/cmdpal/Microsoft.CmdPal.UI/Assets/Dev/icon.svg
Normal file
@@ -0,0 +1,31 @@
|
||||
<svg width="96" height="96" viewBox="0 0 96 96" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_4303_1230)">
|
||||
<g clip-path="url(#clip1_4303_1230)">
|
||||
<path d="M68.6048 46.5117C69.1572 47.4784 69.1288 48.6715 68.5311 49.6108L47.5311 82.6108C47.1611 83.1921 46.6024 83.6283 45.9488 83.8462L9.94876 95.8462C8.84743 96.2133 7.63315 95.9121 6.83113 95.0728C6.0291 94.2334 5.7833 93.0067 6.20004 91.9232L18.4781 60.0001L42.7686 49.9034L67.059 43.8066L68.6048 46.5117Z" fill="url(#paint0_linear_4303_1230)"/>
|
||||
<path d="M56.8013 0.488769C57.5681 0.989947 58.0639 1.81404 58.1475 2.72632L60.9281 33.0774L67.059 43.8065L18.4781 60H3.00002C2.01162 60 1.08665 59.5132 0.527081 58.6984C-0.0324852 57.8837 -0.154834 56.8456 0.199979 55.9231L16.3538 13.9231C16.6756 13.0863 17.3548 12.4374 18.2053 12.1539L54.2115 0.153915C55.0806 -0.135737 56.0344 -0.0124095 56.8013 0.488769Z" fill="url(#paint1_linear_4303_1230)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M52.3538 1.92306C52.7994 0.764487 53.9125 0 55.1539 0H75C75.9884 0 76.9134 0.486845 77.4729 1.30159C78.0325 2.11634 78.1549 3.15442 77.8001 4.07694L67.0604 32H93C94.225 32 95.327 32.7448 95.7837 33.8815C96.2404 35.0182 95.9602 36.3184 95.0757 37.166L47.0757 83.166C46.0614 84.138 44.5084 84.276 43.3386 83.4979C42.1688 82.7199 41.6957 81.2343 42.2 79.9231L54.4781 48H39C38.0116 48 37.0866 47.5132 36.5271 46.6984C35.9675 45.8837 35.8452 44.8456 36.2 43.9231L52.3538 1.92306Z" fill="url(#paint2_radial_4303_1230)"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_4303_1230" x1="29.3743" y1="43.8066" x2="44.9485" y2="96.2016" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#7A41DC"/>
|
||||
<stop offset="0.75" stop-color="#A931D8"/>
|
||||
<stop offset="0.841927" stop-color="#7A41DC"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_4303_1230" x1="24.8802" y1="3.90205e-07" x2="43.9581" y2="59.4288" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#7A41DC"/>
|
||||
<stop offset="0.75" stop-color="#A931D8"/>
|
||||
<stop offset="0.841927" stop-color="#7A41DC"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="paint2_radial_4303_1230" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(66.0001) rotate(79.3315) scale(69.4504 51.0523)">
|
||||
<stop stop-color="#FAB500"/>
|
||||
<stop offset="1" stop-color="#FE8401"/>
|
||||
</radialGradient>
|
||||
<clipPath id="clip0_4303_1230">
|
||||
<rect width="96" height="96" fill="white"/>
|
||||
</clipPath>
|
||||
<clipPath id="clip1_4303_1230">
|
||||
<rect width="96" height="96" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 165 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 8.4 KiB |
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 181 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 722 B After Width: | Height: | Size: 648 B |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 2.6 KiB |