Compare commits

...

11 Commits

Author SHA1 Message Date
Shawn Yuan (from Dev Box)
2128505de8 fix xaml format 2025-12-24 15:23:50 +08:00
Shawn Yuan (from Dev Box)
0f4ead7069 update 2025-12-24 15:22:51 +08:00
Shawn Yuan (from Dev Box)
3749f3e87d update 2025-12-24 14:14:05 +08:00
Shawn Yuan (from Dev Box)
d341bd2ca6 update localization 2025-12-24 11:36:10 +08:00
Shawn Yuan (from Dev Box)
20dcb6fb47 add localization support 2025-12-24 11:17:17 +08:00
Shawn Yuan (from Dev Box)
72f84f9652 add model usage tag to ui 2025-12-24 11:01:01 +08:00
Shawn Yuan (from Dev Box)
64dafff7c4 add img size config 2025-12-24 10:20:49 +08:00
Shawn Yuan (from Dev Box)
927d190cf2 fix merge coflicts 2025-12-23 17:40:55 +08:00
Shawn Yuan (from Dev Box)
667800eb86 fix merge conflict 2025-12-23 17:16:43 +08:00
Shawn Yuan (from Dev Box)
35cab47465 Merge branch 'main' into shawn/APImprove2 2025-12-23 17:15:44 +08:00
Shawn Yuan (from Dev Box)
c1603b189f init 2025-12-23 17:12:17 +08:00
17 changed files with 462 additions and 42 deletions

View File

@@ -335,6 +335,7 @@
<converters:CountToVisibilityConverter x:Key="CountToVisibilityConverter" />
<converters:CountToInvertedVisibilityConverter x:Key="CountToInvertedVisibilityConverter" />
<converters:ServiceTypeToIconConverter x:Key="ServiceTypeToIconConverter" />
<converters:PasteAIUsageToStringConverter x:Key="PasteAIUsageToStringConverter" />
</ResourceDictionary>
</UserControl.Resources>
<Grid x:Name="PromptBoxGrid" Loaded="Grid_Loaded">
@@ -430,12 +431,20 @@
Grid.Row="1"
MinHeight="104"
MaxHeight="320">
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
IsTextSelectionEnabled="True"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind ViewModel.CustomFormatResult, Mode=OneWay}"
TextWrapping="Wrap" />
<StackPanel>
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
IsTextSelectionEnabled="True"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind ViewModel.CustomFormatResult, Mode=OneWay}"
TextWrapping="Wrap"
Visibility="{x:Bind ViewModel.HasCustomFormatText, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
<Image
HorizontalAlignment="Left"
Source="{x:Bind ViewModel.CustomFormatImageResult, Mode=OneWay}"
Stretch="Uniform"
Visibility="{x:Bind ViewModel.HasCustomFormatImage, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
</StackPanel>
</ScrollViewer>
</Grid>
<Rectangle
@@ -602,20 +611,37 @@
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind ServiceType, Mode=OneWay}" />
</StackPanel>
<Border
<StackPanel
Grid.Column="2"
Padding="2,0,2,0"
VerticalAlignment="Center"
BorderBrush="{ThemeResource ControlStrokeColorSecondary}"
BorderThickness="1"
CornerRadius="{StaticResource ControlCornerRadius}"
Visibility="{x:Bind IsLocalModel, Mode=OneWay}">
<TextBlock
x:Uid="LocalModelBadge"
AutomationProperties.AccessibilityView="Raw"
FontSize="10"
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
</Border>
Orientation="Horizontal"
Spacing="4">
<Border
Padding="2,0,2,0"
VerticalAlignment="Center"
BorderBrush="{ThemeResource ControlStrokeColorSecondary}"
BorderThickness="1"
CornerRadius="{StaticResource ControlCornerRadius}">
<TextBlock
AutomationProperties.AccessibilityView="Raw"
FontSize="10"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{x:Bind Usage, Mode=OneWay, Converter={StaticResource PasteAIUsageToStringConverter}}" />
</Border>
<Border
Padding="2,0,2,0"
VerticalAlignment="Center"
BorderBrush="{ThemeResource ControlStrokeColorSecondary}"
BorderThickness="1"
CornerRadius="{StaticResource ControlCornerRadius}"
Visibility="{x:Bind IsLocalModel, Mode=OneWay}">
<TextBlock
x:Uid="LocalModelBadge"
AutomationProperties.AccessibilityView="Raw"
FontSize="10"
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
</Border>
</StackPanel>
<!--<Border
Grid.Column="2"
Padding="2,0,2,0"

