mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-14 18:57:55 +01:00
Merge branch 'main' into shawn/fixAPPrompttextbox
This commit is contained in:
40
README.md
40
README.md
@@ -7,7 +7,9 @@
|
||||
<h1 align="center">
|
||||
<span>Microsoft PowerToys</span>
|
||||
</h1>
|
||||
|
||||
<p align="center">
|
||||
<span align="center">Microsoft PowerToys is a collection of utilities that help you customize Windows and streamline everyday tasks.</span>
|
||||
</p>
|
||||
<h3 align="center">
|
||||
<a href="#-installation">Installation</a>
|
||||
<span> · </span>
|
||||
@@ -18,8 +20,10 @@
|
||||
<a href="#-whats-new">Release notes</a>
|
||||
</h3>
|
||||
<br/><br/>
|
||||
Microsoft PowerToys is a collection of utilities that help you customize Windows and streamline everyday tasks.
|
||||
<br/><br/>
|
||||
|
||||
## 🔨 Utilities
|
||||
|
||||
PowerToys includes over 25 utilities to help you customize and optimize your Windows experience:
|
||||
|
||||
| | | |
|
||||
|---|---|---|
|
||||
@@ -37,20 +41,13 @@ Microsoft PowerToys is a collection of utilities that help you customize Windows
|
||||
|
||||
## 📋 Installation
|
||||
|
||||
For detailed installation instructions, visit the [installation docs](https://learn.microsoft.com/windows/powertoys/install).
|
||||
|
||||
Before you begin, make sure your device meets the system requirements:
|
||||
|
||||
> [!NOTE]
|
||||
> - Windows 11 or Windows 10 version 2004 (20H1 / build 19041) or newer
|
||||
> - 64-bit processor: x64 or ARM64
|
||||
> - Latest stable version of [Microsoft Edge WebView2 Runtime](https://go.microsoft.com/fwlink/p/?LinkId=2124703) is installed via the bootstrapper during setup
|
||||
|
||||
Choose one of the installation methods below:
|
||||
For detailed installation instructions and system requirements, visit the [installation docs](https://learn.microsoft.com/windows/powertoys/install).
|
||||
|
||||
But to get started quickly, choose one of the installation methods below:
|
||||
<br/><br/>
|
||||
<details open>
|
||||
<summary>Download .exe from GitHub</summary>
|
||||
|
||||
<summary><strong>Download .exe from GitHub</strong></summary>
|
||||
<br/>
|
||||
Go to the [PowerToys GitHub releases][github-release-link], click Assets to reveal the downloads, and choose the installer that matches your architecture and install scope. For most devices, that's the x64 per-user installer.
|
||||
|
||||
<!-- items that need to be updated release to release -->
|
||||
@@ -67,11 +64,11 @@ Go to the [PowerToys GitHub releases][github-release-link], click Assets to reve
|
||||
| Per user - ARM64 | [PowerToysUserSetup-0.95.1-arm64.exe][ptUserArm64] |
|
||||
| Machine wide - x64 | [PowerToysSetup-0.95.1-x64.exe][ptMachineX64] |
|
||||
| Machine wide - ARM64 | [PowerToysSetup-0.95.1-arm64.exe][ptMachineArm64] |
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Microsoft Store</summary>
|
||||
<summary><strong>Microsoft Store</strong></summary>
|
||||
<br/>
|
||||
You can easily install PowerToys from the Microsoft Store:
|
||||
<p>
|
||||
<a style="text-decoration:none" href="https://aka.ms/getPowertoys">
|
||||
@@ -82,10 +79,9 @@ You can easily install PowerToys from the Microsoft Store:
|
||||
</p>
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>WinGet</summary>
|
||||
|
||||
<summary><strong>WinGet</strong></summary>
|
||||
<br/>
|
||||
Download PowerToys from [WinGet][winget-link]. Updating PowerToys via winget will respect the current PowerToys installation scope. To install PowerToys, run the following command from the command line / PowerShell:
|
||||
|
||||
*User scope installer [default]*
|
||||
@@ -100,8 +96,8 @@ winget install --scope machine Microsoft.PowerToys -s winget
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Other methods</summary>
|
||||
|
||||
<summary><strong>Other methods</strong></summary>
|
||||
<br/>
|
||||
There are [community driven install methods](./doc/unofficialInstallMethods.md) such as Chocolatey and Scoop. If these are your preferred install solutions, you can find the install instructions there.
|
||||
</details>
|
||||
|
||||
|
||||
@@ -163,28 +163,143 @@ namespace AdvancedPaste.Settings
|
||||
return false;
|
||||
}
|
||||
|
||||
if (settings.Properties.IsAIEnabled || !LegacyOpenAIKeyExists())
|
||||
var properties = settings.Properties;
|
||||
var configuration = properties.PasteAIConfiguration;
|
||||
|
||||
if (configuration is null)
|
||||
{
|
||||
configuration = new PasteAIConfiguration();
|
||||
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;
|
||||
}
|
||||
|
||||
settings.Properties.IsAIEnabled = true;
|
||||
return true;
|
||||
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;
|
||||
}
|
||||
|
||||
if (legacyAdvancedAIConsumed && openAIProvider is not null && openAIProvider.EnableAdvancedAI != legacyAdvancedAIEnabled)
|
||||
{
|
||||
openAIProvider.EnableAdvancedAI = legacyAdvancedAIEnabled;
|
||||
configurationUpdated = true;
|
||||
}
|
||||
|
||||
if (legacyCredential is not null && openAIProvider is not null)
|
||||
{
|
||||
StoreMigratedOpenAICredential(openAIProvider.Id, openAIProvider.ServiceType, legacyCredential.Password);
|
||||
RemoveLegacyOpenAICredential();
|
||||
}
|
||||
|
||||
bool enabledUpdated = false;
|
||||
if (!properties.IsAIEnabled && legacyCredential is not null)
|
||||
{
|
||||
properties.IsAIEnabled = true;
|
||||
enabledUpdated = true;
|
||||
}
|
||||
|
||||
return configurationUpdated || enabledUpdated || legacyAdvancedAIConsumed;
|
||||
}
|
||||
|
||||
private static bool LegacyOpenAIKeyExists()
|
||||
private static PasswordCredential TryGetLegacyOpenAICredential()
|
||||
{
|
||||
try
|
||||
{
|
||||
PasswordVault vault = new();
|
||||
return vault.Retrieve("https://platform.openai.com/api-keys", "PowerToys_AdvancedPaste_OpenAIKey") is not null;
|
||||
var credential = vault.Retrieve("https://platform.openai.com/api-keys", "PowerToys_AdvancedPaste_OpenAIKey");
|
||||
credential?.RetrievePassword();
|
||||
return credential;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void RemoveLegacyOpenAICredential()
|
||||
{
|
||||
try
|
||||
{
|
||||
PasswordVault vault = new();
|
||||
TryRemoveCredential(vault, "https://platform.openai.com/api-keys", "PowerToys_AdvancedPaste_OpenAIKey");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private static void StoreMigratedOpenAICredential(string providerId, string serviceType, string password)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(password))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var serviceKind = serviceType.ToAIServiceType();
|
||||
if (serviceKind != AIServiceType.OpenAI)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string resource = "https://platform.openai.com/api-keys";
|
||||
string username = $"PowerToys_AdvancedPaste_PasteAI_openai_{NormalizeProviderIdentifier(providerId)}";
|
||||
|
||||
PasswordVault vault = new();
|
||||
TryRemoveCredential(vault, resource, username);
|
||||
|
||||
PasswordCredential credential = new(resource, username, password);
|
||||
vault.Add(credential);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Failed to migrate legacy OpenAI credential", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryRemoveCredential(PasswordVault vault, string credentialResource, string credentialUserName)
|
||||
{
|
||||
try
|
||||
{
|
||||
PasswordCredential existingCred = vault.Retrieve(credentialResource, credentialUserName);
|
||||
vault.Remove(existingCred);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Credential doesn't exist, which is fine
|
||||
}
|
||||
}
|
||||
|
||||
private static string NormalizeProviderIdentifier(string providerId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(providerId))
|
||||
{
|
||||
return "default";
|
||||
}
|
||||
|
||||
var filtered = new string(providerId.Where(char.IsLetterOrDigit).ToArray());
|
||||
return string.IsNullOrWhiteSpace(filtered) ? "default" : filtered.ToLowerInvariant();
|
||||
}
|
||||
|
||||
public async Task SetActiveAIProviderAsync(string providerId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(providerId))
|
||||
|
||||
@@ -196,10 +196,10 @@ public:
|
||||
m_enabled = true;
|
||||
Trace::EnableCursorWrap(true);
|
||||
|
||||
if (m_autoActivate)
|
||||
{
|
||||
StartMouseHook();
|
||||
}
|
||||
// Always start the mouse hook when the module is enabled
|
||||
// This ensures cursor wrapping is active immediately after enabling
|
||||
StartMouseHook();
|
||||
Logger::info("CursorWrap enabled - mouse hook started");
|
||||
}
|
||||
|
||||
// Disable the powertoy
|
||||
@@ -208,6 +208,7 @@ public:
|
||||
m_enabled = false;
|
||||
Trace::EnableCursorWrap(false);
|
||||
StopMouseHook();
|
||||
Logger::info("CursorWrap disabled - mouse hook stopped");
|
||||
}
|
||||
|
||||
// Returns if the powertoys is enabled
|
||||
|
||||
@@ -1812,7 +1812,7 @@ INT_PTR CALLBACK OptionsTabProc( HWND hDlg, UINT message,
|
||||
// Check if GIF is selected by comparing the text
|
||||
bool isGifSelected = (wcscmp(selectedText, L"GIF") == 0);
|
||||
|
||||
// if gif is selected set the scaling to the g_recordScaleGIF value otherwise to the g_recordScaleMP4 value
|
||||
// If GIF is selected, set the scaling to the g_RecordScalingGIF value; otherwise to the g_RecordScalingMP4 value
|
||||
if (isGifSelected) {
|
||||
g_RecordScaling = g_RecordScalingGIF;
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 550 B After Width: | Height: | Size: 604 B |
Binary file not shown.
|
Before Width: | Height: | Size: 525 B After Width: | Height: | Size: 565 B |
@@ -0,0 +1,205 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper methods for migrating legacy Advanced Paste settings to the updated schema.
|
||||
/// </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)
|
||||
{
|
||||
return (null, false);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (existingProvider is null)
|
||||
{
|
||||
existingProvider = CreateProvider(normalizedServiceType, snapshot);
|
||||
configuration.Providers.Add(existingProvider);
|
||||
configurationUpdated = true;
|
||||
}
|
||||
else if (snapshot is not null)
|
||||
{
|
||||
configurationUpdated |= ApplySnapshot(existingProvider, snapshot);
|
||||
}
|
||||
|
||||
configurationUpdated |= EnsureActiveProviderIsValid(configuration, existingProvider);
|
||||
|
||||
return (existingProvider, configurationUpdated);
|
||||
}
|
||||
|
||||
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 = !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)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(configuration?.ActiveProviderId))
|
||||
{
|
||||
configuration.ActiveProviderId = string.Empty;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool updated = false;
|
||||
|
||||
var activeProvider = configuration.Providers.FirstOrDefault(provider => string.Equals(provider.Id, configuration.ActiveProviderId, StringComparison.OrdinalIgnoreCase));
|
||||
if (activeProvider is null)
|
||||
{
|
||||
activeProvider = preferredProvider ?? configuration.Providers.First();
|
||||
configuration.ActiveProviderId = activeProvider.Id;
|
||||
updated = true;
|
||||
}
|
||||
|
||||
foreach (var provider in configuration.Providers)
|
||||
{
|
||||
bool shouldBeActive = string.Equals(provider.Id, configuration.ActiveProviderId, StringComparison.OrdinalIgnoreCase);
|
||||
if (provider.IsActive != shouldBeActive)
|
||||
{
|
||||
provider.IsActive = shouldBeActive;
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
return updated;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,10 +52,68 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
|
||||
_extensionData.Remove("IsOpenAIEnabled");
|
||||
}
|
||||
|
||||
if (_extensionData != null && _extensionData.TryGetValue("IsAdvancedAIEnabled", out var legacyAdvancedElement))
|
||||
{
|
||||
bool? legacyValue = legacyAdvancedElement.ValueKind switch
|
||||
{
|
||||
JsonValueKind.True => true,
|
||||
JsonValueKind.False => false,
|
||||
JsonValueKind.Object when legacyAdvancedElement.TryGetProperty("value", out var advancedValueElement) => advancedValueElement.ValueKind switch
|
||||
{
|
||||
JsonValueKind.True => true,
|
||||
JsonValueKind.False => false,
|
||||
_ => null,
|
||||
},
|
||||
_ => null,
|
||||
};
|
||||
|
||||
if (legacyValue.HasValue)
|
||||
{
|
||||
LegacyAdvancedAIEnabled = legacyValue.Value;
|
||||
}
|
||||
|
||||
_extensionData.Remove("IsAdvancedAIEnabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<string, JsonElement> _extensionData;
|
||||
private bool? _legacyAdvancedAIEnabled;
|
||||
|
||||
[JsonPropertyName("IsAdvancedAIEnabled")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public BoolProperty LegacyAdvancedAIEnabledProperty
|
||||
{
|
||||
get => null;
|
||||
set
|
||||
{
|
||||
if (value is not null)
|
||||
{
|
||||
LegacyAdvancedAIEnabled = value.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public bool? LegacyAdvancedAIEnabled
|
||||
{
|
||||
get => _legacyAdvancedAIEnabled;
|
||||
private set => _legacyAdvancedAIEnabled = value;
|
||||
}
|
||||
|
||||
public bool TryConsumeLegacyAdvancedAIEnabled(out bool value)
|
||||
{
|
||||
if (_legacyAdvancedAIEnabled is bool flag)
|
||||
{
|
||||
value = flag;
|
||||
_legacyAdvancedAIEnabled = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(BoolPropertyJsonConverter))]
|
||||
public bool ShowCustomPreview { get; set; }
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides default values for Paste AI provider definitions.
|
||||
/// </summary>
|
||||
public static class PasteAIProviderDefaults
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the default model name for a given AI service type.
|
||||
/// </summary>
|
||||
public static string GetDefaultModelName(AIServiceType serviceType)
|
||||
{
|
||||
return serviceType switch
|
||||
{
|
||||
AIServiceType.OpenAI => "gpt-4o",
|
||||
AIServiceType.AzureOpenAI => "gpt-4o",
|
||||
AIServiceType.Mistral => "mistral-large-latest",
|
||||
AIServiceType.Google => "gemini-1.5-pro",
|
||||
AIServiceType.AzureAIInference => "gpt-4o-mini",
|
||||
AIServiceType.Ollama => "llama3",
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,8 +42,9 @@
|
||||
HorizontalAlignment="Center" />
|
||||
<TextBlock
|
||||
x:Name="LoadingStatusTextBlock"
|
||||
x:Uid="AdvancedPaste_FL_LoadingStatus"
|
||||
HorizontalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="Loading Foundry Local status..."
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
@@ -56,7 +57,6 @@
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel
|
||||
x:Name="NoModelsPanel"
|
||||
Grid.Row="0"
|
||||
@@ -64,24 +64,28 @@
|
||||
HorizontalAlignment="Center"
|
||||
Orientation="Vertical"
|
||||
Spacing="4">
|
||||
<FontIcon FontSize="24" Glyph="" />
|
||||
<FontIcon
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
FontSize="24"
|
||||
Glyph="" />
|
||||
<TextBlock
|
||||
x:Uid="AdvancedPaste_FL_NoModelsDownloaded."
|
||||
HorizontalAlignment="Center"
|
||||
Style="{StaticResource BodyStrongTextBlockStyle}"
|
||||
Text="No models downloaded"
|
||||
TextAlignment="Center" />
|
||||
<TextBlock
|
||||
x:Uid="AdvancedPaste_FL_RunFoundryLocalText.Text"
|
||||
HorizontalAlignment="Center"
|
||||
FontSize="12"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="Run Foundry Local to download or add a local model below."
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
<Button
|
||||
x:Name="LaunchFoundryModelListButton"
|
||||
x:Uid="AdvancedPaste_FL_OpenFoundryModelList"
|
||||
Margin="0,8,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
Click="LaunchFoundryModelListButton_Click"
|
||||
Content="Open Foundry model list"
|
||||
Style="{StaticResource AccentButtonStyle}" />
|
||||
</StackPanel>
|
||||
|
||||
@@ -101,10 +105,10 @@
|
||||
SelectionChanged="CachedModelsComboBox_SelectionChanged">
|
||||
<ComboBox.Header>
|
||||
<TextBlock>
|
||||
<Run Text="Foundry Local model" /><LineBreak /><Run
|
||||
<Run x:Uid="AdvancedPaste_FL_LocalModel" /><LineBreak /><Run
|
||||
x:Uid="AdvancedPaste_FL_UseCLIToDownloadModels"
|
||||
FontSize="12"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="Use the Foundry Local CLI to download models that run locally on-device. They'll appear here." />
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
</TextBlock>
|
||||
</ComboBox.Header>
|
||||
</ComboBox>
|
||||
@@ -114,9 +118,11 @@
|
||||
MinHeight="32"
|
||||
VerticalAlignment="Bottom"
|
||||
Click="RefreshModelsButton_Click"
|
||||
Style="{StaticResource SubtleButtonStyle}"
|
||||
ToolTipService.ToolTip="Refresh model list">
|
||||
Style="{StaticResource SubtleButtonStyle}">
|
||||
<FontIcon FontSize="16" Glyph="" />
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock x:Uid="AdvancedPaste_FL_RefreshModelList" />
|
||||
</ToolTipService.ToolTip>
|
||||
</Button>
|
||||
</Grid>
|
||||
<StackPanel
|
||||
@@ -146,24 +152,28 @@
|
||||
Spacing="8">
|
||||
<Image Width="36" Source="ms-appx:///Assets/Settings/Icons/Models/FoundryLocal.svg" />
|
||||
<TextBlock
|
||||
x:Uid="AdvancedPaste_FL_FLNotavailableYet"
|
||||
HorizontalAlignment="Center"
|
||||
FontWeight="SemiBold"
|
||||
Text="Foundry Local is not available on this device yet."
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock
|
||||
x:Uid="AdvancedPaste_FL_StartService"
|
||||
HorizontalAlignment="Center"
|
||||
FontSize="12"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
IsTextSelectionEnabled="True"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap">
|
||||
<Run Text="Start the Foundry Local service before returning to PowerToys." />
|
||||
</TextBlock>
|
||||
<HyperlinkButton Content="Follow the Foundry Local CLI guide" NavigateUri="https://learn.microsoft.com/en-us/azure/ai-foundry/foundry-local/get-started" />
|
||||
TextWrapping="Wrap" />
|
||||
<HyperlinkButton
|
||||
x:Uid="AdvancedPaste_FL_CLIGuide"
|
||||
HorizontalAlignment="Center"
|
||||
NavigateUri="https://learn.microsoft.com/azure/ai-foundry/foundry-local/get-started" />
|
||||
<TextBlock
|
||||
x:Uid="FoundryLocal_RestartRequiredNote"
|
||||
HorizontalAlignment="Center"
|
||||
FontSize="12"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="Note: After installing the Foundry Local CLI, restart PowerToys to use it."
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
|
||||
@@ -103,10 +103,7 @@
|
||||
</StackPanel>
|
||||
</tkcontrols:SettingsExpander.Description>
|
||||
<tkcontrols:SettingsExpander.ItemsHeader>
|
||||
<tkcontrols:SettingsCard
|
||||
Description="Add online or local models"
|
||||
Header="Model providers"
|
||||
Style="{StaticResource DefaultSettingsExpanderItemStyle}">
|
||||
<tkcontrols:SettingsCard x:Uid="AdvancedPaste_ModelProviders" Style="{StaticResource DefaultSettingsExpanderItemStyle}">
|
||||
<Button Content="Add model" Style="{StaticResource AccentButtonStyle}">
|
||||
<Button.Flyout>
|
||||
<MenuFlyout x:Name="AddProviderMenuFlyout" Opening="AddProviderMenuFlyout_Opening" />
|
||||
@@ -130,16 +127,16 @@
|
||||
<Button.Flyout>
|
||||
<MenuFlyout>
|
||||
<MenuFlyoutItem
|
||||
x:Uid="AdvancedPaste_Edit"
|
||||
Click="EditPasteAIProviderButton_Click"
|
||||
Icon="{ui:FontIcon Glyph=}"
|
||||
Tag="{x:Bind}"
|
||||
Text="Edit" />
|
||||
Tag="{x:Bind}" />
|
||||
<MenuFlyoutSeparator />
|
||||
<MenuFlyoutItem
|
||||
x:Uid="AdvancedPaste_Remove"
|
||||
Click="RemovePasteAIProviderButton_Click"
|
||||
Icon="{ui:FontIcon Glyph=}"
|
||||
Tag="{x:Bind}"
|
||||
Text="Remove" />
|
||||
Tag="{x:Bind}" />
|
||||
</MenuFlyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
@@ -496,40 +493,39 @@
|
||||
Spacing="16">
|
||||
<TextBox
|
||||
x:Name="PasteAIModelNameTextBox"
|
||||
x:Uid="AdvancedPaste_ModelName"
|
||||
MinWidth="200"
|
||||
HorizontalAlignment="Stretch"
|
||||
Header="Model name"
|
||||
PlaceholderText="gpt-4"
|
||||
PlaceholderText="gpt-4o"
|
||||
Text="{x:Bind ViewModel.PasteAIProviderDraft.ModelName, Mode=TwoWay}" />
|
||||
<TextBox
|
||||
x:Name="PasteAIEndpointUrlTextBox"
|
||||
x:Uid="AdvancedPaste_EndpointURL"
|
||||
MinWidth="200"
|
||||
HorizontalAlignment="Stretch"
|
||||
Header="Endpoint URL"
|
||||
PlaceholderText="https://your-resource.openai.azure.com/"
|
||||
Text="{x:Bind ViewModel.PasteAIProviderDraft.EndpointUrl, Mode=TwoWay}" />
|
||||
|
||||
<PasswordBox
|
||||
x:Name="PasteAIApiKeyPasswordBox"
|
||||
MinWidth="200"
|
||||
Header="API key"
|
||||
PlaceholderText="Enter API Key" />
|
||||
x:Uid="AdvancedPaste_APIKey"
|
||||
MinWidth="200" />
|
||||
<TextBox
|
||||
x:Name="PasteAIApiVersionTextBox"
|
||||
x:Uid="AdvancedPaste_APIVersion"
|
||||
MinWidth="200"
|
||||
HorizontalAlignment="Stretch"
|
||||
Header="API version"
|
||||
PlaceholderText="2024-10-01"
|
||||
Text="{x:Bind ViewModel.PasteAIProviderDraft.ApiVersion, Mode=TwoWay}"
|
||||
Visibility="Collapsed" />
|
||||
<TextBox
|
||||
x:Name="PasteAIDeploymentNameTextBox"
|
||||
x:Uid="AdvancedPaste_DeploymentName"
|
||||
MinWidth="200"
|
||||
Header="Deployment name"
|
||||
PlaceholderText="gpt-4"
|
||||
PlaceholderText="gpt-4o"
|
||||
Text="{x:Bind ViewModel.PasteAIProviderDraft.DeploymentName, Mode=TwoWay}" />
|
||||
<TextBox
|
||||
x:Name="PasteAISystemPromptTextBox"
|
||||
x:Uid="AdvancedPaste_SystemPrompt"
|
||||
MinWidth="200"
|
||||
MinHeight="76"
|
||||
HorizontalAlignment="Stretch"
|
||||
@@ -574,13 +570,10 @@
|
||||
IsOn="{x:Bind ViewModel.PasteAIProviderDraft.EnableAdvancedAI, Mode=TwoWay}"
|
||||
Toggled="PasteAIEnableAdvancedAICheckBox_Toggled"
|
||||
Visibility="Collapsed">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock Text="" TextWrapping="Wrap" />
|
||||
</ToolTipService.ToolTip>
|
||||
<ToggleSwitch.Header>
|
||||
<TextBlock>
|
||||
<Run Text="Enable Advanced AI" /> <LineBreak />
|
||||
<Run Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="Use built-in functions to handle complex tasks. Token consumption may increase." />
|
||||
<Run x:Uid="AdvancedPaste_EnableAdvancedAI" /> <LineBreak />
|
||||
<Run x:Uid="AdvancedPaste_EnableAdvancedAIDescription" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
</TextBlock>
|
||||
</ToggleSwitch.Header>
|
||||
</ToggleSwitch>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
@@ -26,36 +26,36 @@
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
@@ -644,14 +644,11 @@ Please review the placeholder content that represents the final terms and usage
|
||||
<data name="AdvancedPaste_EnablePasteAIModerationToggle.Header" xml:space="preserve">
|
||||
<value>Enable OpenAI content moderation</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_EnableAdvancedAI_SettingsCard.Header" xml:space="preserve">
|
||||
<value>Enable Advanced AI</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_EnableAdvancedAI_SettingsCard.Description" xml:space="preserve">
|
||||
<data name="AdvancedPaste_EnableAdvancedAIDescription.Text" xml:space="preserve">
|
||||
<value>Use built-in functions to handle complex tasks. Token consumption may increase.</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_Clipboard_History_Enabled_SettingsCard.Header" xml:space="preserve">
|
||||
<value>Access Clipboard History</value>
|
||||
<value>Show what's currently on your Clipboard and access your Clipboard history</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_Clipboard_History_Enabled_SettingsCard.Description" xml:space="preserve">
|
||||
<value>Clipboard History shows a list of previously copied items.</value>
|
||||
@@ -4058,11 +4055,8 @@ Activate by holding the key for the character you want to add an accent to, then
|
||||
<data name="AdvancedPaste_ShowCustomPreviewSettingsCard.Description" xml:space="preserve">
|
||||
<value>Preview the output of AI formats and Image to text before pasting</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_EnableAdvancedAI.Header" xml:space="preserve">
|
||||
<value>Advanced AI</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_EnableAdvancedAI.Description" xml:space="preserve">
|
||||
<value>Supports advanced workflows by chaining transformations and working with files and images. May use additional API credits.</value>
|
||||
<data name="AdvancedPaste_EnableAdvancedAI.Text" xml:space="preserve">
|
||||
<value>Enable Advanced AI</value>
|
||||
</data>
|
||||
<data name="Oobe_AdvancedPaste.Description" xml:space="preserve">
|
||||
<value>Advanced Paste is a tool to put your clipboard content into any format you need, focused towards developer workflows. It can paste as plain text, markdown, or json directly with the UX or with a direct keystroke invoke. These are fully locally executed. In addition, it has an AI powered option that is 100% opt-in and requires an Open AI key. Note: this will replace the formatted text in your clipboard with the selected format.</value>
|
||||
@@ -4604,7 +4598,7 @@ Activate by holding the key for the character you want to add an accent to, then
|
||||
<value>If you do not have credits you will see an 'API key quota exceeded' error</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_CloseAfterLosingFocus.Content" xml:space="preserve">
|
||||
<value>Automatically close the Advanced Paste window after it loses focus</value>
|
||||
<value>Automatically close the window after it loses focus</value>
|
||||
<comment>Advanced Paste is a product name, do not loc</comment>
|
||||
</data>
|
||||
<data name="GPO_CommandNotFound_ForceDisabled.Title" xml:space="preserve">
|
||||
@@ -5679,4 +5673,74 @@ To record a specific window, enter the hotkey with the Alt key in the opposite m
|
||||
<data name="LightSwitch_SetLocationButton.Content" xml:space="preserve">
|
||||
<value>Set Location</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_FL_OpenFoundryModelList.Content" xml:space="preserve">
|
||||
<value>Open Foundry Local model list</value>
|
||||
<comment>Do not localize "Foundry Local", it's a product name</comment>
|
||||
</data>
|
||||
<data name="AdvancedPaste_FL_RunFoundryLocalText.Text" xml:space="preserve">
|
||||
<value>Run Foundry Local to download or add a local model</value>
|
||||
<comment>Do not localize "Foundry Local", it's a product name</comment>
|
||||
</data>
|
||||
<data name="AdvancedPaste_FL_NoModelsDownloaded.Text" xml:space="preserve">
|
||||
<value>No models downloaded</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_FL_LoadingStatus.Text" xml:space="preserve">
|
||||
<value>Loading Foundry Local status..</value>
|
||||
<comment>Do not localize "Foundry Local", it's a product name</comment>
|
||||
</data>
|
||||
<data name="AdvancedPaste_FL_LocalModel.Text" xml:space="preserve">
|
||||
<value>Foundry Local model</value>
|
||||
<comment>Do not localize "Foundry Local", it's a product name</comment>
|
||||
</data>
|
||||
<data name="AdvancedPaste_FL_UseCLIToDownloadModels.Text" xml:space="preserve">
|
||||
<value>Use the Foundry Local CLI to download models that run locally on-device. They'll appear here.</value>
|
||||
<comment>Do not localize "Foundry Local", it's a product name</comment>
|
||||
</data>
|
||||
<data name="AdvancedPaste_FL_RefreshModelList.Text" xml:space="preserve">
|
||||
<value>Refresh model list</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_FL_FLNotavailableYet.Text" xml:space="preserve">
|
||||
<value>Foundry Local is not available on this device yet.</value>
|
||||
<comment>Do not localize "Foundry Local", it's a product name</comment>
|
||||
</data>
|
||||
<data name="AdvancedPaste_FL_StartService.Text" xml:space="preserve">
|
||||
<value>Start the Foundry Local service before returning to PowerToys.</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_FL_CLIGuide.Content" xml:space="preserve">
|
||||
<value>Follow the Foundry Local CLI guide</value>
|
||||
<comment>Do not localize "Foundry Local", it's a product name</comment>
|
||||
</data>
|
||||
<data name="AdvancedPaste_ModelProviders.Header" xml:space="preserve">
|
||||
<value>Model providers</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_ModelProviders.Description" xml:space="preserve">
|
||||
<value>Add online or local models</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_Edit.Text" xml:space="preserve">
|
||||
<value>Edit</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_Remove.Text" xml:space="preserve">
|
||||
<value>Remove</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_ModelName.Header" xml:space="preserve">
|
||||
<value>Model name</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_EndpointURL.Header" xml:space="preserve">
|
||||
<value>Endpoint URL</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_APIKey.Header" xml:space="preserve">
|
||||
<value>API key</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_APIKey.PlaceholderText" xml:space="preserve">
|
||||
<value>Enter API key</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_APIVersion.Header" xml:space="preserve">
|
||||
<value>API version</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_DeploymentName.Header" xml:space="preserve">
|
||||
<value>Deployment name</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_SystemPrompt.Header" xml:space="preserve">
|
||||
<value>System prompt</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -172,19 +172,90 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
|
||||
private void MigrateLegacyAIEnablement()
|
||||
{
|
||||
if (_advancedPasteSettings.Properties.IsAIEnabled || IsOnlineAIModelsDisallowedByGPO)
|
||||
var properties = _advancedPasteSettings?.Properties;
|
||||
if (properties is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!LegacyOpenAIKeyExists())
|
||||
bool legacyAdvancedAIConsumed = properties.TryConsumeLegacyAdvancedAIEnabled(out var advancedFlag);
|
||||
bool legacyAdvancedAIEnabled = legacyAdvancedAIConsumed && advancedFlag;
|
||||
|
||||
if (IsOnlineAIModelsDisallowedByGPO)
|
||||
{
|
||||
if (legacyAdvancedAIConsumed)
|
||||
{
|
||||
SaveAndNotifySettings();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var configuration = properties.PasteAIConfiguration;
|
||||
if (configuration is null)
|
||||
{
|
||||
configuration = new PasteAIConfiguration();
|
||||
properties.PasteAIConfiguration = configuration;
|
||||
}
|
||||
|
||||
bool hasLegacyProviders = configuration.LegacyProviderConfigurations is { Count: > 0 };
|
||||
PasswordCredential legacyCredential = TryGetLegacyOpenAICredential();
|
||||
|
||||
if (!hasLegacyProviders && legacyCredential is null && !legacyAdvancedAIConsumed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_advancedPasteSettings.Properties.IsAIEnabled = true;
|
||||
SaveAndNotifySettings();
|
||||
OnPropertyChanged(nameof(IsAIEnabled));
|
||||
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;
|
||||
}
|
||||
|
||||
if (legacyAdvancedAIConsumed && openAIProvider is not null && openAIProvider.EnableAdvancedAI != legacyAdvancedAIEnabled)
|
||||
{
|
||||
openAIProvider.EnableAdvancedAI = legacyAdvancedAIEnabled;
|
||||
configurationUpdated = true;
|
||||
}
|
||||
|
||||
if (legacyCredential is not null && openAIProvider is not null)
|
||||
{
|
||||
SavePasteAIApiKey(openAIProvider.Id, openAIProvider.ServiceType, legacyCredential.Password);
|
||||
RemoveLegacyOpenAICredential();
|
||||
}
|
||||
|
||||
bool enabledChanged = false;
|
||||
if (!properties.IsAIEnabled && legacyCredential is not null)
|
||||
{
|
||||
properties.IsAIEnabled = true;
|
||||
enabledChanged = true;
|
||||
}
|
||||
|
||||
bool shouldPersist = configurationUpdated || enabledChanged || legacyAdvancedAIConsumed;
|
||||
|
||||
if (shouldPersist)
|
||||
{
|
||||
SaveAndNotifySettings();
|
||||
|
||||
if (configurationUpdated)
|
||||
{
|
||||
OnPropertyChanged(nameof(PasteAIConfiguration));
|
||||
}
|
||||
|
||||
if (enabledChanged)
|
||||
{
|
||||
OnPropertyChanged(nameof(IsAIEnabled));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsEnabled
|
||||
@@ -229,34 +300,30 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
|
||||
public bool IsAIEnabled => _advancedPasteSettings.Properties.IsAIEnabled && !IsOnlineAIModelsDisallowedByGPO;
|
||||
|
||||
private bool LegacyOpenAIKeyExists()
|
||||
private PasswordCredential TryGetLegacyOpenAICredential()
|
||||
{
|
||||
try
|
||||
{
|
||||
PasswordVault vault = new();
|
||||
|
||||
// return vault.Retrieve("https://platform.openai.com/api-keys", "PowerToys_AdvancedPaste_OpenAIKey") is not null;
|
||||
var legacyOpenAIKey = vault.Retrieve("https://platform.openai.com/api-keys", "PowerToys_AdvancedPaste_OpenAIKey");
|
||||
if (legacyOpenAIKey != null)
|
||||
{
|
||||
string credentialResource = GetAICredentialResource("OpenAI");
|
||||
var targetProvider = PasteAIConfiguration?.ActiveProvider ?? PasteAIConfiguration?.Providers?.FirstOrDefault();
|
||||
string providerId = targetProvider?.Id ?? string.Empty;
|
||||
string serviceType = targetProvider?.ServiceType ?? "OpenAI";
|
||||
string credentialUserName = GetPasteAICredentialUserName(providerId, serviceType);
|
||||
PasswordCredential cred = new(credentialResource, credentialUserName, legacyOpenAIKey.Password);
|
||||
vault.Add(cred);
|
||||
|
||||
// delete old key
|
||||
TryRemoveCredential(vault, "https://platform.openai.com/api-keys", "PowerToys_AdvancedPaste_OpenAIKey");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
var credential = vault.Retrieve("https://platform.openai.com/api-keys", "PowerToys_AdvancedPaste_OpenAIKey");
|
||||
credential?.RetrievePassword();
|
||||
return credential;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveLegacyOpenAICredential()
|
||||
{
|
||||
try
|
||||
{
|
||||
PasswordVault vault = new();
|
||||
TryRemoveCredential(vault, "https://platform.openai.com/api-keys", "PowerToys_AdvancedPaste_OpenAIKey");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -519,7 +586,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
var provider = new PasteAIProviderDefinition
|
||||
{
|
||||
ServiceType = persistedServiceType,
|
||||
ModelName = GetDefaultModelName(normalizedServiceType),
|
||||
ModelName = PasteAIProviderDefaults.GetDefaultModelName(normalizedServiceType),
|
||||
EndpointUrl = string.Empty,
|
||||
ApiVersion = string.Empty,
|
||||
DeploymentName = string.Empty,
|
||||
@@ -559,20 +626,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
return serviceTypeKind;
|
||||
}
|
||||
|
||||
private static string GetDefaultModelName(AIServiceType serviceType)
|
||||
{
|
||||
return serviceType switch
|
||||
{
|
||||
AIServiceType.OpenAI => "gpt-4",
|
||||
AIServiceType.AzureOpenAI => "gpt-4",
|
||||
AIServiceType.Mistral => "mistral-large-latest",
|
||||
AIServiceType.Google => "gemini-2.5-pro",
|
||||
AIServiceType.AzureAIInference => "gpt-4o-mini",
|
||||
AIServiceType.Ollama => "llama3",
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
|
||||
public bool IsServiceTypeAllowedByGPO(AIServiceType serviceType)
|
||||
{
|
||||
var metadata = AIServiceTypeRegistry.GetMetadata(serviceType);
|
||||
@@ -1352,7 +1405,16 @@ 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)
|
||||
|
||||
@@ -1000,6 +1000,13 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
GeneralSettingsConfig.Enabled.CursorWrap = value;
|
||||
OnPropertyChanged(nameof(IsCursorWrapEnabled));
|
||||
|
||||
// Auto-enable the AutoActivate setting when CursorWrap is enabled
|
||||
// This ensures cursor wrapping is active immediately after enabling
|
||||
if (value && !_cursorWrapAutoActivate)
|
||||
{
|
||||
CursorWrapAutoActivate = true;
|
||||
}
|
||||
|
||||
OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(GeneralSettingsConfig);
|
||||
SendConfigMSG(outgoing.ToString());
|
||||
|
||||
|
||||
@@ -24,6 +24,9 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
{
|
||||
public class ZoomItViewModel : Observable
|
||||
{
|
||||
private const string FormatGif = "GIF";
|
||||
private const string FormatMp4 = "MP4";
|
||||
|
||||
private ISettingsUtils SettingsUtils { get; set; }
|
||||
|
||||
private GeneralSettings GeneralSettingsConfig { get; set; }
|
||||
@@ -656,12 +659,12 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_zoomItSettings.Properties.RecordFormat.Value == "GIF")
|
||||
if (_zoomItSettings.Properties.RecordFormat.Value == FormatGif)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (_zoomItSettings.Properties.RecordFormat.Value == "MP4")
|
||||
if (_zoomItSettings.Properties.RecordFormat.Value == FormatMp4)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
@@ -672,19 +675,19 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
set
|
||||
{
|
||||
int format = 0;
|
||||
if (_zoomItSettings.Properties.RecordFormat.Value == "GIF")
|
||||
if (_zoomItSettings.Properties.RecordFormat.Value == FormatGif)
|
||||
{
|
||||
format = 0;
|
||||
}
|
||||
|
||||
if (_zoomItSettings.Properties.RecordFormat.Value == "MP4")
|
||||
if (_zoomItSettings.Properties.RecordFormat.Value == FormatMp4)
|
||||
{
|
||||
format = 1;
|
||||
}
|
||||
|
||||
if (format != value)
|
||||
{
|
||||
_zoomItSettings.Properties.RecordFormat.Value = value == 0 ? "GIF" : "MP4";
|
||||
_zoomItSettings.Properties.RecordFormat.Value = value == 0 ? FormatGif : FormatMp4;
|
||||
OnPropertyChanged(nameof(RecordFormatIndex));
|
||||
NotifySettingsChanged();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user