Compare commits

...

4 Commits

Author SHA1 Message Date
Michael Jolley
449c4ec95c More of the service stuff 2025-11-15 17:03:12 -06:00
Michael Jolley
0873dcf341 broken services 2025-11-14 16:50:36 -06:00
Shawn Yuan
1884e6abc1 Remove unused properties in AP (#43564)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
This pull request removes legacy provider configuration migration logic
and associated data structures from the Advanced Paste AI provider
settings. The changes simplify the codebase by eliminating support for
legacy provider configuration snapshots and related migration methods,
focusing configuration management on the current provider model.

## 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
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

---------

Signed-off-by: Shawn Yuan (from Dev Box) <shuaiyuan@microsoft.com>
2025-11-14 16:56:23 +08:00
leileizhang
ad4b553bb1 Remove all AdvancedPaste stored keys during uninstall (#43563)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
Remove all AdvancedPaste stored keys during uninstall
<!-- Please review the items on the PR checklist before submitting-->
## 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
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2025-11-14 16:15:15 +08:00
20 changed files with 244 additions and 370 deletions

View File

@@ -4,6 +4,7 @@
#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"
@@ -856,14 +857,69 @@ 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");
cred = vault.Retrieve(L"https://platform.openai.com/api-keys", L"PowerToys_AdvancedPaste_OpenAIKey");
vault.Remove(cred);
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 (...)
{
}
}
}
catch (...)
{

View File

@@ -168,6 +168,15 @@ 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)
@@ -176,30 +185,11 @@ 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;
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;
}
var ensureResult = AdvancedPasteMigrationHelper.EnsureOpenAIProvider(configuration);
PasteAIProviderDefinition openAIProvider = ensureResult.Provider;
configurationUpdated |= ensureResult.Updated;
if (legacyAdvancedAIConsumed && openAIProvider is not null && openAIProvider.EnableAdvancedAI != legacyAdvancedAIEnabled)
{
@@ -207,13 +197,13 @@ namespace AdvancedPaste.Settings
configurationUpdated = true;
}
if (legacyCredential is not null && openAIProvider is not null)
if (openAIProvider is not null)
{
StoreMigratedOpenAICredential(openAIProvider.Id, openAIProvider.ServiceType, legacyCredential.Password);
RemoveLegacyOpenAICredential();
}
bool shouldEnableAI = legacyCredential is not null;
const bool shouldEnableAI = true;
bool enabledUpdated = false;
if (properties.IsAIEnabled != shouldEnableAI)
{

View File

@@ -16,7 +16,6 @@ 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;
@@ -38,8 +37,10 @@ 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;
@@ -53,17 +54,18 @@ public partial class MainListPage : DynamicListPage,
private CancellationTokenSource? _cancellationTokenSource;
public MainListPage(IServiceProvider serviceProvider)
public MainListPage(TopLevelCommandManager topLevelCommandManager, SettingsModel settingsModel, AliasManager aliasManager, AppStateModel appStateModel)
{
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 = _serviceProvider.GetService<TopLevelCommandManager>()!;
_tlcManager = 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;
@@ -78,12 +80,13 @@ public partial class MainListPage : DynamicListPage,
WeakReferenceMessenger.Default.Register<ClearSearchMessage>(this);
WeakReferenceMessenger.Default.Register<UpdateFallbackItemsMessage>(this);
var settings = _serviceProvider.GetService<SettingsModel>()!;
settings.SettingsChanged += SettingsChangedHandler;
HotReloadSettings(settings);
_settings = 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)
@@ -219,14 +222,12 @@ 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 (aliases.CheckAlias(newSearch))
if (_aliasManager.CheckAlias(newSearch))
{
if (_filteredItemsIncludesApps != _includeApps)
{
@@ -388,7 +389,7 @@ public partial class MainListPage : DynamicListPage,
}
}
var history = _serviceProvider.GetService<AppStateModel>()!.RecentCommands!;
var history = _appState.RecentCommands!;
Func<string, IListItem, int> scoreItem = (a, b) => { return ScoreTopLevelItem(a, b, history); };
// Produce a list of everything that matches the current filter.
@@ -483,9 +484,8 @@ 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,10 +580,9 @@ public partial class MainListPage : DynamicListPage,
public void UpdateHistory(IListItem topLevelOrAppItem)
{
var id = IdForTopLevelOrAppItem(topLevelOrAppItem);
var state = _serviceProvider.GetService<AppStateModel>()!;
var history = state.RecentCommands;
var history = _appState.RecentCommands;
history.AddHistoryItem(id);
AppStateModel.SaveState(state);
AppStateModel.SaveState(_appState);
}
private static string IdForTopLevelOrAppItem(IListItem topLevelOrAppItem)
@@ -615,10 +614,9 @@ public partial class MainListPage : DynamicListPage,
_tlcManager.PropertyChanged -= TlcManager_PropertyChanged;
_tlcManager.TopLevelCommands.CollectionChanged -= Commands_CollectionChanged;
var settings = _serviceProvider.GetService<SettingsModel>();
if (settings is not null)
if (_settings is not null)
{
settings.SettingsChanged -= SettingsChangedHandler;
_settings.SettingsChanged -= SettingsChangedHandler;
}
WeakReferenceMessenger.Default.UnregisterAll(this);

View File

@@ -9,16 +9,14 @@ 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,
IServiceProvider _serviceProvider) : ObservableObject
SettingsModel _settings) : ObservableObject
{
private readonly SettingsModel _settings = _serviceProvider.GetService<SettingsModel>()!;
private readonly Lock _initializeSettingsLock = new();
private Task? _initializeSettingsTask;

View File

@@ -5,14 +5,13 @@
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 IServiceProvider _serviceProvider;
private readonly TopLevelCommandManager _tlcManager;
public event PropertyChangedEventHandler? PropertyChanged;
@@ -142,10 +141,10 @@ public partial class SettingsViewModel : INotifyPropertyChanged
public SettingsExtensionsViewModel Extensions { get; }
public SettingsViewModel(SettingsModel settings, IServiceProvider serviceProvider, TaskScheduler scheduler)
public SettingsViewModel(SettingsModel settings, TaskScheduler scheduler, TopLevelCommandManager topLevelCommandManager)
{
_settings = settings;
_serviceProvider = serviceProvider;
_tlcManager = topLevelCommandManager;
var activeProviders = GetCommandProviders();
var allProviderSettings = _settings.ProviderSettings;
@@ -154,7 +153,7 @@ public partial class SettingsViewModel : INotifyPropertyChanged
{
var providerSettings = settings.GetProviderSettings(item);
var settingsModel = new ProviderSettingsViewModel(item, providerSettings, _serviceProvider);
var settingsModel = new ProviderSettingsViewModel(item, providerSettings, _settings);
CommandProviders.Add(settingsModel);
}
@@ -163,8 +162,7 @@ public partial class SettingsViewModel : INotifyPropertyChanged
private IEnumerable<CommandProviderWrapper> GetCommandProviders()
{
var manager = _serviceProvider.GetService<TopLevelCommandManager>()!;
var allProviders = manager.CommandProviders;
var allProviders = _tlcManager.CommandProviders;
return allProviders;
}

View File

@@ -34,10 +34,10 @@ public partial class TopLevelCommandManager : ObservableObject,
TaskScheduler IPageContext.Scheduler => _taskScheduler;
public TopLevelCommandManager(IServiceProvider serviceProvider)
public TopLevelCommandManager(IServiceProvider serviceProvider, TaskScheduler taskScheduler)
{
_serviceProvider = serviceProvider;
_taskScheduler = _serviceProvider.GetService<TaskScheduler>()!;
_taskScheduler = taskScheduler;
WeakReferenceMessenger.Default.Register<ReloadCommandsMessage>(this);
_reloadCommandsGate = new(ReloadAllCommandsAsyncCore);
}

View File

@@ -23,6 +23,8 @@ 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;
@@ -51,10 +53,7 @@ public partial class App : Application
public ETWTrace EtwTrace { get; private set; } = new ETWTrace();
/// <summary>
/// Gets the <see cref="IServiceProvider"/> instance to resolve application services.
/// </summary>
public IServiceProvider Services { get; }
private readonly ServiceProvider _services;
/// <summary>
/// Initializes a new instance of the <see cref="App"/> class.
@@ -67,7 +66,7 @@ public partial class App : Application
_globalErrorHandler.Register(this);
#endif
Services = ConfigureServices();
_services = ConfigureServices();
this.InitializeComponent();
@@ -94,7 +93,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 = new MainWindow();
AppWindow = _services.GetRequiredService<MainWindow>();
var activatedEventArgs = Microsoft.Windows.AppLifecycle.AppInstance.GetCurrent().GetActivatedEventArgs();
((MainWindow)AppWindow).HandleLaunchNonUI(activatedEventArgs);
@@ -110,6 +109,11 @@ 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();
@@ -152,25 +156,35 @@ public partial class App : Application
services.AddSingleton<ICommandProvider, TimeDateCommandsProvider>();
services.AddSingleton<ICommandProvider, SystemCommandExtensionProvider>();
// Models
services.AddSingleton<TopLevelCommandManager>();
services.AddSingleton<AliasManager>();
services.AddSingleton<HotkeyManager>();
// Settings & state
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>();
services.AddSingleton<IRootPageService, PowerToysRootPageService>();
services.AddSingleton<IAppHostService, PowerToysAppHostService>();
services.AddSingleton<ITelemetryService, TelemetryForwarder>();
// ViewModels
services.AddSingleton<IRootPageService, PowerToysRootPageService>();
services.AddSingleton<SettingsViewModel>();
services.AddSingleton<ShellViewModel>();
services.AddSingleton<IPageViewModelFactoryService, CommandPalettePageViewModelFactory>();
// Views
services.AddSingleton<MainWindow>();
services.AddSingleton<ShellPage>();
services.AddTransient<ListPage>();
// Settings Pages
services.AddTransient<GeneralPage>();
services.AddTransient<ExtensionPage>();
services.AddTransient<ExtensionsPage>();
return services.BuildServiceProvider();
}

View File

@@ -11,7 +11,6 @@ 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;
@@ -31,6 +30,7 @@ public sealed partial class ListPage : Page,
IRecipient<ActivateSelectedListItemMessage>,
IRecipient<ActivateSecondaryCommandMessage>
{
private readonly SettingsModel _settings;
private InputSource _lastInputSource;
internal ListViewModel? ViewModel
@@ -51,9 +51,11 @@ public sealed partial class ListPage : Page,
}
}
public ListPage()
public ListPage(SettingsModel settings)
{
this.InitializeComponent();
_settings = settings;
this.NavigationCacheMode = NavigationCacheMode.Disabled;
this.ItemView.Loaded += Items_Loaded;
this.ItemView.PreviewKeyDown += Items_PreviewKeyDown;
@@ -133,8 +135,7 @@ public sealed partial class ListPage : Page,
return;
}
var settings = App.Current.Services.GetService<SettingsModel>()!;
if (settings.SingleClickActivates)
if (_settings.SingleClickActivates)
{
ViewModel?.InvokeItemCommand.Execute(item);
}
@@ -150,8 +151,7 @@ public sealed partial class ListPage : Page,
{
if (ItemView.SelectedItem is ListItemViewModel vm)
{
var settings = App.Current.Services.GetService<SettingsModel>()!;
if (!settings.SingleClickActivates)
if (!_settings.SingleClickActivates)
{
ViewModel?.InvokeItemCommand.Execute(vm);
}

View File

@@ -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_*")]
internal sealed partial class TrayIconService
public sealed partial class TrayIconService
{
private const uint MY_NOTIFY_ID = 1000;
private const uint WM_TRAY_ICON = PInvoke.WM_USER + 1;

View File

@@ -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>

View File

@@ -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,6 +58,10 @@ 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;
@@ -65,10 +69,17 @@ public sealed partial class MainWindow : WindowEx,
private WindowPosition _currentWindowPosition = new();
public MainWindow()
public MainWindow(SettingsModel settingsModel, TrayIconService trayIconService, IExtensionService extensionService, ShellPage shellPage)
{
InitializeComponent();
_settings = settingsModel;
_trayIconService = trayIconService;
_extensionService = extensionService;
_rootShellPage = shellPage;
Window.WindowContent = _rootShellPage;
_hwnd = new HWND(WinRT.Interop.WindowNative.GetWindowHandle(this).ToInt32());
unsafe
@@ -102,7 +113,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");
@@ -116,10 +127,10 @@ public sealed partial class MainWindow : WindowEx,
// Load our settings, and then also wire up a settings changed handler
HotReloadSettings();
App.Current.Services.GetService<SettingsModel>()!.SettingsChanged += SettingsChangedHandler;
_settings.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", () =>
@@ -160,8 +171,7 @@ public sealed partial class MainWindow : WindowEx,
private void RestoreWindowPosition()
{
var settings = App.Current.Services.GetService<SettingsModel>();
if (settings?.LastWindowPosition is not WindowPosition savedPosition)
if (_settings.LastWindowPosition is not WindowPosition savedPosition)
{
PositionCentered();
return;
@@ -218,12 +228,10 @@ public sealed partial class MainWindow : WindowEx,
private void HotReloadSettings()
{
var settings = App.Current.Services.GetService<SettingsModel>()!;
SetupHotkey(_settings);
_trayIconService.SetupTrayIcon(_settings.ShowSystemTrayIcon);
SetupHotkey(settings);
App.Current.Services.GetService<TrayIconService>()!.SetupTrayIcon(settings.ShowSystemTrayIcon);
_ignoreHotKeyWhenFullScreen = settings.IgnoreShortcutWhenFullscreen;
_ignoreHotKeyWhenFullScreen = _settings.IgnoreShortcutWhenFullscreen;
}
// We want to use DesktopAcrylicKind.Thin and custom colors as this is the default material
@@ -378,9 +386,7 @@ public sealed partial class MainWindow : WindowEx,
public void Receive(ShowWindowMessage message)
{
var settings = App.Current.Services.GetService<SettingsModel>()!;
ShowHwnd(message.Hwnd, settings.SummonOn);
ShowHwnd(message.Hwnd, _settings.SummonOn);
}
public void Receive(HideWindowMessage message)
@@ -467,13 +473,11 @@ public sealed partial class MainWindow : WindowEx,
internal void MainWindow_Closed(object sender, WindowEventArgs args)
{
var serviceProvider = App.Current.Services;
UpdateWindowPositionInMemory();
var settings = serviceProvider.GetService<SettingsModel>();
if (settings is not null)
if (_settings is not null)
{
settings.LastWindowPosition = new WindowPosition
_settings.LastWindowPosition = new WindowPosition
{
X = _currentWindowPosition.X,
Y = _currentWindowPosition.Y,
@@ -481,13 +485,12 @@ public sealed partial class MainWindow : WindowEx,
Height = _currentWindowPosition.Height,
};
SettingsModel.SaveSettings(settings);
SettingsModel.SaveSettings(_settings);
}
var extensionService = serviceProvider.GetService<IExtensionService>()!;
extensionService.SignalStopExtensionsAsync();
_extensionService.SignalStopExtensionsAsync();
App.Current.Services.GetService<TrayIconService>()!.Destroy();
_trayIconService.Destroy();
// WinUI bug is causing a crash on shutdown when FailFastOnErrors is set to true (#51773592).
// Workaround by turning it off before shutdown.
@@ -513,28 +516,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
@@ -624,8 +627,7 @@ public sealed partial class MainWindow : WindowEx,
}
else if (uri.StartsWith("x-cmdpal://reload", StringComparison.OrdinalIgnoreCase))
{
var settings = App.Current.Services.GetService<SettingsModel>();
if (settings?.AllowExternalReload == true)
if (_settings.AllowExternalReload == true)
{
Logger.LogInfo("External Reload triggered");
WeakReferenceMessenger.Default.Send<ReloadCommandsMessage>(new());

View File

@@ -16,12 +16,10 @@ 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;
@@ -62,6 +60,9 @@ 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;
@@ -69,14 +70,21 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
private CancellationTokenSource? _focusAfterLoadedCts;
private WeakReference<Page>? _lastNavigatedPageRef;
public ShellViewModel ViewModel { get; private set; } = App.Current.Services.GetService<ShellViewModel>()!;
public ShellViewModel ViewModel { get; private set; }
public event PropertyChangedEventHandler? PropertyChanged;
public ShellPage()
public ShellPage(
ShellViewModel shellViewModel,
SettingsModel settingsModel,
TopLevelCommandManager topLevelCommandManager)
{
this.InitializeComponent();
ViewModel = shellViewModel;
_settings = settingsModel;
tlcManager = topLevelCommandManager;
// how we are doing navigation around
WeakReferenceMessenger.Default.Register<NavigateBackMessage>(this);
WeakReferenceMessenger.Default.Register<OpenSettingsMessage>(this);
@@ -112,19 +120,16 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
{
get
{
var settings = App.Current.Services.GetService<SettingsModel>()!;
return settings.DisableAnimations ? _noAnimation : _slideRightTransition;
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();
}
@@ -335,7 +340,6 @@ 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)
@@ -346,11 +350,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();
}
@@ -360,8 +364,7 @@ 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 toplevel commands.
var tlcManager = App.Current.Services.GetService<TopLevelCommandManager>()!;
// command from our list of top level commands.
var topLevelCommand = tlcManager.LookupCommand(commandId);
if (topLevelCommand is not null)
{
@@ -655,15 +658,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;
}
}
}

View File

@@ -10,7 +10,6 @@ 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,
@@ -19,24 +18,23 @@ namespace Microsoft.CmdPal.UI;
internal sealed class PowerToysRootPageService : IRootPageService
{
private readonly IServiceProvider _serviceProvider;
private readonly TopLevelCommandManager _topLevelCommandManager;
private IExtensionWrapper? _activeExtension;
private Lazy<MainListPage> _mainListPage;
public PowerToysRootPageService(IServiceProvider serviceProvider)
public PowerToysRootPageService(TopLevelCommandManager topLevelCommandManager, SettingsModel settingsModel, AliasManager aliasManager, AppStateModel appStateModel)
{
_serviceProvider = serviceProvider;
_topLevelCommandManager = topLevelCommandManager;
_mainListPage = new Lazy<MainListPage>(() =>
{
return new MainListPage(_serviceProvider);
return new MainListPage(_topLevelCommandManager, settingsModel, aliasManager, appStateModel);
});
}
public async Task PreLoadAsync()
{
var tlcManager = _serviceProvider.GetService<TopLevelCommandManager>()!;
await tlcManager.LoadBuiltinsAsync();
await _topLevelCommandManager.LoadBuiltinsAsync();
}
public Microsoft.CommandPalette.Extensions.IPage GetRootPage()
@@ -46,13 +44,11 @@ internal sealed class PowerToysRootPageService : IRootPageService
public async Task PostLoadRootPageAsync()
{
var tlcManager = _serviceProvider.GetService<TopLevelCommandManager>()!;
// After loading built-ins, and starting navigation, kick off a thread to load extensions.
tlcManager.LoadExtensionsCommand.Execute(null);
_topLevelCommandManager.LoadExtensionsCommand.Execute(null);
await tlcManager.LoadExtensionsCommand.ExecutionTask!;
if (tlcManager.LoadExtensionsCommand.ExecutionTask.Status != TaskStatus.RanToCompletion)
await _topLevelCommandManager.LoadExtensionsCommand.ExecutionTask!;
if (_topLevelCommandManager.LoadExtensionsCommand.ExecutionTask.Status != TaskStatus.RanToCompletion)
{
// TODO: Handle failure case
}

View File

@@ -10,8 +10,6 @@ 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()

View File

@@ -5,7 +5,6 @@
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;
@@ -14,16 +13,13 @@ namespace Microsoft.CmdPal.UI.Settings;
public sealed partial class ExtensionsPage : Page
{
private readonly TaskScheduler _mainTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
private readonly SettingsViewModel? viewModel;
public ExtensionsPage()
public ExtensionsPage(SettingsViewModel settingsViewModel)
{
this.InitializeComponent();
var settings = App.Current.Services.GetService<SettingsModel>()!;
viewModel = new SettingsViewModel(settings, App.Current.Services, _mainTaskScheduler);
viewModel = settingsViewModel;
}
private void SettingsCard_Click(object sender, RoutedEventArgs e)

View File

@@ -3,7 +3,6 @@
// 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;
@@ -11,16 +10,13 @@ namespace Microsoft.CmdPal.UI.Settings;
public sealed partial class GeneralPage : Page
{
private readonly TaskScheduler _mainTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
private readonly SettingsViewModel? viewModel;
public GeneralPage()
public GeneralPage(SettingsViewModel settings, TopLevelCommandManager topLevelCommandManager)
{
this.InitializeComponent();
var settings = App.Current.Services.GetService<SettingsModel>()!;
viewModel = new SettingsViewModel(settings, App.Current.Services, _mainTaskScheduler);
viewModel = settings;
}
public string ApplicationVersion

View File

@@ -1,35 +0,0 @@
// 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;
}
}

View File

@@ -13,56 +13,12 @@ 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)
{
@@ -71,101 +27,45 @@ namespace Microsoft.PowerToys.Settings.UI.Library
configuration.Providers ??= new ObservableCollection<PasteAIProviderDefinition>();
var normalizedServiceType = NormalizeServiceType(serviceTypeKey);
var existingProvider = configuration.Providers.FirstOrDefault(provider => string.Equals(provider.ServiceType, normalizedServiceType, StringComparison.OrdinalIgnoreCase));
bool configurationUpdated = false;
const string serviceTypeKey = "OpenAI";
var existingProvider = configuration.Providers.FirstOrDefault(provider => string.Equals(provider.ServiceType, serviceTypeKey, StringComparison.OrdinalIgnoreCase));
bool updated = false;
if (existingProvider is null)
{
existingProvider = CreateProvider(normalizedServiceType, snapshot);
existingProvider = CreateProvider(serviceTypeKey);
configuration.Providers.Add(existingProvider);
configurationUpdated = true;
}
else if (snapshot is not null)
{
configurationUpdated |= ApplySnapshot(existingProvider, snapshot);
updated = true;
}
configurationUpdated |= EnsureActiveProviderIsValid(configuration, existingProvider);
updated |= EnsureActiveProviderIsValid(configuration, existingProvider);
return (existingProvider, configurationUpdated);
return (existingProvider, updated);
}
private static string NormalizeServiceType(string serviceTypeKey)
{
var serviceType = serviceTypeKey.ToAIServiceType();
return serviceType.ToConfigurationString();
}
private static PasteAIProviderDefinition CreateProvider(string serviceTypeKey, AIProviderConfigurationSnapshot snapshot)
/// <summary>
/// Creates a provider with default values for the requested service type.
/// </summary>
private static PasteAIProviderDefinition CreateProvider(string serviceTypeKey)
{
var serviceType = serviceTypeKey.ToAIServiceType();
var metadata = AIServiceTypeRegistry.GetMetadata(serviceType);
var provider = new PasteAIProviderDefinition
{
ServiceType = serviceTypeKey,
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,
ModelName = PasteAIProviderDefaults.GetDefaultModelName(serviceType),
EndpointUrl = string.Empty,
ApiVersion = string.Empty,
DeploymentName = string.Empty,
ModelPath = string.Empty,
SystemPrompt = string.Empty,
ModerationEnabled = serviceType == AIServiceType.OpenAI,
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)

View File

@@ -20,8 +20,6 @@ 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;
@@ -39,21 +37,6 @@ 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
{

View File

@@ -191,6 +191,18 @@ 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)
{
@@ -198,28 +210,11 @@ 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;
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;
}
var ensureResult = AdvancedPasteMigrationHelper.EnsureOpenAIProvider(configuration);
PasteAIProviderDefinition openAIProvider = ensureResult.Provider;
configurationUpdated |= ensureResult.Updated;
if (legacyAdvancedAIConsumed && openAIProvider is not null && openAIProvider.EnableAdvancedAI != legacyAdvancedAIEnabled)
{
@@ -233,7 +228,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
RemoveLegacyOpenAICredential();
}
bool shouldEnableAI = legacyCredential is not null;
const bool shouldEnableAI = true;
bool enabledChanged = false;
if (properties.IsAIEnabled != shouldEnableAI)
{
@@ -1248,11 +1243,6 @@ 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>();
@@ -1408,8 +1398,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
return;
}
if (string.Equals(e.PropertyName, nameof(PasteAIConfiguration.ActiveProviderId), StringComparison.Ordinal)
|| string.Equals(e.PropertyName, nameof(PasteAIConfiguration.UseSharedCredentials), StringComparison.Ordinal))
if (string.Equals(e.PropertyName, nameof(PasteAIConfiguration.ActiveProviderId), StringComparison.Ordinal))
{
SaveAndNotifySettings();
}
@@ -1426,15 +1415,7 @@ 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)