View File

@@ -164,7 +164,7 @@ namespace AdvancedPaste.Controls
return;
}
var flyout = FlyoutBase.GetAttachedFlyout(AIProviderButton);
var flyout = AIProviderButton.Flyout;
if (AIProviderListView.SelectedItem is not PasteAIProviderDefinition provider)
{
@@ -180,7 +180,6 @@ namespace AdvancedPaste.Controls
if (ViewModel.SetActiveProviderCommand.CanExecute(provider))
{
await ViewModel.SetActiveProviderCommand.ExecuteAsync(provider);
SyncProviderSelection();
}
flyout?.Hide();

View File

@@ -0,0 +1,30 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using AdvancedPaste.Helpers;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.UI.Xaml.Data;
namespace AdvancedPaste.Converters;
public sealed partial class PasteAIUsageToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var usage = value switch
{
string s => PasteAIUsageExtensions.FromConfigString(s),
PasteAIUsage u => u,
_ => PasteAIUsage.ChatCompletion,
};
return ResourceLoaderInstance.ResourceLoader.GetString($"PasteAIUsage_{usage}");
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}

View File

@@ -46,6 +46,13 @@ internal static class DataPackageHelpers
return dataPackage;
}
internal static DataPackage CreateFromImage(RandomAccessStreamReference imageStreamRef)
{
DataPackage dataPackage = new();
dataPackage.SetBitmap(imageStreamRef);
return dataPackage;
}
internal static async Task<DataPackage> CreateFromFileAsync(string fileName)
{
var storageFile = await StorageFile.GetFileFromPathAsync(fileName);

View File

@@ -168,6 +168,9 @@ namespace AdvancedPaste.Services.CustomActions
ModelPath = provider.ModelPath,
SystemPrompt = systemPrompt,
ModerationEnabled = provider.ModerationEnabled,
Usage = provider.UsageKind,
ImageWidth = provider.ImageWidth,
ImageHeight = provider.ImageHeight,
};
return providerConfig;

View File

@@ -28,5 +28,11 @@ namespace AdvancedPaste.Services.CustomActions
public string SystemPrompt { get; set; }
public bool ModerationEnabled { get; set; }
public PasteAIUsage Usage { get; set; }
public int ImageWidth { get; set; }
public int ImageHeight { get; set; }
}
}

View File

