mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-01-09 22:06:51 +01:00
Compare commits
11 Commits
dev/mjolle
...
dev/vanzue
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b95d4c5035 | ||
|
|
4e6496ce2e | ||
|
|
8f87058508 | ||
|
|
755c138723 | ||
|
|
8b066cea2e | ||
|
|
2d92ccdf3b | ||
|
|
83ea0c2f28 | ||
|
|
cb81a99c5f | ||
|
|
48a3f4fa87 | ||
|
|
6c05e44680 | ||
|
|
6505cd7a63 |
1
.github/pull_request_template.md
vendored
1
.github/pull_request_template.md
vendored
@@ -5,7 +5,6 @@
|
||||
## PR Checklist
|
||||
|
||||
- [ ] Closes: #xxx
|
||||
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved issues) -->
|
||||
- [ ] **Communication:** I've discussed this with core contributors already. If the work hasn't been agreed, this work might be rejected
|
||||
- [ ] **Tests:** Added/updated and all pass
|
||||
- [ ] **Localization:** All end-user-facing strings can be localized
|
||||
|
||||
2
.github/workflows/msstore-submissions.yml
vendored
2
.github/workflows/msstore-submissions.yml
vendored
@@ -40,7 +40,7 @@ jobs:
|
||||
echo powerToysInstallerArm64Url=$(jq -n "$powerToysSetup" | jq -r '[.[]|select(.name | contains("arm64"))][0].browser_download_url') >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Setup .NET 9.0
|
||||
uses: actions/setup-dotnet@v5
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: '9.0.x'
|
||||
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -4,3 +4,6 @@
|
||||
[submodule "deps/expected-lite"]
|
||||
path = deps/expected-lite
|
||||
url = https://github.com/martinmoene/expected-lite.git
|
||||
[submodule "deps/cziplib"]
|
||||
path = deps/cziplib
|
||||
url = https://github.com/kuba--/zip.git
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
<PropertyGroup Condition="'$(SkipCppCodeAnalysis)' == ''">
|
||||
<RunCodeAnalysis>true</RunCodeAnalysis>
|
||||
<CodeAnalysisRuleSet>$(MsbuildThisFileDirectory)\CppRuleSet.ruleset</CodeAnalysisRuleSet>
|
||||
<CAExcludePath>$(MSBuildThisFileDirectory)deps;$(MSBuildThisFileDirectory)packages;$(CAExcludePath)</CAExcludePath>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- C++ source compile-specific things for all configurations -->
|
||||
@@ -35,7 +34,7 @@
|
||||
<PreferredToolArchitecture Condition="'$(PROCESSOR_ARCHITECTURE)' == 'ARM64' or '$(PROCESSOR_ARCHITEW6432)' == 'ARM64'">arm64</PreferredToolArchitecture>
|
||||
<VcpkgEnabled>false</VcpkgEnabled>
|
||||
<ReplaceWildcardsInProjectItems>true</ReplaceWildcardsInProjectItems>
|
||||
<ExternalIncludePath>$(MSBuildThisFileDirectory)deps;$(MSBuildThisFileDirectory)packages;$(ExternalIncludePath)</ExternalIncludePath>
|
||||
<ExternalIncludePath>$(MSBuildThisFileFullPath)\..\deps\;$(MSBuildThisFileFullPath)\..\packages\;$(ExternalIncludePath)</ExternalIncludePath>
|
||||
<!-- Enable control flow guard for C++ projects that don't consume any C++ files -->
|
||||
<!-- This covers the case where a .dll exports a .lib, but doesn't have any ClCompile entries. -->
|
||||
<LinkControlFlowGuard>Guard</LinkControlFlowGuard>
|
||||
|
||||
1
deps/cziplib
vendored
Submodule
1
deps/cziplib
vendored
Submodule
Submodule deps/cziplib added at 81314fff0a
@@ -4,7 +4,6 @@
|
||||
#include <ProjectTelemetry.h>
|
||||
#include <spdlog/sinks/base_sink.h>
|
||||
#include <filesystem>
|
||||
#include <string_view>
|
||||
|
||||
#include "../../src/common/logger/logger.h"
|
||||
#include "../../src/common/utils/gpo.h"
|
||||
@@ -857,69 +856,14 @@ UINT __stdcall UnsetAdvancedPasteAPIKeyCA(MSIHANDLE hInstall)
|
||||
|
||||
try
|
||||
{
|
||||
winrt::Windows::Security::Credentials::PasswordVault vault;
|
||||
winrt::Windows::Security::Credentials::PasswordCredential cred;
|
||||
|
||||
hr = WcaInitialize(hInstall, "UnsetAdvancedPasteAPIKey");
|
||||
ExitOnFailure(hr, "Failed to initialize");
|
||||
|
||||
winrt::Windows::Security::Credentials::PasswordVault vault;
|
||||
|
||||
auto hasPrefix = [](std::wstring_view value, wchar_t const* prefix) {
|
||||
std::wstring_view prefixView{ prefix };
|
||||
return value.compare(0, prefixView.size(), prefixView) == 0;
|
||||
};
|
||||
|
||||
const wchar_t* resourcePrefixes[] = {
|
||||
L"https://platform.openai.com/api-keys",
|
||||
L"https://azure.microsoft.com/products/ai-services/openai-service",
|
||||
L"https://azure.microsoft.com/products/ai-services/ai-inference",
|
||||
L"https://console.mistral.ai/account/api-keys",
|
||||
L"https://ai.google.dev/",
|
||||
};
|
||||
|
||||
const wchar_t* usernamePrefixes[] = {
|
||||
L"PowerToys_AdvancedPaste_",
|
||||
};
|
||||
|
||||
auto credentials = vault.RetrieveAll();
|
||||
for (auto const& credential : credentials)
|
||||
{
|
||||
bool shouldRemove = false;
|
||||
|
||||
std::wstring resource{ credential.Resource() };
|
||||
for (auto const prefix : resourcePrefixes)
|
||||
{
|
||||
if (hasPrefix(resource, prefix))
|
||||
{
|
||||
shouldRemove = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!shouldRemove)
|
||||
{
|
||||
std::wstring username{ credential.UserName() };
|
||||
for (auto const prefix : usernamePrefixes)
|
||||
{
|
||||
if (hasPrefix(username, prefix))
|
||||
{
|
||||
shouldRemove = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!shouldRemove)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
vault.Remove(credential);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
cred = vault.Retrieve(L"https://platform.openai.com/api-keys", L"PowerToys_AdvancedPaste_OpenAIKey");
|
||||
vault.Remove(cred);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
|
||||
@@ -168,15 +168,6 @@ namespace AdvancedPaste.Settings
|
||||
}
|
||||
|
||||
var properties = settings.Properties;
|
||||
bool legacyAdvancedAIConsumed = properties.TryConsumeLegacyAdvancedAIEnabled(out var advancedFlag);
|
||||
bool legacyAdvancedAIEnabled = legacyAdvancedAIConsumed && advancedFlag;
|
||||
PasswordCredential legacyCredential = TryGetLegacyOpenAICredential();
|
||||
|
||||
if (legacyCredential is null)
|
||||
{
|
||||
return legacyAdvancedAIConsumed;
|
||||
}
|
||||
|
||||
var configuration = properties.PasteAIConfiguration;
|
||||
|
||||
if (configuration is null)
|
||||
@@ -185,11 +176,30 @@ namespace AdvancedPaste.Settings
|
||||
properties.PasteAIConfiguration = configuration;
|
||||
}
|
||||
|
||||
bool hasLegacyProviders = configuration.LegacyProviderConfigurations is { Count: > 0 };
|
||||
bool legacyAdvancedAIConsumed = properties.TryConsumeLegacyAdvancedAIEnabled(out var advancedFlag);
|
||||
bool legacyAdvancedAIEnabled = legacyAdvancedAIConsumed && advancedFlag;
|
||||
PasswordCredential legacyCredential = TryGetLegacyOpenAICredential();
|
||||
|
||||
if (!hasLegacyProviders && legacyCredential is null && !legacyAdvancedAIConsumed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool configurationUpdated = false;
|
||||
|
||||
var ensureResult = AdvancedPasteMigrationHelper.EnsureOpenAIProvider(configuration);
|
||||
PasteAIProviderDefinition openAIProvider = ensureResult.Provider;
|
||||
configurationUpdated |= ensureResult.Updated;
|
||||
if (hasLegacyProviders)
|
||||
{
|
||||
configurationUpdated |= AdvancedPasteMigrationHelper.MigrateLegacyProviderConfigurations(configuration);
|
||||
}
|
||||
|
||||
PasteAIProviderDefinition openAIProvider = null;
|
||||
if (legacyCredential is not null || hasLegacyProviders || legacyAdvancedAIConsumed)
|
||||
{
|
||||
var ensureResult = AdvancedPasteMigrationHelper.EnsureOpenAIProvider(configuration);
|
||||
openAIProvider = ensureResult.Provider;
|
||||
configurationUpdated |= ensureResult.Updated;
|
||||
}
|
||||
|
||||
if (legacyAdvancedAIConsumed && openAIProvider is not null && openAIProvider.EnableAdvancedAI != legacyAdvancedAIEnabled)
|
||||
{
|
||||
@@ -197,13 +207,13 @@ namespace AdvancedPaste.Settings
|
||||
configurationUpdated = true;
|
||||
}
|
||||
|
||||
if (openAIProvider is not null)
|
||||
if (legacyCredential is not null && openAIProvider is not null)
|
||||
{
|
||||
StoreMigratedOpenAICredential(openAIProvider.Id, openAIProvider.ServiceType, legacyCredential.Password);
|
||||
RemoveLegacyOpenAICredential();
|
||||
}
|
||||
|
||||
const bool shouldEnableAI = true;
|
||||
bool shouldEnableAI = legacyCredential is not null;
|
||||
bool enabledUpdated = false;
|
||||
if (properties.IsAIEnabled != shouldEnableAI)
|
||||
{
|
||||
|
||||
@@ -16,6 +16,7 @@ using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Properties;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels.MainPage;
|
||||
|
||||
@@ -37,10 +38,8 @@ public partial class MainListPage : DynamicListPage,
|
||||
"com.microsoft.cmdpal.builtin.datetime",
|
||||
];
|
||||
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly TopLevelCommandManager _tlcManager;
|
||||
private readonly SettingsModel _settings;
|
||||
private readonly AliasManager _aliasManager;
|
||||
private readonly AppStateModel _appState;
|
||||
private List<Scored<IListItem>>? _filteredItems;
|
||||
private List<Scored<IListItem>>? _filteredApps;
|
||||
private List<Scored<IListItem>>? _fallbackItems;
|
||||
@@ -54,18 +53,17 @@ public partial class MainListPage : DynamicListPage,
|
||||
|
||||
private CancellationTokenSource? _cancellationTokenSource;
|
||||
|
||||
public MainListPage(TopLevelCommandManager topLevelCommandManager, SettingsModel settingsModel, AliasManager aliasManager, AppStateModel appStateModel)
|
||||
public MainListPage(IServiceProvider serviceProvider)
|
||||
{
|
||||
Title = Resources.builtin_home_name;
|
||||
Icon = IconHelpers.FromRelativePath("Assets\\StoreLogo.scale-200.png");
|
||||
PlaceholderText = Properties.Resources.builtin_main_list_page_searchbar_placeholder;
|
||||
_serviceProvider = serviceProvider;
|
||||
|
||||
_tlcManager = topLevelCommandManager;
|
||||
_tlcManager = _serviceProvider.GetService<TopLevelCommandManager>()!;
|
||||
_tlcManager.PropertyChanged += TlcManager_PropertyChanged;
|
||||
_tlcManager.TopLevelCommands.CollectionChanged += Commands_CollectionChanged;
|
||||
|
||||
_appState = appStateModel;
|
||||
|
||||
// The all apps page will kick off a BG thread to start loading apps.
|
||||
// We just want to know when it is done.
|
||||
var allApps = AllAppsCommandProvider.Page;
|
||||
@@ -80,13 +78,12 @@ public partial class MainListPage : DynamicListPage,
|
||||
WeakReferenceMessenger.Default.Register<ClearSearchMessage>(this);
|
||||
WeakReferenceMessenger.Default.Register<UpdateFallbackItemsMessage>(this);
|
||||
|
||||
_settings = settingsModel;
|
||||
_settings.SettingsChanged += SettingsChangedHandler;
|
||||
HotReloadSettings(_settings);
|
||||
var settings = _serviceProvider.GetService<SettingsModel>()!;
|
||||
settings.SettingsChanged += SettingsChangedHandler;
|
||||
HotReloadSettings(settings);
|
||||
_includeApps = _tlcManager.IsProviderActive(AllAppsCommandProvider.WellKnownId);
|
||||
|
||||
IsLoading = true;
|
||||
_aliasManager = aliasManager;
|
||||
}
|
||||
|
||||
private void TlcManager_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
@@ -222,12 +219,14 @@ public partial class MainListPage : DynamicListPage,
|
||||
// Handle changes to the filter text here
|
||||
if (!string.IsNullOrEmpty(SearchText))
|
||||
{
|
||||
var aliases = _serviceProvider.GetService<AliasManager>()!;
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_aliasManager.CheckAlias(newSearch))
|
||||
if (aliases.CheckAlias(newSearch))
|
||||
{
|
||||
if (_filteredItemsIncludesApps != _includeApps)
|
||||
{
|
||||
@@ -389,7 +388,7 @@ public partial class MainListPage : DynamicListPage,
|
||||
}
|
||||
}
|
||||
|
||||
var history = _appState.RecentCommands!;
|
||||
var history = _serviceProvider.GetService<AppStateModel>()!.RecentCommands!;
|
||||
Func<string, IListItem, int> scoreItem = (a, b) => { return ScoreTopLevelItem(a, b, history); };
|
||||
|
||||
// Produce a list of everything that matches the current filter.
|
||||
@@ -484,8 +483,9 @@ public partial class MainListPage : DynamicListPage,
|
||||
|
||||
private bool ActuallyLoading()
|
||||
{
|
||||
var tlcManager = _serviceProvider.GetService<TopLevelCommandManager>()!;
|
||||
var allApps = AllAppsCommandProvider.Page;
|
||||
return allApps.IsLoading || _tlcManager.IsLoading;
|
||||
return allApps.IsLoading || tlcManager.IsLoading;
|
||||
}
|
||||
|
||||
// Almost verbatim ListHelpers.ScoreListItem, but also accounting for the
|
||||
@@ -580,9 +580,10 @@ public partial class MainListPage : DynamicListPage,
|
||||
public void UpdateHistory(IListItem topLevelOrAppItem)
|
||||
{
|
||||
var id = IdForTopLevelOrAppItem(topLevelOrAppItem);
|
||||
var history = _appState.RecentCommands;
|
||||
var state = _serviceProvider.GetService<AppStateModel>()!;
|
||||
var history = state.RecentCommands;
|
||||
history.AddHistoryItem(id);
|
||||
AppStateModel.SaveState(_appState);
|
||||
AppStateModel.SaveState(state);
|
||||
}
|
||||
|
||||
private static string IdForTopLevelOrAppItem(IListItem topLevelOrAppItem)
|
||||
@@ -614,9 +615,10 @@ public partial class MainListPage : DynamicListPage,
|
||||
_tlcManager.PropertyChanged -= TlcManager_PropertyChanged;
|
||||
_tlcManager.TopLevelCommands.CollectionChanged -= Commands_CollectionChanged;
|
||||
|
||||
if (_settings is not null)
|
||||
var settings = _serviceProvider.GetService<SettingsModel>();
|
||||
if (settings is not null)
|
||||
{
|
||||
_settings.SettingsChanged -= SettingsChangedHandler;
|
||||
settings.SettingsChanged -= SettingsChangedHandler;
|
||||
}
|
||||
|
||||
WeakReferenceMessenger.Default.UnregisterAll(this);
|
||||
|
||||
@@ -9,14 +9,16 @@ using Microsoft.CmdPal.Core.Common.Services;
|
||||
using Microsoft.CmdPal.Core.ViewModels;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Properties;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
|
||||
public partial class ProviderSettingsViewModel(
|
||||
CommandProviderWrapper _provider,
|
||||
ProviderSettings _providerSettings,
|
||||
SettingsModel _settings) : ObservableObject
|
||||
IServiceProvider _serviceProvider) : ObservableObject
|
||||
{
|
||||
private readonly SettingsModel _settings = _serviceProvider.GetService<SettingsModel>()!;
|
||||
private readonly Lock _initializeSettingsLock = new();
|
||||
private Task? _initializeSettingsTask;
|
||||
|
||||
|
||||
@@ -5,13 +5,14 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Settings;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
|
||||
public partial class SettingsViewModel : INotifyPropertyChanged
|
||||
{
|
||||
private readonly SettingsModel _settings;
|
||||
private readonly TopLevelCommandManager _tlcManager;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
@@ -141,10 +142,10 @@ public partial class SettingsViewModel : INotifyPropertyChanged
|
||||
|
||||
public SettingsExtensionsViewModel Extensions { get; }
|
||||
|
||||
public SettingsViewModel(SettingsModel settings, TaskScheduler scheduler, TopLevelCommandManager topLevelCommandManager)
|
||||
public SettingsViewModel(SettingsModel settings, IServiceProvider serviceProvider, TaskScheduler scheduler)
|
||||
{
|
||||
_settings = settings;
|
||||
_tlcManager = topLevelCommandManager;
|
||||
_serviceProvider = serviceProvider;
|
||||
|
||||
var activeProviders = GetCommandProviders();
|
||||
var allProviderSettings = _settings.ProviderSettings;
|
||||
@@ -153,7 +154,7 @@ public partial class SettingsViewModel : INotifyPropertyChanged
|
||||
{
|
||||
var providerSettings = settings.GetProviderSettings(item);
|
||||
|
||||
var settingsModel = new ProviderSettingsViewModel(item, providerSettings, _settings);
|
||||
var settingsModel = new ProviderSettingsViewModel(item, providerSettings, _serviceProvider);
|
||||
CommandProviders.Add(settingsModel);
|
||||
}
|
||||
|
||||
@@ -162,7 +163,8 @@ public partial class SettingsViewModel : INotifyPropertyChanged
|
||||
|
||||
private IEnumerable<CommandProviderWrapper> GetCommandProviders()
|
||||
{
|
||||
var allProviders = _tlcManager.CommandProviders;
|
||||
var manager = _serviceProvider.GetService<TopLevelCommandManager>()!;
|
||||
var allProviders = manager.CommandProviders;
|
||||
return allProviders;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,10 +34,10 @@ public partial class TopLevelCommandManager : ObservableObject,
|
||||
|
||||
TaskScheduler IPageContext.Scheduler => _taskScheduler;
|
||||
|
||||
public TopLevelCommandManager(IServiceProvider serviceProvider, TaskScheduler taskScheduler)
|
||||
public TopLevelCommandManager(IServiceProvider serviceProvider)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_taskScheduler = taskScheduler;
|
||||
_taskScheduler = _serviceProvider.GetService<TaskScheduler>()!;
|
||||
WeakReferenceMessenger.Default.Register<ReloadCommandsMessage>(this);
|
||||
_reloadCommandsGate = new(ReloadAllCommandsAsyncCore);
|
||||
}
|
||||
|
||||
@@ -23,8 +23,6 @@ using Microsoft.CmdPal.Ext.WindowsTerminal;
|
||||
using Microsoft.CmdPal.Ext.WindowWalker;
|
||||
using Microsoft.CmdPal.Ext.WinGet;
|
||||
using Microsoft.CmdPal.UI.Helpers;
|
||||
using Microsoft.CmdPal.UI.Pages;
|
||||
using Microsoft.CmdPal.UI.Settings;
|
||||
using Microsoft.CmdPal.UI.ViewModels;
|
||||
using Microsoft.CmdPal.UI.ViewModels.BuiltinCommands;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Models;
|
||||
@@ -53,7 +51,10 @@ public partial class App : Application
|
||||
|
||||
public ETWTrace EtwTrace { get; private set; } = new ETWTrace();
|
||||
|
||||
private readonly ServiceProvider _services;
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IServiceProvider"/> instance to resolve application services.
|
||||
/// </summary>
|
||||
public IServiceProvider Services { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="App"/> class.
|
||||
@@ -66,7 +67,7 @@ public partial class App : Application
|
||||
_globalErrorHandler.Register(this);
|
||||
#endif
|
||||
|
||||
_services = ConfigureServices();
|
||||
Services = ConfigureServices();
|
||||
|
||||
this.InitializeComponent();
|
||||
|
||||
@@ -93,7 +94,7 @@ public partial class App : Application
|
||||
/// <param name="args">Details about the launch request and process.</param>
|
||||
protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
|
||||
{
|
||||
AppWindow = _services.GetRequiredService<MainWindow>();
|
||||
AppWindow = new MainWindow();
|
||||
|
||||
var activatedEventArgs = Microsoft.Windows.AppLifecycle.AppInstance.GetCurrent().GetActivatedEventArgs();
|
||||
((MainWindow)AppWindow).HandleLaunchNonUI(activatedEventArgs);
|
||||
@@ -109,11 +110,6 @@ public partial class App : Application
|
||||
|
||||
// Root services
|
||||
services.AddSingleton(TaskScheduler.FromCurrentSynchronizationContext());
|
||||
services.AddSingleton<IAppHostService, PowerToysAppHostService>();
|
||||
services.AddSingleton<ITelemetryService, TelemetryForwarder>();
|
||||
services.AddSingleton<IPageViewModelFactoryService, CommandPalettePageViewModelFactory>();
|
||||
|
||||
// TODO: Register ILogger
|
||||
|
||||
// Built-in Commands. Order matters - this is the order they'll be presented by default.
|
||||
var allApps = new AllAppsCommandProvider();
|
||||
@@ -156,35 +152,25 @@ public partial class App : Application
|
||||
services.AddSingleton<ICommandProvider, TimeDateCommandsProvider>();
|
||||
services.AddSingleton<ICommandProvider, SystemCommandExtensionProvider>();
|
||||
|
||||
// Settings & state
|
||||
// Models
|
||||
services.AddSingleton<TopLevelCommandManager>();
|
||||
services.AddSingleton<AliasManager>();
|
||||
services.AddSingleton<HotkeyManager>();
|
||||
var sm = SettingsModel.LoadSettings();
|
||||
services.AddSingleton(sm);
|
||||
var state = AppStateModel.LoadState();
|
||||
services.AddSingleton(state);
|
||||
|
||||
// Services
|
||||
services.AddSingleton<TopLevelCommandManager>();
|
||||
services.AddSingleton<AliasManager>();
|
||||
services.AddSingleton<HotkeyManager>();
|
||||
services.AddSingleton<IExtensionService, ExtensionService>();
|
||||
services.AddSingleton<TrayIconService>();
|
||||
services.AddSingleton<IRunHistoryService, RunHistoryService>();
|
||||
|
||||
// ViewModels
|
||||
services.AddSingleton<IRootPageService, PowerToysRootPageService>();
|
||||
services.AddSingleton<SettingsViewModel>();
|
||||
services.AddSingleton<IAppHostService, PowerToysAppHostService>();
|
||||
services.AddSingleton<ITelemetryService, TelemetryForwarder>();
|
||||
|
||||
// ViewModels
|
||||
services.AddSingleton<ShellViewModel>();
|
||||
|
||||
// Views
|
||||
services.AddSingleton<MainWindow>();
|
||||
services.AddSingleton<ShellPage>();
|
||||
|
||||
services.AddTransient<ListPage>();
|
||||
|
||||
// Settings Pages
|
||||
services.AddTransient<GeneralPage>();
|
||||
services.AddTransient<ExtensionPage>();
|
||||
services.AddTransient<ExtensionsPage>();
|
||||
services.AddSingleton<IPageViewModelFactoryService, CommandPalettePageViewModelFactory>();
|
||||
|
||||
return services.BuildServiceProvider();
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ using Microsoft.CmdPal.Core.ViewModels.Messages;
|
||||
using Microsoft.CmdPal.UI.Helpers;
|
||||
using Microsoft.CmdPal.UI.Messages;
|
||||
using Microsoft.CmdPal.UI.ViewModels;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||
@@ -30,7 +31,6 @@ public sealed partial class ListPage : Page,
|
||||
IRecipient<ActivateSelectedListItemMessage>,
|
||||
IRecipient<ActivateSecondaryCommandMessage>
|
||||
{
|
||||
private readonly SettingsModel _settings;
|
||||
private InputSource _lastInputSource;
|
||||
|
||||
internal ListViewModel? ViewModel
|
||||
@@ -51,11 +51,9 @@ public sealed partial class ListPage : Page,
|
||||
}
|
||||
}
|
||||
|
||||
public ListPage(SettingsModel settings)
|
||||
public ListPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
_settings = settings;
|
||||
|
||||
this.NavigationCacheMode = NavigationCacheMode.Disabled;
|
||||
this.ItemView.Loaded += Items_Loaded;
|
||||
this.ItemView.PreviewKeyDown += Items_PreviewKeyDown;
|
||||
@@ -135,7 +133,8 @@ public sealed partial class ListPage : Page,
|
||||
return;
|
||||
}
|
||||
|
||||
if (_settings.SingleClickActivates)
|
||||
var settings = App.Current.Services.GetService<SettingsModel>()!;
|
||||
if (settings.SingleClickActivates)
|
||||
{
|
||||
ViewModel?.InvokeItemCommand.Execute(item);
|
||||
}
|
||||
@@ -151,7 +150,8 @@ public sealed partial class ListPage : Page,
|
||||
{
|
||||
if (ItemView.SelectedItem is ListItemViewModel vm)
|
||||
{
|
||||
if (!_settings.SingleClickActivates)
|
||||
var settings = App.Current.Services.GetService<SettingsModel>()!;
|
||||
if (!settings.SingleClickActivates)
|
||||
{
|
||||
ViewModel?.InvokeItemCommand.Execute(vm);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Microsoft.CmdPal.UI.Helpers;
|
||||
|
||||
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "Stylistically, window messages are WM_*")]
|
||||
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1306:Field names should begin with lower-case letter", Justification = "Stylistically, window messages are WM_*")]
|
||||
public sealed partial class TrayIconService
|
||||
internal sealed partial class TrayIconService
|
||||
{
|
||||
private const uint MY_NOTIFY_ID = 1000;
|
||||
private const uint WM_TRAY_ICON = PInvoke.WM_USER + 1;
|
||||
|
||||
@@ -13,6 +13,6 @@
|
||||
MinHeight="240"
|
||||
Activated="MainWindow_Activated"
|
||||
Closed="MainWindow_Closed"
|
||||
x:Name="Window"
|
||||
mc:Ignorable="d">
|
||||
<pages:ShellPage x:Name="RootShellPage" />
|
||||
</winuiex:WindowEx>
|
||||
|
||||
@@ -14,9 +14,9 @@ using Microsoft.CmdPal.Ext.ClipboardHistory.Messages;
|
||||
using Microsoft.CmdPal.UI.Events;
|
||||
using Microsoft.CmdPal.UI.Helpers;
|
||||
using Microsoft.CmdPal.UI.Messages;
|
||||
using Microsoft.CmdPal.UI.Pages;
|
||||
using Microsoft.CmdPal.UI.ViewModels;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using Microsoft.UI.Composition;
|
||||
using Microsoft.UI.Composition.SystemBackdrops;
|
||||
@@ -58,10 +58,6 @@ public sealed partial class MainWindow : WindowEx,
|
||||
private readonly KeyboardListener _keyboardListener;
|
||||
private readonly LocalKeyboardListener _localKeyboardListener;
|
||||
private readonly HiddenOwnerWindowBehavior _hiddenOwnerBehavior = new();
|
||||
private readonly SettingsModel _settings;
|
||||
private readonly TrayIconService _trayIconService;
|
||||
private readonly IExtensionService _extensionService;
|
||||
private readonly ShellPage _rootShellPage;
|
||||
private bool _ignoreHotKeyWhenFullScreen = true;
|
||||
|
||||
private DesktopAcrylicController? _acrylicController;
|
||||
@@ -69,17 +65,10 @@ public sealed partial class MainWindow : WindowEx,
|
||||
|
||||
private WindowPosition _currentWindowPosition = new();
|
||||
|
||||
public MainWindow(SettingsModel settingsModel, TrayIconService trayIconService, IExtensionService extensionService, ShellPage shellPage)
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_settings = settingsModel;
|
||||
_trayIconService = trayIconService;
|
||||
_extensionService = extensionService;
|
||||
|
||||
_rootShellPage = shellPage;
|
||||
Window.WindowContent = _rootShellPage;
|
||||
|
||||
_hwnd = new HWND(WinRT.Interop.WindowNative.GetWindowHandle(this).ToInt32());
|
||||
|
||||
unsafe
|
||||
@@ -113,7 +102,7 @@ public sealed partial class MainWindow : WindowEx,
|
||||
ExtendsContentIntoTitleBar = true;
|
||||
AppWindow.TitleBar.PreferredHeightOption = TitleBarHeightOption.Collapsed;
|
||||
SizeChanged += WindowSizeChanged;
|
||||
_rootShellPage.Loaded += RootShellPage_Loaded;
|
||||
RootShellPage.Loaded += RootShellPage_Loaded;
|
||||
|
||||
WM_TASKBAR_RESTART = PInvoke.RegisterWindowMessage("TaskbarCreated");
|
||||
|
||||
@@ -127,10 +116,10 @@ public sealed partial class MainWindow : WindowEx,
|
||||
|
||||
// Load our settings, and then also wire up a settings changed handler
|
||||
HotReloadSettings();
|
||||
_settings.SettingsChanged += SettingsChangedHandler;
|
||||
App.Current.Services.GetService<SettingsModel>()!.SettingsChanged += SettingsChangedHandler;
|
||||
|
||||
// Make sure that we update the acrylic theme when the OS theme changes
|
||||
_rootShellPage.ActualThemeChanged += (s, e) => DispatcherQueue.TryEnqueue(UpdateAcrylic);
|
||||
RootShellPage.ActualThemeChanged += (s, e) => DispatcherQueue.TryEnqueue(UpdateAcrylic);
|
||||
|
||||
// Hardcoding event name to avoid bringing in the PowerToys.interop dependency. Event name must match CMDPAL_SHOW_EVENT from shared_constants.h
|
||||
NativeEventWaiter.WaitForEventLoop("Local\\PowerToysCmdPal-ShowEvent-62336fcd-8611-4023-9b30-091a6af4cc5a", () =>
|
||||
@@ -171,7 +160,8 @@ public sealed partial class MainWindow : WindowEx,
|
||||
|
||||
private void RestoreWindowPosition()
|
||||
{
|
||||
if (_settings.LastWindowPosition is not WindowPosition savedPosition)
|
||||
var settings = App.Current.Services.GetService<SettingsModel>();
|
||||
if (settings?.LastWindowPosition is not WindowPosition savedPosition)
|
||||
{
|
||||
PositionCentered();
|
||||
return;
|
||||
@@ -228,10 +218,12 @@ public sealed partial class MainWindow : WindowEx,
|
||||
|
||||
private void HotReloadSettings()
|
||||
{
|
||||
SetupHotkey(_settings);
|
||||
_trayIconService.SetupTrayIcon(_settings.ShowSystemTrayIcon);
|
||||
var settings = App.Current.Services.GetService<SettingsModel>()!;
|
||||
|
||||
_ignoreHotKeyWhenFullScreen = _settings.IgnoreShortcutWhenFullscreen;
|
||||
SetupHotkey(settings);
|
||||
App.Current.Services.GetService<TrayIconService>()!.SetupTrayIcon(settings.ShowSystemTrayIcon);
|
||||
|
||||
_ignoreHotKeyWhenFullScreen = settings.IgnoreShortcutWhenFullscreen;
|
||||
}
|
||||
|
||||
// We want to use DesktopAcrylicKind.Thin and custom colors as this is the default material
|
||||
@@ -386,7 +378,9 @@ public sealed partial class MainWindow : WindowEx,
|
||||
|
||||
public void Receive(ShowWindowMessage message)
|
||||
{
|
||||
ShowHwnd(message.Hwnd, _settings.SummonOn);
|
||||
var settings = App.Current.Services.GetService<SettingsModel>()!;
|
||||
|
||||
ShowHwnd(message.Hwnd, settings.SummonOn);
|
||||
}
|
||||
|
||||
public void Receive(HideWindowMessage message)
|
||||
@@ -473,11 +467,13 @@ public sealed partial class MainWindow : WindowEx,
|
||||
|
||||
internal void MainWindow_Closed(object sender, WindowEventArgs args)
|
||||
{
|
||||
var serviceProvider = App.Current.Services;
|
||||
UpdateWindowPositionInMemory();
|
||||
|
||||
if (_settings is not null)
|
||||
var settings = serviceProvider.GetService<SettingsModel>();
|
||||
if (settings is not null)
|
||||
{
|
||||
_settings.LastWindowPosition = new WindowPosition
|
||||
settings.LastWindowPosition = new WindowPosition
|
||||
{
|
||||
X = _currentWindowPosition.X,
|
||||
Y = _currentWindowPosition.Y,
|
||||
@@ -485,12 +481,13 @@ public sealed partial class MainWindow : WindowEx,
|
||||
Height = _currentWindowPosition.Height,
|
||||
};
|
||||
|
||||
SettingsModel.SaveSettings(_settings);
|
||||
SettingsModel.SaveSettings(settings);
|
||||
}
|
||||
|
||||
_extensionService.SignalStopExtensionsAsync();
|
||||
var extensionService = serviceProvider.GetService<IExtensionService>()!;
|
||||
extensionService.SignalStopExtensionsAsync();
|
||||
|
||||
_trayIconService.Destroy();
|
||||
App.Current.Services.GetService<TrayIconService>()!.Destroy();
|
||||
|
||||
// WinUI bug is causing a crash on shutdown when FailFastOnErrors is set to true (#51773592).
|
||||
// Workaround by turning it off before shutdown.
|
||||
@@ -516,28 +513,28 @@ public sealed partial class MainWindow : WindowEx,
|
||||
private void UpdateRegionsForCustomTitleBar()
|
||||
{
|
||||
// Specify the interactive regions of the title bar.
|
||||
var scaleAdjustment = _rootShellPage.XamlRoot.RasterizationScale;
|
||||
var scaleAdjustment = RootShellPage.XamlRoot.RasterizationScale;
|
||||
|
||||
// Get the rectangle around our XAML content. We're going to mark this
|
||||
// rectangle as "Passthrough", so that the normal window operations
|
||||
// (resizing, dragging) don't apply in this space.
|
||||
var transform = _rootShellPage.TransformToVisual(null);
|
||||
var transform = RootShellPage.TransformToVisual(null);
|
||||
|
||||
// Reserve 16px of space at the top for dragging.
|
||||
var topHeight = 16;
|
||||
var bounds = transform.TransformBounds(new Rect(
|
||||
0,
|
||||
topHeight,
|
||||
_rootShellPage.ActualWidth,
|
||||
_rootShellPage.ActualHeight));
|
||||
RootShellPage.ActualWidth,
|
||||
RootShellPage.ActualHeight));
|
||||
var contentRect = GetRect(bounds, scaleAdjustment);
|
||||
var rectArray = new RectInt32[] { contentRect };
|
||||
var nonClientInputSrc = InputNonClientPointerSource.GetForWindowId(this.AppWindow.Id);
|
||||
nonClientInputSrc.SetRegionRects(NonClientRegionKind.Passthrough, rectArray);
|
||||
|
||||
// Add a drag-able region on top
|
||||
var w = _rootShellPage.ActualWidth;
|
||||
_ = _rootShellPage.ActualHeight;
|
||||
var w = RootShellPage.ActualWidth;
|
||||
_ = RootShellPage.ActualHeight;
|
||||
var dragSides = new RectInt32[]
|
||||
{
|
||||
GetRect(new Rect(0, 0, w, topHeight), scaleAdjustment), // the top, {topHeight=16} tall
|
||||
@@ -627,7 +624,8 @@ public sealed partial class MainWindow : WindowEx,
|
||||
}
|
||||
else if (uri.StartsWith("x-cmdpal://reload", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (_settings.AllowExternalReload == true)
|
||||
var settings = App.Current.Services.GetService<SettingsModel>();
|
||||
if (settings?.AllowExternalReload == true)
|
||||
{
|
||||
Logger.LogInfo("External Reload triggered");
|
||||
WeakReferenceMessenger.Default.Send<ReloadCommandsMessage>(new());
|
||||
|
||||
@@ -16,10 +16,12 @@ using Microsoft.CmdPal.UI.Messages;
|
||||
using Microsoft.CmdPal.UI.Settings;
|
||||
using Microsoft.CmdPal.UI.ViewModels;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Input;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Automation.Peers;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using Microsoft.UI.Xaml.Media.Animation;
|
||||
@@ -60,9 +62,6 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
|
||||
|
||||
private readonly ToastWindow _toast = new();
|
||||
|
||||
private readonly SettingsModel _settings;
|
||||
private readonly TopLevelCommandManager tlcManager;
|
||||
|
||||
private readonly CompositeFormat _pageNavigatedAnnouncement;
|
||||
|
||||
private SettingsWindow? _settingsWindow;
|
||||
@@ -70,21 +69,14 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
|
||||
private CancellationTokenSource? _focusAfterLoadedCts;
|
||||
private WeakReference<Page>? _lastNavigatedPageRef;
|
||||
|
||||
public ShellViewModel ViewModel { get; private set; }
|
||||
public ShellViewModel ViewModel { get; private set; } = App.Current.Services.GetService<ShellViewModel>()!;
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
public ShellPage(
|
||||
ShellViewModel shellViewModel,
|
||||
SettingsModel settingsModel,
|
||||
TopLevelCommandManager topLevelCommandManager)
|
||||
public ShellPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
ViewModel = shellViewModel;
|
||||
_settings = settingsModel;
|
||||
tlcManager = topLevelCommandManager;
|
||||
|
||||
// how we are doing navigation around
|
||||
WeakReferenceMessenger.Default.Register<NavigateBackMessage>(this);
|
||||
WeakReferenceMessenger.Default.Register<OpenSettingsMessage>(this);
|
||||
@@ -120,16 +112,19 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
|
||||
{
|
||||
get
|
||||
{
|
||||
return _settings.DisableAnimations ? _noAnimation : _slideRightTransition;
|
||||
var settings = App.Current.Services.GetService<SettingsModel>()!;
|
||||
return settings.DisableAnimations ? _noAnimation : _slideRightTransition;
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(NavigateBackMessage message)
|
||||
{
|
||||
var settings = App.Current.Services.GetService<SettingsModel>()!;
|
||||
|
||||
if (RootFrame.CanGoBack)
|
||||
{
|
||||
if (!message.FromBackspace ||
|
||||
_settings.BackspaceGoesBack)
|
||||
settings.BackspaceGoesBack)
|
||||
{
|
||||
GoBack();
|
||||
}
|
||||
@@ -340,6 +335,7 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
|
||||
|
||||
private void SummonOnUiThread(HotkeySummonMessage message)
|
||||
{
|
||||
var settings = App.Current.Services.GetService<SettingsModel>()!;
|
||||
var commandId = message.CommandId;
|
||||
var isRoot = string.IsNullOrEmpty(commandId);
|
||||
if (isRoot)
|
||||
@@ -350,11 +346,11 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
|
||||
// Depending on the settings, either
|
||||
// * Go home, or
|
||||
// * Select the search text (if we should remain open on this page)
|
||||
if (_settings.HotkeyGoesHome)
|
||||
if (settings.HotkeyGoesHome)
|
||||
{
|
||||
GoHome(false);
|
||||
}
|
||||
else if (_settings.HighlightSearchOnActivate)
|
||||
else if (settings.HighlightSearchOnActivate)
|
||||
{
|
||||
SearchBox.SelectSearch();
|
||||
}
|
||||
@@ -364,7 +360,8 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
|
||||
try
|
||||
{
|
||||
// For a hotkey bound to a command, first lookup the
|
||||
// command from our list of top level commands.
|
||||
// command from our list of toplevel commands.
|
||||
var tlcManager = App.Current.Services.GetService<TopLevelCommandManager>()!;
|
||||
var topLevelCommand = tlcManager.LookupCommand(commandId);
|
||||
if (topLevelCommand is not null)
|
||||
{
|
||||
@@ -658,15 +655,15 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
|
||||
e.Handled = true;
|
||||
break;
|
||||
default:
|
||||
{
|
||||
// The CommandBar is responsible for handling all the item keybindings,
|
||||
// since the bound context item may need to then show another
|
||||
// context menu
|
||||
TryCommandKeybindingMessage msg = new(ctrlPressed, altPressed, shiftPressed, winPressed, e.Key);
|
||||
WeakReferenceMessenger.Default.Send(msg);
|
||||
e.Handled = msg.Handled;
|
||||
break;
|
||||
}
|
||||
{
|
||||
// The CommandBar is responsible for handling all the item keybindings,
|
||||
// since the bound context item may need to then show another
|
||||
// context menu
|
||||
TryCommandKeybindingMessage msg = new(ctrlPressed, altPressed, shiftPressed, winPressed, e.Key);
|
||||
WeakReferenceMessenger.Default.Send(msg);
|
||||
e.Handled = msg.Handled;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ using Microsoft.CmdPal.Core.ViewModels;
|
||||
using Microsoft.CmdPal.UI.ViewModels;
|
||||
using Microsoft.CmdPal.UI.ViewModels.MainPage;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using WinRT;
|
||||
|
||||
// To learn more about WinUI, the WinUI project structure,
|
||||
@@ -18,23 +19,24 @@ namespace Microsoft.CmdPal.UI;
|
||||
|
||||
internal sealed class PowerToysRootPageService : IRootPageService
|
||||
{
|
||||
private readonly TopLevelCommandManager _topLevelCommandManager;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private IExtensionWrapper? _activeExtension;
|
||||
private Lazy<MainListPage> _mainListPage;
|
||||
|
||||
public PowerToysRootPageService(TopLevelCommandManager topLevelCommandManager, SettingsModel settingsModel, AliasManager aliasManager, AppStateModel appStateModel)
|
||||
public PowerToysRootPageService(IServiceProvider serviceProvider)
|
||||
{
|
||||
_topLevelCommandManager = topLevelCommandManager;
|
||||
_serviceProvider = serviceProvider;
|
||||
|
||||
_mainListPage = new Lazy<MainListPage>(() =>
|
||||
{
|
||||
return new MainListPage(_topLevelCommandManager, settingsModel, aliasManager, appStateModel);
|
||||
return new MainListPage(_serviceProvider);
|
||||
});
|
||||
}
|
||||
|
||||
public async Task PreLoadAsync()
|
||||
{
|
||||
await _topLevelCommandManager.LoadBuiltinsAsync();
|
||||
var tlcManager = _serviceProvider.GetService<TopLevelCommandManager>()!;
|
||||
await tlcManager.LoadBuiltinsAsync();
|
||||
}
|
||||
|
||||
public Microsoft.CommandPalette.Extensions.IPage GetRootPage()
|
||||
@@ -44,11 +46,13 @@ internal sealed class PowerToysRootPageService : IRootPageService
|
||||
|
||||
public async Task PostLoadRootPageAsync()
|
||||
{
|
||||
// After loading built-ins, and starting navigation, kick off a thread to load extensions.
|
||||
_topLevelCommandManager.LoadExtensionsCommand.Execute(null);
|
||||
var tlcManager = _serviceProvider.GetService<TopLevelCommandManager>()!;
|
||||
|
||||
await _topLevelCommandManager.LoadExtensionsCommand.ExecutionTask!;
|
||||
if (_topLevelCommandManager.LoadExtensionsCommand.ExecutionTask.Status != TaskStatus.RanToCompletion)
|
||||
// After loading built-ins, and starting navigation, kick off a thread to load extensions.
|
||||
tlcManager.LoadExtensionsCommand.Execute(null);
|
||||
|
||||
await tlcManager.LoadExtensionsCommand.ExecutionTask!;
|
||||
if (tlcManager.LoadExtensionsCommand.ExecutionTask.Status != TaskStatus.RanToCompletion)
|
||||
{
|
||||
// TODO: Handle failure case
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ namespace Microsoft.CmdPal.UI.Settings;
|
||||
|
||||
public sealed partial class ExtensionPage : Page
|
||||
{
|
||||
private readonly TaskScheduler _mainTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
|
||||
|
||||
public ProviderSettingsViewModel? ViewModel { get; private set; }
|
||||
|
||||
public ExtensionPage()
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.WinUI.Controls;
|
||||
using Microsoft.CmdPal.UI.ViewModels;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
@@ -13,13 +14,16 @@ namespace Microsoft.CmdPal.UI.Settings;
|
||||
|
||||
public sealed partial class ExtensionsPage : Page
|
||||
{
|
||||
private readonly TaskScheduler _mainTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
|
||||
|
||||
private readonly SettingsViewModel? viewModel;
|
||||
|
||||
public ExtensionsPage(SettingsViewModel settingsViewModel)
|
||||
public ExtensionsPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
viewModel = settingsViewModel;
|
||||
var settings = App.Current.Services.GetService<SettingsModel>()!;
|
||||
viewModel = new SettingsViewModel(settings, App.Current.Services, _mainTaskScheduler);
|
||||
}
|
||||
|
||||
private void SettingsCard_Click(object sender, RoutedEventArgs e)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.CmdPal.UI.ViewModels;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Windows.ApplicationModel;
|
||||
|
||||
@@ -10,13 +11,16 @@ namespace Microsoft.CmdPal.UI.Settings;
|
||||
|
||||
public sealed partial class GeneralPage : Page
|
||||
{
|
||||
private readonly TaskScheduler _mainTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
|
||||
|
||||
private readonly SettingsViewModel? viewModel;
|
||||
|
||||
public GeneralPage(SettingsViewModel settings, TopLevelCommandManager topLevelCommandManager)
|
||||
public GeneralPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
viewModel = settings;
|
||||
var settings = App.Current.Services.GetService<SettingsModel>()!;
|
||||
viewModel = new SettingsViewModel(settings, App.Current.Services, _mainTaskScheduler);
|
||||
}
|
||||
|
||||
public string ApplicationVersion
|
||||
|
||||
@@ -15,7 +15,6 @@ namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
[JsonSerializable(typeof(List<Choice>))]
|
||||
[JsonSerializable(typeof(List<ChoiceSetSetting>))]
|
||||
[JsonSerializable(typeof(Dictionary<string, object>), TypeInfoPropertyName = "Dictionary")]
|
||||
[JsonSerializable(typeof(List<Dictionary<string, object>>))]
|
||||
[JsonSourceGenerationOptions(UseStringEnumConverter = true, WriteIndented = true)]
|
||||
internal sealed partial class JsonSerializationContext : JsonSerializerContext
|
||||
{
|
||||
|
||||
@@ -26,69 +26,15 @@ public sealed class ToggleSetting : Setting<bool>
|
||||
|
||||
public override Dictionary<string, object> ToDictionary()
|
||||
{
|
||||
var items = new List<Dictionary<string, object>>();
|
||||
|
||||
if (!string.IsNullOrEmpty(Label))
|
||||
return new Dictionary<string, object>
|
||||
{
|
||||
items.Add(
|
||||
new()
|
||||
{
|
||||
{ "type", "TextBlock" },
|
||||
{ "text", Label },
|
||||
{ "wrap", true },
|
||||
});
|
||||
}
|
||||
|
||||
if (!(string.IsNullOrEmpty(Description) || string.Equals(Description, Label, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
items.Add(
|
||||
new()
|
||||
{
|
||||
{ "type", "TextBlock" },
|
||||
{ "text", Description },
|
||||
{ "isSubtle", true },
|
||||
{ "size", "Small" },
|
||||
{ "spacing", "Small" },
|
||||
{ "wrap", true },
|
||||
});
|
||||
}
|
||||
|
||||
return new()
|
||||
{
|
||||
{ "type", "ColumnSet" },
|
||||
{
|
||||
"columns", new List<Dictionary<string, object>>
|
||||
{
|
||||
new()
|
||||
{
|
||||
{ "type", "Column" },
|
||||
{ "width", "20px" },
|
||||
{
|
||||
"items", new List<Dictionary<string, object>>
|
||||
{
|
||||
new()
|
||||
{
|
||||
{ "type", "Input.Toggle" },
|
||||
{ "title", " " },
|
||||
{ "id", Key },
|
||||
{ "value", JsonSerializer.Serialize(Value, JsonSerializationContext.Default.Boolean) },
|
||||
{ "isRequired", IsRequired },
|
||||
{ "errorMessage", ErrorMessage },
|
||||
},
|
||||
}
|
||||
},
|
||||
{ "verticalContentAlignment", "Center" },
|
||||
},
|
||||
new()
|
||||
{
|
||||
{ "type", "Column" },
|
||||
{ "width", "stretch" },
|
||||
{ "items", items },
|
||||
{ "verticalContentAlignment", "Center" },
|
||||
},
|
||||
}
|
||||
},
|
||||
{ "spacing", "Medium" },
|
||||
{ "type", "Input.Toggle" },
|
||||
{ "title", Label },
|
||||
{ "id", Key },
|
||||
{ "label", Description },
|
||||
{ "value", JsonSerializer.Serialize(Value, JsonSerializationContext.Default.Boolean) },
|
||||
{ "isRequired", IsRequired },
|
||||
{ "errorMessage", ErrorMessage },
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -161,7 +161,7 @@ int runner(bool isProcessElevated, bool openSettings, std::string settingsWindow
|
||||
L"PowerToys.MouseJump.dll",
|
||||
L"PowerToys.AlwaysOnTopModuleInterface.dll",
|
||||
L"PowerToys.MousePointerCrosshairs.dll",
|
||||
L"PowerToys.CursorWrap.dll",
|
||||
// L"PowerToys.CursorWrap.dll",
|
||||
L"PowerToys.PowerAccentModuleInterface.dll",
|
||||
L"PowerToys.PowerOCRModuleInterface.dll",
|
||||
L"PowerToys.AdvancedPasteModuleInterface.dll",
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// 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;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores provider-specific configuration overrides so each AI service can keep distinct settings.
|
||||
/// </summary>
|
||||
public class AIProviderConfigurationSnapshot
|
||||
{
|
||||
[JsonPropertyName("model-name")]
|
||||
public string ModelName { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("endpoint-url")]
|
||||
public string EndpointUrl { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("api-version")]
|
||||
public string ApiVersion { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("deployment-name")]
|
||||
public string DeploymentName { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("model-path")]
|
||||
public string ModelPath { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("system-prompt")]
|
||||
public string SystemPrompt { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("moderation-enabled")]
|
||||
public bool ModerationEnabled { get; set; } = true;
|
||||
}
|
||||
}
|
||||
@@ -13,12 +13,56 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
/// </summary>
|
||||
public static class AdvancedPasteMigrationHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Moves legacy provider configuration snapshots into the strongly-typed providers collection.
|
||||
/// </summary>
|
||||
/// <param name="configuration">The configuration instance to migrate.</param>
|
||||
/// <returns>True if the configuration was modified.</returns>
|
||||
public static bool MigrateLegacyProviderConfigurations(PasteAIConfiguration configuration)
|
||||
{
|
||||
if (configuration is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
configuration.Providers ??= new ObservableCollection<PasteAIProviderDefinition>();
|
||||
|
||||
bool configurationUpdated = false;
|
||||
|
||||
if (configuration.LegacyProviderConfigurations is { Count: > 0 })
|
||||
{
|
||||
foreach (var entry in configuration.LegacyProviderConfigurations)
|
||||
{
|
||||
var result = EnsureProvider(configuration, entry.Key, entry.Value);
|
||||
configurationUpdated |= result.Updated;
|
||||
}
|
||||
|
||||
configuration.LegacyProviderConfigurations = null;
|
||||
}
|
||||
|
||||
configurationUpdated |= EnsureActiveProviderIsValid(configuration);
|
||||
|
||||
return configurationUpdated;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures an OpenAI provider exists in the configuration, creating one if necessary.
|
||||
/// </summary>
|
||||
/// <param name="configuration">The configuration instance.</param>
|
||||
/// <returns>The ensured provider and a flag indicating whether changes were made.</returns>
|
||||
public static (PasteAIProviderDefinition Provider, bool Updated) EnsureOpenAIProvider(PasteAIConfiguration configuration)
|
||||
{
|
||||
return EnsureProvider(configuration, AIServiceType.OpenAI.ToConfigurationString(), null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures a provider for the supplied service type exists, optionally applying a legacy snapshot.
|
||||
/// </summary>
|
||||
/// <param name="configuration">The configuration instance.</param>
|
||||
/// <param name="serviceTypeKey">The persisted service type key.</param>
|
||||
/// <param name="snapshot">An optional snapshot containing legacy values.</param>
|
||||
/// <returns>The ensured provider and whether the configuration was updated.</returns>
|
||||
public static (PasteAIProviderDefinition Provider, bool Updated) EnsureProvider(PasteAIConfiguration configuration, string serviceTypeKey, AIProviderConfigurationSnapshot snapshot)
|
||||
{
|
||||
if (configuration is null)
|
||||
{
|
||||
@@ -27,45 +71,101 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
|
||||
configuration.Providers ??= new ObservableCollection<PasteAIProviderDefinition>();
|
||||
|
||||
const string serviceTypeKey = "OpenAI";
|
||||
var existingProvider = configuration.Providers.FirstOrDefault(provider => string.Equals(provider.ServiceType, serviceTypeKey, StringComparison.OrdinalIgnoreCase));
|
||||
bool updated = false;
|
||||
var normalizedServiceType = NormalizeServiceType(serviceTypeKey);
|
||||
var existingProvider = configuration.Providers.FirstOrDefault(provider => string.Equals(provider.ServiceType, normalizedServiceType, StringComparison.OrdinalIgnoreCase));
|
||||
bool configurationUpdated = false;
|
||||
|
||||
if (existingProvider is null)
|
||||
{
|
||||
existingProvider = CreateProvider(serviceTypeKey);
|
||||
existingProvider = CreateProvider(normalizedServiceType, snapshot);
|
||||
configuration.Providers.Add(existingProvider);
|
||||
updated = true;
|
||||
configurationUpdated = true;
|
||||
}
|
||||
else if (snapshot is not null)
|
||||
{
|
||||
configurationUpdated |= ApplySnapshot(existingProvider, snapshot);
|
||||
}
|
||||
|
||||
updated |= EnsureActiveProviderIsValid(configuration, existingProvider);
|
||||
configurationUpdated |= EnsureActiveProviderIsValid(configuration, existingProvider);
|
||||
|
||||
return (existingProvider, updated);
|
||||
return (existingProvider, configurationUpdated);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a provider with default values for the requested service type.
|
||||
/// </summary>
|
||||
private static PasteAIProviderDefinition CreateProvider(string serviceTypeKey)
|
||||
private static string NormalizeServiceType(string serviceTypeKey)
|
||||
{
|
||||
var serviceType = serviceTypeKey.ToAIServiceType();
|
||||
return serviceType.ToConfigurationString();
|
||||
}
|
||||
|
||||
private static PasteAIProviderDefinition CreateProvider(string serviceTypeKey, AIProviderConfigurationSnapshot snapshot)
|
||||
{
|
||||
var serviceType = serviceTypeKey.ToAIServiceType();
|
||||
var metadata = AIServiceTypeRegistry.GetMetadata(serviceType);
|
||||
var provider = new PasteAIProviderDefinition
|
||||
{
|
||||
ServiceType = serviceTypeKey,
|
||||
ModelName = PasteAIProviderDefaults.GetDefaultModelName(serviceType),
|
||||
EndpointUrl = string.Empty,
|
||||
ApiVersion = string.Empty,
|
||||
DeploymentName = string.Empty,
|
||||
ModelPath = string.Empty,
|
||||
SystemPrompt = string.Empty,
|
||||
ModerationEnabled = serviceType == AIServiceType.OpenAI,
|
||||
ModelName = !string.IsNullOrWhiteSpace(snapshot?.ModelName) ? snapshot.ModelName : PasteAIProviderDefaults.GetDefaultModelName(serviceType),
|
||||
EndpointUrl = snapshot?.EndpointUrl ?? string.Empty,
|
||||
ApiVersion = snapshot?.ApiVersion ?? string.Empty,
|
||||
DeploymentName = snapshot?.DeploymentName ?? string.Empty,
|
||||
ModelPath = snapshot?.ModelPath ?? string.Empty,
|
||||
SystemPrompt = snapshot?.SystemPrompt ?? string.Empty,
|
||||
ModerationEnabled = snapshot?.ModerationEnabled ?? true,
|
||||
IsLocalModel = metadata.IsLocalModel,
|
||||
};
|
||||
|
||||
return provider;
|
||||
}
|
||||
|
||||
private static bool ApplySnapshot(PasteAIProviderDefinition provider, AIProviderConfigurationSnapshot snapshot)
|
||||
{
|
||||
bool updated = false;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(snapshot.ModelName) && !string.Equals(provider.ModelName, snapshot.ModelName, StringComparison.Ordinal))
|
||||
{
|
||||
provider.ModelName = snapshot.ModelName;
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(snapshot.EndpointUrl) && !string.Equals(provider.EndpointUrl, snapshot.EndpointUrl, StringComparison.Ordinal))
|
||||
{
|
||||
provider.EndpointUrl = snapshot.EndpointUrl;
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(snapshot.ApiVersion) && !string.Equals(provider.ApiVersion, snapshot.ApiVersion, StringComparison.Ordinal))
|
||||
{
|
||||
provider.ApiVersion = snapshot.ApiVersion;
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(snapshot.DeploymentName) && !string.Equals(provider.DeploymentName, snapshot.DeploymentName, StringComparison.Ordinal))
|
||||
{
|
||||
provider.DeploymentName = snapshot.DeploymentName;
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(snapshot.ModelPath) && !string.Equals(provider.ModelPath, snapshot.ModelPath, StringComparison.Ordinal))
|
||||
{
|
||||
provider.ModelPath = snapshot.ModelPath;
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(snapshot.SystemPrompt) && !string.Equals(provider.SystemPrompt, snapshot.SystemPrompt, StringComparison.Ordinal))
|
||||
{
|
||||
provider.SystemPrompt = snapshot.SystemPrompt;
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (provider.ModerationEnabled != snapshot.ModerationEnabled)
|
||||
{
|
||||
provider.ModerationEnabled = snapshot.ModerationEnabled;
|
||||
updated = true;
|
||||
}
|
||||
|
||||
return updated;
|
||||
}
|
||||
|
||||
private static bool EnsureActiveProviderIsValid(PasteAIConfiguration configuration, PasteAIProviderDefinition preferredProvider = null)
|
||||
{
|
||||
if (configuration?.Providers is null || configuration.Providers.Count == 0)
|
||||
|
||||
@@ -20,6 +20,8 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
{
|
||||
private string _activeProviderId = string.Empty;
|
||||
private ObservableCollection<PasteAIProviderDefinition> _providers = new();
|
||||
private bool _useSharedCredentials = true;
|
||||
private Dictionary<string, AIProviderConfigurationSnapshot> _legacyProviderConfigurations;
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
@@ -37,6 +39,21 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
set => SetProperty(ref _providers, value ?? new ObservableCollection<PasteAIProviderDefinition>());
|
||||
}
|
||||
|
||||
[JsonPropertyName("use-shared-credentials")]
|
||||
public bool UseSharedCredentials
|
||||
{
|
||||
get => _useSharedCredentials;
|
||||
set => SetProperty(ref _useSharedCredentials, value);
|
||||
}
|
||||
|
||||
[JsonPropertyName("provider-configurations")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public Dictionary<string, AIProviderConfigurationSnapshot> LegacyProviderConfigurations
|
||||
{
|
||||
get => _legacyProviderConfigurations;
|
||||
set => _legacyProviderConfigurations = value;
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public PasteAIProviderDefinition ActiveProvider
|
||||
{
|
||||
|
||||
@@ -273,6 +273,7 @@
|
||||
|
||||
<panels:MouseJumpPanel x:Name="MouseUtils_MouseJump_Panel" x:Uid="MouseUtils_MouseJump_Panel" />
|
||||
|
||||
<!--
|
||||
<controls:SettingsGroup x:Uid="MouseUtils_CursorWrap" AutomationProperties.AutomationId="MouseUtils_CursorWrapTestId">
|
||||
<controls:GPOInfoControl ShowWarning="{x:Bind ViewModel.IsCursorWrapEnabledGpoConfigured, Mode=OneWay}">
|
||||
<tkcontrols:SettingsCard
|
||||
@@ -300,7 +301,7 @@
|
||||
</tkcontrols:SettingsCard>
|
||||
</tkcontrols:SettingsExpander.Items>
|
||||
</tkcontrols:SettingsExpander>
|
||||
</controls:SettingsGroup>
|
||||
</controls:SettingsGroup>-->
|
||||
<controls:SettingsGroup x:Uid="MouseUtils_MousePointerCrosshairs" AutomationProperties.AutomationId="MouseUtils_MousePointerCrosshairsTestId">
|
||||
<controls:GPOInfoControl ShowWarning="{x:Bind ViewModel.IsMousePointerCrosshairsEnabledGpoConfigured, Mode=OneWay}">
|
||||
<tkcontrols:SettingsCard
|
||||
|
||||
@@ -285,9 +285,6 @@
|
||||
AutomationProperties.AutomationId="InputOutputNavItem"
|
||||
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/InputOutput.png}"
|
||||
SelectsOnInvoked="False">
|
||||
<NavigationViewItem.InfoBadge>
|
||||
<InfoBadge Style="{StaticResource NewInfoBadge}" />
|
||||
</NavigationViewItem.InfoBadge>
|
||||
<NavigationViewItem.MenuItems>
|
||||
<NavigationViewItem
|
||||
x:Name="KeyboardManagerNavigationItem"
|
||||
@@ -302,11 +299,7 @@
|
||||
x:Uid="Shell_MouseUtilities"
|
||||
helpers:NavHelper.NavigateTo="views:MouseUtilsPage"
|
||||
AutomationProperties.AutomationId="MouseUtilitiesNavItem"
|
||||
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/MouseUtils.png}">
|
||||
<NavigationViewItem.InfoBadge>
|
||||
<InfoBadge Style="{StaticResource NewInfoBadge}" />
|
||||
</NavigationViewItem.InfoBadge>
|
||||
</NavigationViewItem>
|
||||
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/MouseUtils.png}" />
|
||||
<NavigationViewItem
|
||||
x:Name="MouseWithoutBordersNavigationItem"
|
||||
x:Uid="Shell_MouseWithoutBorders"
|
||||
|
||||
@@ -191,18 +191,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
return;
|
||||
}
|
||||
|
||||
PasswordCredential legacyCredential = TryGetLegacyOpenAICredential();
|
||||
|
||||
if (legacyCredential is null)
|
||||
{
|
||||
if (legacyAdvancedAIConsumed)
|
||||
{
|
||||
SaveAndNotifySettings();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var configuration = properties.PasteAIConfiguration;
|
||||
if (configuration is null)
|
||||
{
|
||||
@@ -210,11 +198,28 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
properties.PasteAIConfiguration = configuration;
|
||||
}
|
||||
|
||||
bool hasLegacyProviders = configuration.LegacyProviderConfigurations is { Count: > 0 };
|
||||
PasswordCredential legacyCredential = TryGetLegacyOpenAICredential();
|
||||
|
||||
if (!hasLegacyProviders && legacyCredential is null && !legacyAdvancedAIConsumed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool configurationUpdated = false;
|
||||
|
||||
var ensureResult = AdvancedPasteMigrationHelper.EnsureOpenAIProvider(configuration);
|
||||
PasteAIProviderDefinition openAIProvider = ensureResult.Provider;
|
||||
configurationUpdated |= ensureResult.Updated;
|
||||
if (hasLegacyProviders)
|
||||
{
|
||||
configurationUpdated |= AdvancedPasteMigrationHelper.MigrateLegacyProviderConfigurations(configuration);
|
||||
}
|
||||
|
||||
PasteAIProviderDefinition openAIProvider = null;
|
||||
if (legacyCredential is not null || hasLegacyProviders || legacyAdvancedAIConsumed)
|
||||
{
|
||||
var ensureResult = AdvancedPasteMigrationHelper.EnsureOpenAIProvider(configuration);
|
||||
openAIProvider = ensureResult.Provider;
|
||||
configurationUpdated |= ensureResult.Updated;
|
||||
}
|
||||
|
||||
if (legacyAdvancedAIConsumed && openAIProvider is not null && openAIProvider.EnableAdvancedAI != legacyAdvancedAIEnabled)
|
||||
{
|
||||
@@ -228,7 +233,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
RemoveLegacyOpenAICredential();
|
||||
}
|
||||
|
||||
const bool shouldEnableAI = true;
|
||||
bool shouldEnableAI = legacyCredential is not null;
|
||||
bool enabledChanged = false;
|
||||
if (properties.IsAIEnabled != shouldEnableAI)
|
||||
{
|
||||
@@ -1243,6 +1248,11 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
return true;
|
||||
}
|
||||
|
||||
if (current.UseSharedCredentials != incoming.UseSharedCredentials)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var currentProviders = current.Providers ?? new ObservableCollection<PasteAIProviderDefinition>();
|
||||
var incomingProviders = incoming.Providers ?? new ObservableCollection<PasteAIProviderDefinition>();
|
||||
|
||||
@@ -1398,7 +1408,8 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.Equals(e.PropertyName, nameof(PasteAIConfiguration.ActiveProviderId), StringComparison.Ordinal))
|
||||
if (string.Equals(e.PropertyName, nameof(PasteAIConfiguration.ActiveProviderId), StringComparison.Ordinal)
|
||||
|| string.Equals(e.PropertyName, nameof(PasteAIConfiguration.UseSharedCredentials), StringComparison.Ordinal))
|
||||
{
|
||||
SaveAndNotifySettings();
|
||||
}
|
||||
@@ -1415,7 +1426,15 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
|
||||
pasteConfig.Providers ??= new ObservableCollection<PasteAIProviderDefinition>();
|
||||
|
||||
bool configurationUpdated = AdvancedPasteMigrationHelper.MigrateLegacyProviderConfigurations(pasteConfig);
|
||||
|
||||
SubscribeToPasteAIProviders(pasteConfig);
|
||||
|
||||
if (configurationUpdated)
|
||||
{
|
||||
SaveAndNotifySettings();
|
||||
OnPropertyChanged(nameof(PasteAIConfiguration));
|
||||
}
|
||||
}
|
||||
|
||||
private static string RetrieveCredentialValue(string credentialResource, string credentialUserName)
|
||||
|
||||
@@ -137,6 +137,12 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
|
||||
foreach (ModuleType moduleType in Enum.GetValues<ModuleType>())
|
||||
{
|
||||
// Hide CursorWrap from Dashboard
|
||||
if (moduleType == ModuleType.CursorWrap)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
GpoRuleConfigured gpo = ModuleHelper.GetModuleGpoConfiguration(moduleType);
|
||||
var newItem = new DashboardListItem()
|
||||
{
|
||||
@@ -145,7 +151,8 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
IsEnabled = gpo == GpoRuleConfigured.Enabled || (gpo != GpoRuleConfigured.Disabled && ModuleHelper.GetIsModuleEnabled(generalSettingsConfig, moduleType)),
|
||||
IsLocked = gpo == GpoRuleConfigured.Enabled || gpo == GpoRuleConfigured.Disabled,
|
||||
Icon = ModuleHelper.GetModuleTypeFluentIconName(moduleType),
|
||||
IsNew = moduleType == ModuleType.CursorWrap,
|
||||
|
||||
// IsNew = moduleType == ModuleType.CursorWrap,
|
||||
DashboardModuleItems = GetModuleItems(moduleType),
|
||||
};
|
||||
newItem.EnabledChangedCallback = EnabledChangedOnUI;
|
||||
|
||||
@@ -36,6 +36,12 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
|
||||
foreach (ModuleType moduleType in Enum.GetValues<ModuleType>())
|
||||
{
|
||||
// Hide CursorWrap from All Apps flyout
|
||||
if (moduleType == ModuleType.CursorWrap)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
AddFlyoutMenuItem(moduleType);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<AdditionalIncludeDirectories>../../../src/</AdditionalIncludeDirectories>
|
||||
<TreatAngleIncludeAsExternal>true</TreatAngleIncludeAsExternal>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
@@ -38,6 +37,10 @@
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\..\deps\cziplib\src\zip.c">
|
||||
<!-- Disabling warnings for external code -->
|
||||
<DisableSpecificWarnings>4706;26451;4267;4244;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<ClCompile Include="EventViewer.cpp" />
|
||||
<ClCompile Include="InstallationFolder.cpp" />
|
||||
<ClCompile Include="Package.cpp" />
|
||||
@@ -58,6 +61,8 @@
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\..\deps\cziplib\src\miniz.h" />
|
||||
<ClInclude Include="..\..\..\deps\cziplib\src\zip.h" />
|
||||
<ClInclude Include="EventViewer.h" />
|
||||
<ClInclude Include="InstallationFolder.h" />
|
||||
<ClInclude Include="Package.h" />
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
<ClCompile Include="ZipTools\ZipFolder.cpp">
|
||||
<Filter>ZipTools</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\deps\cziplib\src\zip.c" />
|
||||
<ClCompile Include="ReportMonitorInfo.cpp" />
|
||||
<ClCompile Include="RegistryUtils.cpp" />
|
||||
<ClCompile Include="EventViewer.cpp" />
|
||||
@@ -27,6 +28,8 @@
|
||||
<Filter>ZipTools</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\common\utils\json.h" />
|
||||
<ClInclude Include="..\..\..\deps\cziplib\src\miniz.h" />
|
||||
<ClInclude Include="..\..\..\deps\cziplib\src\zip.h" />
|
||||
<ClInclude Include="ReportMonitorInfo.h" />
|
||||
<ClInclude Include="RegistryUtils.h" />
|
||||
<ClInclude Include="EventViewer.h" />
|
||||
|
||||
@@ -1,53 +1,50 @@
|
||||
#include "ZipFolder.h"
|
||||
#include "..\..\..\..\deps\cziplib\src\zip.h"
|
||||
#include <common/utils/timeutil.h>
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
|
||||
#include <format>
|
||||
#include <wil/stl.h>
|
||||
#include <wil/win32_helpers.h>
|
||||
|
||||
void ZipFolder(std::filesystem::path zipPath, std::filesystem::path folderPath)
|
||||
{
|
||||
const auto reportFilename{
|
||||
std::format("PowerToysReport_{0}.zip",
|
||||
timeutil::format_as_local("%F-%H-%M-%S", timeutil::now()))
|
||||
};
|
||||
const auto finalReportFullPath{ zipPath / reportFilename };
|
||||
std::string reportFilename{ "PowerToysReport_" };
|
||||
reportFilename += timeutil::format_as_local("%F-%H-%M-%S", timeutil::now());
|
||||
reportFilename += ".zip";
|
||||
|
||||
const auto tempReportFilename{ reportFilename + ".tmp" };
|
||||
const auto tempReportFullPath{ zipPath / tempReportFilename };
|
||||
auto tmpZipPath = std::filesystem::temp_directory_path();
|
||||
tmpZipPath /= reportFilename;
|
||||
|
||||
// tar -c --format=zip -f "ReportFile.zip" *
|
||||
const auto executable{ wil::ExpandEnvironmentStringsW<std::wstring>(LR"(%WINDIR%\System32\tar.exe)") };
|
||||
auto commandline{ std::format(LR"("{0}" -c --format=zip -f "{1}" *)", executable, tempReportFullPath.wstring()) };
|
||||
|
||||
const auto folderPathAsString{ folderPath.lexically_normal().wstring() };
|
||||
|
||||
wil::unique_process_information pi;
|
||||
STARTUPINFOW si{ .cb = sizeof(STARTUPINFOW) };
|
||||
if (!CreateProcessW(executable.c_str(),
|
||||
commandline.data() /* must be mutable */,
|
||||
nullptr,
|
||||
nullptr,
|
||||
FALSE,
|
||||
DETACHED_PROCESS,
|
||||
nullptr,
|
||||
folderPathAsString.c_str(),
|
||||
&si,
|
||||
&pi))
|
||||
struct zip_t* zip = zip_open(tmpZipPath.string().c_str(), ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
|
||||
if (!zip)
|
||||
{
|
||||
printf("Cannot open zip.");
|
||||
throw -1;
|
||||
}
|
||||
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
using recursive_directory_iterator = std::filesystem::recursive_directory_iterator;
|
||||
const size_t rootSize = folderPath.wstring().size();
|
||||
for (const auto& dirEntry : recursive_directory_iterator(folderPath))
|
||||
{
|
||||
if (dirEntry.is_regular_file())
|
||||
{
|
||||
auto path = dirEntry.path().string();
|
||||
auto relativePath = path.substr(rootSize, path.size());
|
||||
zip_entry_open(zip, relativePath.c_str());
|
||||
zip_entry_fwrite(zip, path.c_str());
|
||||
zip_entry_close(zip);
|
||||
}
|
||||
}
|
||||
|
||||
std::error_code err{};
|
||||
std::filesystem::rename(tempReportFullPath, finalReportFullPath, err);
|
||||
zip_close(zip);
|
||||
|
||||
std::error_code err;
|
||||
std::filesystem::copy(tmpZipPath, zipPath, err);
|
||||
if (err.value() != 0)
|
||||
{
|
||||
wprintf_s(L"Failed to rename %s. Error code: %d\n", tempReportFullPath.native().c_str(), err.value());
|
||||
wprintf_s(L"Failed to copy %s. Error code: %d\n", tmpZipPath.c_str(), err.value());
|
||||
}
|
||||
}
|
||||
|
||||
err = {};
|
||||
std::filesystem::remove_all(tmpZipPath, err);
|
||||
if (err.value() != 0)
|
||||
{
|
||||
wprintf_s(L"Failed to delete %s. Error code: %d\n", tmpZipPath.c_str(), err.value());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user