@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using AdvancedPaste.Helpers;
@@ -16,6 +17,7 @@ using Microsoft.SemanticKernel.Connectors.Google;
using Microsoft.SemanticKernel.Connectors.MistralAI;
using Microsoft.SemanticKernel.Connectors.Ollama;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel.TextToImage;
namespace AdvancedPaste.Services.CustomActions
{
@@ -73,6 +75,69 @@ namespace AdvancedPaste.Services.CustomActions
var executionSettings = CreateExecutionSettings();
var kernel = CreateKernel();
switch (_config.Usage)
{
case PasteAIUsage.TextToImage:
var imageDescription = string.IsNullOrWhiteSpace(prompt) ? inputText : $"{inputText}. {prompt}";
return await ProcessTextToImageAsync(kernel, imageDescription, cancellationToken);
case PasteAIUsage.ChatCompletion:
default:
var userMessageContent = $"""
User instructions:
{prompt}
Clipboard Content:
{inputText}
Output:
""";
return await ProcessChatCompletionAsync(kernel, request, userMessageContent, systemPrompt, cancellationToken);
}
}
private async Task<string> ProcessTextToImageAsync(Kernel kernel, string userMessageContent, CancellationToken cancellationToken)
{
#pragma warning disable SKEXP0001
var imageService = kernel.GetRequiredService<ITextToImageService>();
var width = _config.ImageWidth > 0 ? _config.ImageWidth : 1024;
var height = _config.ImageHeight > 0 ? _config.ImageHeight : 1024;
var settings = new OpenAITextToImageExecutionSettings
{
Size = (width, height),
};
var generatedImages = await imageService.GetImageContentsAsync(new TextContent(userMessageContent), settings, cancellationToken: cancellationToken);
if (generatedImages.Count == 0)
{
throw new InvalidOperationException("No image generated.");
}
var imageContent = generatedImages[0];
if (imageContent.Data.HasValue)
{
var base64 = Convert.ToBase64String(imageContent.Data.Value.ToArray());
return $"data:{imageContent.MimeType ?? "image/png"};base64,{base64}";
}
else if (imageContent.Uri != null)
{
using var client = new HttpClient();
var imageBytes = await client.GetByteArrayAsync(imageContent.Uri, cancellationToken);
var base64 = Convert.ToBase64String(imageBytes);
return $"data:image/png;base64,{base64}";
}
else
{
throw new InvalidOperationException("Generated image contains no data.");
}
#pragma warning restore SKEXP0001
}
private async Task<string> ProcessChatCompletionAsync(Kernel kernel, PasteAIRequest request, string userMessageContent, string systemPrompt, CancellationToken cancellationToken)
{
var executionSettings = CreateExecutionSettings();
var modelId = _config.Model;
IChatCompletionService chatService;
@@ -95,29 +160,20 @@ namespace AdvancedPaste.Services.CustomActions
var chatHistory = new ChatHistory();
chatHistory.AddSystemMessage(systemPrompt);
if (imageBytes != null)
if (request.ImageBytes != null)
{
var collection = new ChatMessageContentItemCollection();
if (!string.IsNullOrWhiteSpace(inputText))
if (!string.IsNullOrWhiteSpace(request.InputText))
{
collection.Add(new TextContent($"Clipboard Content:\n{inputText}"));
collection.Add(new TextContent($"Clipboard Content:\n{request.InputText}"));
}
collection.Add(new ImageContent(imageBytes, request.ImageMimeType ?? "image/png"));
collection.Add(new TextContent($"User instructions:\n{prompt}\n\nOutput:"));
collection.Add(new ImageContent(request.ImageBytes, request.ImageMimeType ?? "image/png"));
collection.Add(new TextContent($"User instructions:\n{request.Prompt}\n\nOutput:"));
chatHistory.AddUserMessage(collection);
}
else
{
var userMessageContent = $"""
User instructions:
{prompt}
Clipboard Content:
{inputText}
Output:
""";
chatHistory.AddUserMessage(userMessageContent);
}
@@ -142,11 +198,31 @@ namespace AdvancedPaste.Services.CustomActions
switch (_serviceType)
{
case AIServiceType.OpenAI:
kernelBuilder.AddOpenAIChatCompletion(_config.Model, apiKey, serviceId: _config.Model);
if (_config.Usage == PasteAIUsage.TextToImage)
{
#pragma warning disable SKEXP0010
kernelBuilder.AddOpenAITextToImage(apiKey, modelId: _config.Model);
#pragma warning restore SKEXP0010
}
else
{
kernelBuilder.AddOpenAIChatCompletion(_config.Model, apiKey, serviceId: _config.Model);
}
break;
case AIServiceType.AzureOpenAI:
var deploymentName = string.IsNullOrWhiteSpace(_config.DeploymentName) ? _config.Model : _config.DeploymentName;
kernelBuilder.AddAzureOpenAIChatCompletion(deploymentName, RequireEndpoint(endpoint, _serviceType), apiKey, serviceId: _config.Model);
if (_config.Usage == PasteAIUsage.TextToImage)
{
#pragma warning disable SKEXP0010
kernelBuilder.AddAzureOpenAITextToImage(deploymentName, RequireEndpoint(endpoint, _serviceType), apiKey);
#pragma warning restore SKEXP0010
}
else
{
kernelBuilder.AddAzureOpenAIChatCompletion(deploymentName, RequireEndpoint(endpoint, _serviceType), apiKey, serviceId: _config.Model);
}
break;
case AIServiceType.Mistral:
kernelBuilder.AddMistralChatCompletion(_config.Model, apiKey: apiKey);

View File

@@ -372,4 +372,10 @@
<value>Unable to load Foundry Local model: {0}</value>
<comment>{0} is the model identifier. Do not translate {0}.</comment>
</data>
<data name="PasteAIUsage_ChatCompletion" xml:space="preserve">
<value>Chat completion</value>
</data>
<data name="PasteAIUsage_TextToImage" xml:space="preserve">
<value>Text to image</value>
</data>
</root>

View File

@@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Abstractions;
using System.Linq;
using System.Runtime.InteropServices;
@@ -614,8 +615,40 @@ namespace AdvancedPaste.ViewModels
}
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(HasCustomFormatImage))]
[NotifyPropertyChangedFor(nameof(HasCustomFormatText))]
[NotifyPropertyChangedFor(nameof(CustomFormatImageResult))]
private string _customFormatResult;
public bool HasCustomFormatImage => CustomFormatResult?.StartsWith("data:image", StringComparison.OrdinalIgnoreCase) ?? false;
public bool HasCustomFormatText => !HasCustomFormatImage;
public ImageSource CustomFormatImageResult
{
get
{
if (HasCustomFormatImage && !string.IsNullOrEmpty(CustomFormatResult))
{
try
{
var base64Data = CustomFormatResult.Split(',')[1];
var bytes = Convert.FromBase64String(base64Data);
var stream = new System.IO.MemoryStream(bytes);
var image = new BitmapImage();
image.SetSource(stream.AsRandomAccessStream());
return image;
}
catch (Exception ex)
{
Logger.LogError("Failed to create image source from data URI", ex);
}
}
return null;
}
}
[RelayCommand]
public async Task PasteCustomAsync()
{
@@ -623,7 +656,25 @@ namespace AdvancedPaste.ViewModels
if (!string.IsNullOrEmpty(text))
{
await CopyPasteAndHideAsync(DataPackageHelpers.CreateFromText(text));
if (text.StartsWith("data:image", StringComparison.OrdinalIgnoreCase))
{
try
{
var base64Data = text.Split(',')[1];
var bytes = Convert.FromBase64String(base64Data);
var stream = new System.IO.MemoryStream(bytes);
var dataPackage = DataPackageHelpers.CreateFromImage(Windows.Storage.Streams.RandomAccessStreamReference.CreateFromStream(stream.AsRandomAccessStream()));
await CopyPasteAndHideAsync(dataPackage);
}
catch (Exception ex)
{
Logger.LogError("Failed to paste image from data URI", ex);
}
}
else
{
await CopyPasteAndHideAsync(DataPackageHelpers.CreateFromText(text));
}
}
}
@@ -895,11 +946,6 @@ namespace AdvancedPaste.ViewModels
Logger.LogError("Failed to activate AI provider", ex);
return;
}
UpdateAIProviderActiveFlags();
OnPropertyChanged(nameof(AIProviders));
NotifyActiveProviderChanged();
EnqueueRefreshPasteFormats();
}
public async Task CancelPasteActionAsync()

View File

@@ -17,6 +17,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
{
private string _id = Guid.NewGuid().ToString("N");
private string _serviceType = "OpenAI";
private string _usage = "ChatCompletion";
private string _modelName = string.Empty;
private string _endpointUrl = string.Empty;
private string _apiVersion = string.Empty;
@@ -27,6 +28,8 @@ namespace Microsoft.PowerToys.Settings.UI.Library
private bool _isActive;
private bool _enableAdvancedAI;
private bool _isLocalModel;
private int _imageWidth = 1024;
private int _imageHeight = 1024;
public event PropertyChangedEventHandler PropertyChanged;
@@ -50,6 +53,20 @@ namespace Microsoft.PowerToys.Settings.UI.Library
}
}
[JsonPropertyName("usage")]
public string Usage
{
get => _usage;
set => SetProperty(ref _usage, string.IsNullOrWhiteSpace(value) ? "ChatCompletion" : value); // TODO: Localization support
}
[JsonIgnore]
public PasteAIUsage UsageKind
{
get => PasteAIUsageExtensions.FromConfigString(Usage);
set => Usage = value.ToConfigString();
}
[JsonIgnore]
public AIServiceType ServiceTypeKind
{
@@ -126,6 +143,20 @@ namespace Microsoft.PowerToys.Settings.UI.Library
set => SetProperty(ref _isLocalModel, value);
}
[JsonPropertyName("image-width")]
public int ImageWidth
{
get => _imageWidth;
set => SetProperty(ref _imageWidth, value);
}
[JsonPropertyName("image-height")]
public int ImageHeight
{
get => _imageHeight;
set => SetProperty(ref _imageHeight, value);
}
[JsonIgnore]
public bool IsActive
{
@@ -142,6 +173,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
{
Id = Id,
ServiceType = ServiceType,
Usage = Usage,
ModelName = ModelName,
EndpointUrl = EndpointUrl,
ApiVersion = ApiVersion,
@@ -151,6 +183,8 @@ namespace Microsoft.PowerToys.Settings.UI.Library
ModerationEnabled = ModerationEnabled,
EnableAdvancedAI = EnableAdvancedAI,
IsLocalModel = IsLocalModel,
ImageWidth = ImageWidth,
ImageHeight = ImageHeight,
IsActive = IsActive,
};
}

View File

@@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.PowerToys.Settings.UI.Library
{
public enum PasteAIUsage
{
ChatCompletion,
TextToImage,
}
}

View File

@@ -0,0 +1,28 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.PowerToys.Settings.UI.Library
{
public static class PasteAIUsageExtensions
{
public static string ToConfigString(this PasteAIUsage usage)
{
return usage switch
{
PasteAIUsage.ChatCompletion => "ChatCompletion",
PasteAIUsage.TextToImage => "TextToImage",
_ => "ChatCompletion",
};
}
public static PasteAIUsage FromConfigString(string usage)
{
return usage switch
{
"TextToImage" => PasteAIUsage.TextToImage,
_ => PasteAIUsage.ChatCompletion,
};
}
}
}

View File

@@ -0,0 +1,30 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using Microsoft.PowerToys.Settings.UI.Helpers;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.UI.Xaml.Data;
namespace Microsoft.PowerToys.Settings.UI.Converters;
public sealed partial class PasteAIUsageToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var usage = value switch
{
string s => PasteAIUsageExtensions.FromConfigString(s),
PasteAIUsage u => u,
_ => PasteAIUsage.ChatCompletion,
};
return ResourceLoaderInstance.ResourceLoader.GetString($"PasteAIUsage_{usage}_Label");
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}

View File

@@ -40,6 +40,7 @@
</Style>
<converters:ServiceTypeToIconConverter x:Key="ServiceTypeToIconConverter" />
<converters:PasteAIUsageToStringConverter x:Key="PasteAIUsageToStringConverter" />
<DataTemplate x:Key="AdditionalActionTemplate" x:DataType="models:AdvancedPasteAdditionalAction">
<StackPanel Orientation="Horizontal" Spacing="4">
<controls:ShortcutControl
@@ -118,6 +119,18 @@
Header="{x:Bind ModelName, Mode=OneWay}"
HeaderIcon="{x:Bind ServiceType, Mode=OneWay, Converter={StaticResource ServiceTypeToIconConverter}}">
<StackPanel Orientation="Horizontal" Spacing="8">
<Border
Padding="8,4"
VerticalAlignment="Center"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="4">
<TextBlock
FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{x:Bind Usage, Mode=OneWay, Converter={StaticResource PasteAIUsageToStringConverter}}" />
</Border>
<Button
Padding="8"
Background="Transparent"
@@ -493,6 +506,39 @@
Margin="0,8,0,48"
Orientation="Vertical"
Spacing="16">
<ComboBox
x:Name="PasteAIUsageComboBox"
x:Uid="AdvancedPaste_Usage"
MinWidth="200"
HorizontalAlignment="Stretch"
Header="Usage"
SelectedValue="{x:Bind ViewModel.PasteAIProviderDraft.Usage, Mode=TwoWay}"
SelectedValuePath="Tag"
SelectionChanged="PasteAIUsageComboBox_SelectionChanged"
Visibility="{x:Bind GetUsageVisibility(ViewModel.PasteAIProviderDraft.ServiceType), Mode=OneWay}">
<ComboBoxItem x:Uid="PasteAIUsage_ChatCompletion" Tag="ChatCompletion" />
<ComboBoxItem x:Uid="PasteAIUsage_TextToImage" Tag="TextToImage" />
</ComboBox>
<StackPanel
x:Name="PasteAIImageResolutionPanel"
Orientation="Horizontal"
Spacing="8"
Visibility="{x:Bind GetImageResolutionVisibility(ViewModel.PasteAIProviderDraft.Usage), Mode=OneWay}">
<TextBox
x:Name="PasteAIImageWidthTextBox"
x:Uid="AdvancedPaste_ImgOutputWidth"
MinWidth="96"
Text="{x:Bind ViewModel.PasteAIProviderDraft.ImageWidth, Mode=TwoWay}" />
<TextBlock
Margin="0,0,0,8"
VerticalAlignment="Bottom"
Text="x" />
<TextBox
x:Name="PasteAIImageHeightTextBox"
x:Uid="AdvancedPaste_ImgOutputHeight"
MinWidth="96"
Text="{x:Bind ViewModel.PasteAIProviderDraft.ImageHeight, Mode=TwoWay}" />
</StackPanel>
<TextBox
x:Name="PasteAIModelNameTextBox"
x:Uid="AdvancedPaste_ModelName"

View File

@@ -303,6 +303,45 @@ namespace Microsoft.PowerToys.Settings.UI.Views
System.Diagnostics.Debug.WriteLine($"{configType} API key saved successfully");
}
public Visibility GetUsageVisibility(string serviceType)
{
if (string.IsNullOrEmpty(serviceType))
{
return Visibility.Collapsed;
}
return (serviceType.Equals("OpenAI", StringComparison.OrdinalIgnoreCase) ||
serviceType.Equals("AzureOpenAI", StringComparison.OrdinalIgnoreCase))
? Visibility.Visible
: Visibility.Collapsed;
}
public Visibility GetImageResolutionVisibility(string usage)
{
return string.Equals(usage, "TextToImage", StringComparison.OrdinalIgnoreCase)
? Visibility.Visible
: Visibility.Collapsed;
}
private void PasteAIUsageComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (ViewModel?.PasteAIProviderDraft == null)
{
return;
}
var comboBox = (ComboBox)sender;
if (comboBox.SelectedValue is string usage && usage == "TextToImage")
{
ViewModel.PasteAIProviderDraft.EnableAdvancedAI = false;
PasteAIEnableAdvancedAICheckBox.IsEnabled = false;
}
else
{
PasteAIEnableAdvancedAICheckBox.IsEnabled = true;
}
}
private void UpdatePasteAIUIVisibility()
{
var draft = ViewModel?.PasteAIProviderDraft;
@@ -345,6 +384,17 @@ namespace Microsoft.PowerToys.Settings.UI.Views
PasteAIEnableAdvancedAICheckBox.Visibility = showAdvancedAI ? Visibility.Visible : Visibility.Collapsed;
PasteAIApiKeyPasswordBox.Visibility = requiresApiKey ? Visibility.Visible : Visibility.Collapsed;
PasteAIModelNameTextBox.Visibility = isFoundryLocal ? Visibility.Collapsed : Visibility.Visible;
PasteAIImageResolutionPanel.Visibility = GetImageResolutionVisibility(draft.Usage);
if (draft.Usage == "TextToImage")
{
draft.EnableAdvancedAI = false;
PasteAIEnableAdvancedAICheckBox.IsEnabled = false;
}
else
{
PasteAIEnableAdvancedAICheckBox.IsEnabled = true;
}
if (requiresApiKey)
{

View File

@@ -5769,4 +5769,22 @@ To record a specific window, enter the hotkey with the Alt key in the opposite m
<data name="LightSwitch_FollowNightLightCardMessage.Text" xml:space="preserve">
<value>Following Night Light settings.</value>
</data>
<data name="PasteAIUsage_ChatCompletion_Label" xml:space="preserve">
<value>Chat completion</value>
</data>
<data name="PasteAIUsage_TextToImage_Label" xml:space="preserve">
<value>Text to image</value>
</data>
<data name="PasteAIUsage_ChatCompletion.Content" xml:space="preserve">
<value>Chat completion</value>
</data>
<data name="PasteAIUsage_TextToImage.Content" xml:space="preserve">
<value>Text to image</value>
</data>
<data name="AdvancedPaste_ImgOutputWidth.Header" xml:space="preserve">
<value>Image output width</value>
</data>
<data name="AdvancedPaste_ImgOutputHeight.Header" xml:space="preserve">
<value>Image output height</value>
</data>
</root>

View File

@@ -949,6 +949,9 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
target.ModerationEnabled = source.ModerationEnabled;
target.EnableAdvancedAI = source.EnableAdvancedAI;
target.IsLocalModel = source.IsLocalModel;
target.Usage = source.Usage;
target.ImageWidth = source.ImageWidth;
target.ImageHeight = source.ImageHeight;
}
private void RemovePasteAICredentials(string providerId, string serviceType)