From 8dc65d2bb35fb1c3ab3bb5e40a43d5671ef54b99 Mon Sep 17 00:00:00 2001 From: Shawn Yuan Date: Wed, 22 Oct 2025 20:17:07 +0800 Subject: [PATCH] add advanced AI Signed-off-by: Shawn Yuan --- .../AdvancedPaste/Helpers/IUserSettings.cs | 2 - .../AdvancedPaste/Helpers/UserSettings.cs | 3 - .../Services/AdvancedAIKernelService.cs | 135 ++++++++---------- .../CustomActionTransformService.cs | 4 +- .../EnhancedVaultCredentialsProvider.cs | 42 ++++-- .../Services/KernelServiceBase.cs | 8 +- .../ViewModels/OptionsViewModel.cs | 62 +++++++- .../AdvancedPasteModuleInterface/dllmain.cpp | 78 ++++++++-- .../TestFiles/settings.json | 2 +- 9 files changed, 227 insertions(+), 109 deletions(-) diff --git a/src/modules/AdvancedPaste/AdvancedPaste/Helpers/IUserSettings.cs b/src/modules/AdvancedPaste/AdvancedPaste/Helpers/IUserSettings.cs index 12ef60a93b..e32cf61af4 100644 --- a/src/modules/AdvancedPaste/AdvancedPaste/Helpers/IUserSettings.cs +++ b/src/modules/AdvancedPaste/AdvancedPaste/Helpers/IUserSettings.cs @@ -13,8 +13,6 @@ namespace AdvancedPaste.Settings { public interface IUserSettings { - public bool IsAdvancedAIEnabled { get; } - public bool IsAIEnabled { get; } public bool ShowCustomPreview { get; } diff --git a/src/modules/AdvancedPaste/AdvancedPaste/Helpers/UserSettings.cs b/src/modules/AdvancedPaste/AdvancedPaste/Helpers/UserSettings.cs index 08800b0181..ea3d83d20e 100644 --- a/src/modules/AdvancedPaste/AdvancedPaste/Helpers/UserSettings.cs +++ b/src/modules/AdvancedPaste/AdvancedPaste/Helpers/UserSettings.cs @@ -34,8 +34,6 @@ namespace AdvancedPaste.Settings public event EventHandler Changed; - public bool IsAdvancedAIEnabled { get; private set; } - public bool IsAIEnabled { get; private set; } public bool ShowCustomPreview { get; private set; } @@ -52,7 +50,6 @@ namespace AdvancedPaste.Settings { _settingsUtils = new SettingsUtils(fileSystem); - IsAdvancedAIEnabled = false; IsAIEnabled = false; ShowCustomPreview = true; CloseAfterLosingFocus = false; diff --git a/src/modules/AdvancedPaste/AdvancedPaste/Services/AdvancedAIKernelService.cs b/src/modules/AdvancedPaste/AdvancedPaste/Services/AdvancedAIKernelService.cs index 7b714d5cd2..0032f49c06 100644 --- a/src/modules/AdvancedPaste/AdvancedPaste/Services/AdvancedAIKernelService.cs +++ b/src/modules/AdvancedPaste/AdvancedPaste/Services/AdvancedAIKernelService.cs @@ -31,7 +31,8 @@ public sealed class AdvancedAIKernelService : KernelServiceBase string Endpoint, string DeploymentName, string ModelPath, - bool UsePasteScope); + bool UsePasteScope, + bool ModerationEnabled); public AdvancedAIKernelService( IAICredentialsProvider credentialsProvider, @@ -81,27 +82,6 @@ public sealed class AdvancedAIKernelService : KernelServiceBase case AIServiceType.AzureOpenAI: kernelBuilder.AddAzureOpenAIChatCompletion(deployment, RequireEndpoint(endpoint, serviceType), apiKey, serviceId: modelName); break; - case AIServiceType.Mistral: - kernelBuilder.AddMistralChatCompletion(modelName, apiKey: apiKey); - break; - case AIServiceType.Google: - kernelBuilder.AddGoogleAIGeminiChatCompletion(modelName, apiKey: apiKey); - break; - case AIServiceType.HuggingFace: - kernelBuilder.AddHuggingFaceChatCompletion(modelName, apiKey: apiKey); - break; - case AIServiceType.AzureAIInference: - kernelBuilder.AddAzureAIInferenceChatCompletion(modelName, apiKey: apiKey); - break; - case AIServiceType.Ollama: - kernelBuilder.AddOllamaChatCompletion(modelName, endpoint: new Uri(RequireEndpoint(endpoint, serviceType))); - break; - case AIServiceType.Anthropic: - kernelBuilder.AddBedrockChatCompletionService(modelName); - break; - case AIServiceType.AmazonBedrock: - kernelBuilder.AddBedrockChatCompletionService(modelName); - break; default: throw new NotSupportedException($"Service type '{runtimeConfig.ServiceType}' is not supported"); } @@ -112,16 +92,14 @@ public sealed class AdvancedAIKernelService : KernelServiceBase return AIServiceUsageHelper.GetOpenAIServiceUsage(chatMessage); } - private PasteAIProviderDefinition GetConfiguration() + protected override bool ShouldModerateAdvancedAI() { - var config = this.UserSettings?.PasteAIConfiguration.Providers.FirstOrDefault( - p => p.EnableAdvancedAI); - if (config is null) + if (!TryGetRuntimeConfig(out var runtimeConfig)) { - return new PasteAIProviderDefinition(); + return false; } - return config; + return runtimeConfig.ModerationEnabled && (runtimeConfig.ServiceType == AIServiceType.OpenAI || runtimeConfig.ServiceType == AIServiceType.AzureOpenAI); } private static string GetModelName(PasteAIProviderDefinition config) @@ -136,27 +114,19 @@ public sealed class AdvancedAIKernelService : KernelServiceBase private RuntimeConfig GetRuntimeConfig() { - if (TryGetActiveProviderConfig(out var providerConfig)) + if (TryGetRuntimeConfig(out var runtimeConfig)) { - return providerConfig; + return runtimeConfig; } - var fallback = GetConfiguration(); - var serviceType = NormalizeServiceType(fallback.ServiceTypeKind); - return new RuntimeConfig( - serviceType, - GetModelName(fallback), - fallback.EndpointUrl, - fallback.DeploymentName, - fallback.ModelPath, - UsePasteScope: false); + throw new InvalidOperationException("No Advanced AI provider is configured."); } - private bool TryGetActiveProviderConfig(out RuntimeConfig runtimeConfig) + private bool TryGetRuntimeConfig(out RuntimeConfig runtimeConfig) { runtimeConfig = default; - var provider = this.UserSettings?.PasteAIConfiguration?.ActiveProvider; - if (provider is null) + + if (!TryResolveAdvancedProvider(out var provider, out var usePasteScope)) { return false; } @@ -167,34 +137,61 @@ public sealed class AdvancedAIKernelService : KernelServiceBase return false; } - var fallback = GetConfiguration(); - var modelName = !string.IsNullOrWhiteSpace(provider.ModelName) ? provider.ModelName : GetModelName(fallback); - runtimeConfig = new RuntimeConfig( serviceType, - modelName, + GetModelName(provider), provider.EndpointUrl, provider.DeploymentName, provider.ModelPath, - UsePasteScope: true); + usePasteScope, + provider.ModerationEnabled); return true; } + private bool TryResolveAdvancedProvider(out PasteAIProviderDefinition provider, out bool usePasteScope) + { + provider = null; + usePasteScope = false; + + var configuration = this.UserSettings?.PasteAIConfiguration; + if (configuration is null) + { + return false; + } + + var activeProvider = configuration.ActiveProvider; + if (IsAdvancedProvider(activeProvider)) + { + provider = activeProvider; + usePasteScope = true; + return true; + } + + var fallback = configuration.Providers?.FirstOrDefault(IsAdvancedProvider); + if (fallback is not null) + { + provider = fallback; + usePasteScope = configuration.UseSharedCredentials; + return true; + } + + return false; + } + + private static bool IsAdvancedProvider(PasteAIProviderDefinition provider) + { + if (provider is null || !provider.EnableAdvancedAI) + { + return false; + } + + var serviceType = NormalizeServiceType(provider.ServiceTypeKind); + return IsServiceTypeSupported(serviceType); + } + private static bool IsServiceTypeSupported(AIServiceType serviceType) { - return serviceType switch - { - AIServiceType.OpenAI - or AIServiceType.AzureOpenAI - or AIServiceType.Mistral - or AIServiceType.Google - or AIServiceType.HuggingFace - or AIServiceType.AzureAIInference - or AIServiceType.Ollama - or AIServiceType.Anthropic - or AIServiceType.AmazonBedrock => true, - _ => false, - }; + return serviceType is AIServiceType.OpenAI or AIServiceType.AzureOpenAI; } private static AIServiceType NormalizeServiceType(AIServiceType serviceType) @@ -204,13 +201,7 @@ public sealed class AdvancedAIKernelService : KernelServiceBase private static bool RequiresApiKey(AIServiceType serviceType) { - return serviceType switch - { - AIServiceType.Ollama => false, - AIServiceType.Anthropic => false, - AIServiceType.AmazonBedrock => false, - _ => true, - }; + return true; } private static string RequireEndpoint(string endpoint, AIServiceType serviceType) @@ -226,14 +217,10 @@ public sealed class AdvancedAIKernelService : KernelServiceBase private PromptExecutionSettings CreatePromptExecutionSettings() { var serviceType = GetRuntimeConfig().ServiceType; - return serviceType switch + return new OpenAIPromptExecutionSettings { - AIServiceType.OpenAI or AIServiceType.AzureOpenAI => new OpenAIPromptExecutionSettings - { - FunctionChoiceBehavior = FunctionChoiceBehavior.Auto(), - Temperature = 0.01, - }, - _ => new PromptExecutionSettings(), + FunctionChoiceBehavior = FunctionChoiceBehavior.Auto(), + Temperature = 0.01, }; } } diff --git a/src/modules/AdvancedPaste/AdvancedPaste/Services/CustomActions/CustomActionTransformService.cs b/src/modules/AdvancedPaste/AdvancedPaste/Services/CustomActions/CustomActionTransformService.cs index ee2bf7d3cd..1dfc7055ec 100644 --- a/src/modules/AdvancedPaste/AdvancedPaste/Services/CustomActions/CustomActionTransformService.cs +++ b/src/modules/AdvancedPaste/AdvancedPaste/Services/CustomActions/CustomActionTransformService.cs @@ -159,12 +159,12 @@ namespace AdvancedPaste.Services.CustomActions private static bool ShouldModerate(PasteAIConfig providerConfig) { - if (providerConfig is null) + if (providerConfig is null || !providerConfig.ModerationEnabled) { return false; } - return providerConfig.ProviderType == AIServiceType.OpenAI && providerConfig.ModerationEnabled; + return providerConfig.ProviderType == AIServiceType.OpenAI || providerConfig.ProviderType == AIServiceType.AzureOpenAI; } } } diff --git a/src/modules/AdvancedPaste/AdvancedPaste/Services/EnhancedVaultCredentialsProvider.cs b/src/modules/AdvancedPaste/AdvancedPaste/Services/EnhancedVaultCredentialsProvider.cs index 63705173ac..a0ef6a99ee 100644 --- a/src/modules/AdvancedPaste/AdvancedPaste/Services/EnhancedVaultCredentialsProvider.cs +++ b/src/modules/AdvancedPaste/AdvancedPaste/Services/EnhancedVaultCredentialsProvider.cs @@ -118,8 +118,24 @@ public sealed class EnhancedVaultCredentialsProvider : IAICredentialsProvider private AIServiceType ResolveAdvancedAiServiceType() { - // return _userSettings.AdvancedAIConfiguration?.ServiceTypeKind ?? AIServiceType.OpenAI; - // todo: fix + var configuration = _userSettings.PasteAIConfiguration; + if (configuration is null) + { + return AIServiceType.OpenAI; + } + + var activeProvider = configuration.ActiveProvider; + if (IsAdvancedProvider(activeProvider)) + { + return NormalizeServiceType(activeProvider.ServiceTypeKind); + } + + var fallback = configuration.Providers?.FirstOrDefault(IsAdvancedProvider); + if (fallback is not null) + { + return NormalizeServiceType(fallback.ServiceTypeKind); + } + return AIServiceType.OpenAI; } @@ -134,6 +150,21 @@ public sealed class EnhancedVaultCredentialsProvider : IAICredentialsProvider return (provider.ServiceTypeKind, provider.Id ?? string.Empty); } + private static bool IsAdvancedProvider(PasteAIProviderDefinition provider) + { + if (provider is null || !provider.EnableAdvancedAI) + { + return false; + } + + return SupportsAdvancedAI(provider.ServiceTypeKind); + } + + private static bool SupportsAdvancedAI(AIServiceType serviceType) + { + return NormalizeServiceType(serviceType) is AIServiceType.OpenAI or AIServiceType.AzureOpenAI; + } + private static string LoadKey((string Resource, string Username)? entry) { if (entry is null) @@ -168,13 +199,6 @@ public sealed class EnhancedVaultCredentialsProvider : IAICredentialsProvider { AIServiceType.OpenAI => ("https://platform.openai.com/api-keys", "PowerToys_AdvancedPaste_AdvancedAI_OpenAI"), AIServiceType.AzureOpenAI => ("https://azure.microsoft.com/products/ai-services/openai-service", "PowerToys_AdvancedPaste_AdvancedAI_AzureOpenAI"), - AIServiceType.AzureAIInference => ("https://azure.microsoft.com/products/ai-services/ai-inference", "PowerToys_AdvancedPaste_AdvancedAI_AzureAIInference"), - AIServiceType.Mistral => ("https://console.mistral.ai/account/api-keys", "PowerToys_AdvancedPaste_AdvancedAI_Mistral"), - AIServiceType.Google => ("https://ai.google.dev/", "PowerToys_AdvancedPaste_AdvancedAI_Google"), - AIServiceType.HuggingFace => ("https://huggingface.co/settings/tokens", "PowerToys_AdvancedPaste_AdvancedAI_HuggingFace"), - AIServiceType.Ollama => null, - AIServiceType.Anthropic => null, - AIServiceType.AmazonBedrock => null, _ => null, }; } diff --git a/src/modules/AdvancedPaste/AdvancedPaste/Services/KernelServiceBase.cs b/src/modules/AdvancedPaste/AdvancedPaste/Services/KernelServiceBase.cs index b9d9946de4..bf587f5d5f 100644 --- a/src/modules/AdvancedPaste/AdvancedPaste/Services/KernelServiceBase.cs +++ b/src/modules/AdvancedPaste/AdvancedPaste/Services/KernelServiceBase.cs @@ -369,14 +369,8 @@ public abstract class KernelServiceBase( return $"-> {role}: {redactedContent}{usageString}"; } - private bool ShouldModerateAdvancedAI() + protected virtual bool ShouldModerateAdvancedAI() { - // TODO return false; } - - private static AIServiceType NormalizeServiceType(AIServiceType serviceType) - { - return serviceType == AIServiceType.Unknown ? AIServiceType.OpenAI : serviceType; - } } diff --git a/src/modules/AdvancedPaste/AdvancedPaste/ViewModels/OptionsViewModel.cs b/src/modules/AdvancedPaste/AdvancedPaste/ViewModels/OptionsViewModel.cs index 131abbe9f4..46c4817c36 100644 --- a/src/modules/AdvancedPaste/AdvancedPaste/ViewModels/OptionsViewModel.cs +++ b/src/modules/AdvancedPaste/AdvancedPaste/ViewModels/OptionsViewModel.cs @@ -107,7 +107,24 @@ namespace AdvancedPaste.ViewModels public bool IsCustomAIAvailable => IsCustomAIServiceEnabled && ClipboardHasDataForCustomAI; - public bool IsAdvancedAIEnabled => IsAllowedByGPO && _userSettings.IsAIEnabled && _userSettings.IsAdvancedAIEnabled && _credentialsProvider.IsConfigured(AICredentialScope.AdvancedAI); + public bool IsAdvancedAIEnabled + { + get + { + if (!IsAllowedByGPO || !_userSettings.IsAIEnabled) + { + return false; + } + + if (!TryResolveAdvancedAIProvider(out var provider, out var usesPasteScope)) + { + return false; + } + + var scope = usesPasteScope ? AICredentialScope.PasteAI : AICredentialScope.AdvancedAI; + return _credentialsProvider.IsConfigured(scope); + } + } public ObservableCollection AIProviders => _userSettings?.PasteAIConfiguration?.Providers ?? new ObservableCollection(); @@ -143,7 +160,7 @@ namespace AdvancedPaste.ViewModels public bool HasIndeterminateTransformProgress => double.IsNaN(TransformProgress); - private PasteFormats CustomAIFormat => _userSettings.IsAdvancedAIEnabled && _userSettings.IsAIEnabled ? PasteFormats.KernelQuery : PasteFormats.CustomTextTransformation; + private PasteFormats CustomAIFormat => _userSettings.IsAIEnabled && TryResolveAdvancedAIProvider(out _, out _) ? PasteFormats.KernelQuery : PasteFormats.CustomTextTransformation; private bool Visible { @@ -678,6 +695,47 @@ namespace AdvancedPaste.ViewModels IsAllowedByGPO = PowerToys.GPOWrapper.GPOWrapper.GetAllowedAdvancedPasteOnlineAIModelsValue() != PowerToys.GPOWrapper.GpoRuleConfigured.Disabled; } + private bool TryResolveAdvancedAIProvider(out PasteAIProviderDefinition provider, out bool usesPasteScope) + { + provider = null; + usesPasteScope = false; + + var configuration = _userSettings?.PasteAIConfiguration; + if (configuration is null) + { + return false; + } + + var activeProvider = configuration.ActiveProvider; + if (IsAdvancedAIProvider(activeProvider)) + { + provider = activeProvider; + usesPasteScope = true; + return true; + } + + var fallback = configuration.Providers?.FirstOrDefault(IsAdvancedAIProvider); + if (fallback is not null) + { + provider = fallback; + usesPasteScope = configuration.UseSharedCredentials; + return true; + } + + return false; + } + + private static bool IsAdvancedAIProvider(PasteAIProviderDefinition provider) + { + return provider is not null && provider.EnableAdvancedAI && SupportsAdvancedAI(provider.ServiceTypeKind); + } + + private static bool SupportsAdvancedAI(AIServiceType serviceType) + { + return serviceType is AIServiceType.OpenAI + or AIServiceType.AzureOpenAI; + } + private bool UpdateOpenAIKey() { UpdateAllowedByGPO(); diff --git a/src/modules/AdvancedPaste/AdvancedPasteModuleInterface/dllmain.cpp b/src/modules/AdvancedPaste/AdvancedPasteModuleInterface/dllmain.cpp index f3c57a46a4..27a34c4f74 100644 --- a/src/modules/AdvancedPaste/AdvancedPasteModuleInterface/dllmain.cpp +++ b/src/modules/AdvancedPaste/AdvancedPasteModuleInterface/dllmain.cpp @@ -16,6 +16,8 @@ #include #include +#include +#include #include BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*lpReserved*/) @@ -53,10 +55,13 @@ namespace const wchar_t JSON_KEY_ADVANCED_PASTE_UI_HOTKEY[] = L"advanced-paste-ui-hotkey"; const wchar_t JSON_KEY_PASTE_AS_MARKDOWN_HOTKEY[] = L"paste-as-markdown-hotkey"; const wchar_t JSON_KEY_PASTE_AS_JSON_HOTKEY[] = L"paste-as-json-hotkey"; - const wchar_t JSON_KEY_IS_ADVANCED_AI_ENABLED[] = L"IsAdvancedAIEnabled"; const wchar_t JSON_KEY_IS_AI_ENABLED[] = L"IsAIEnabled"; const wchar_t JSON_KEY_IS_OPEN_AI_ENABLED[] = L"IsOpenAIEnabled"; const wchar_t JSON_KEY_SHOW_CUSTOM_PREVIEW[] = L"ShowCustomPreview"; + const wchar_t JSON_KEY_PASTE_AI_CONFIGURATION[] = L"paste-ai-configuration"; + const wchar_t JSON_KEY_PROVIDERS[] = L"providers"; + const wchar_t JSON_KEY_SERVICE_TYPE[] = L"service-type"; + const wchar_t JSON_KEY_ENABLE_ADVANCED_AI[] = L"enable-advanced-ai"; const wchar_t JSON_KEY_VALUE[] = L"value"; } @@ -179,6 +184,13 @@ private: return result; } + static std::wstring to_lower_case(const std::wstring& value) + { + std::wstring result = value; + std::transform(result.begin(), result.end(), result.begin(), [](wchar_t ch) { return std::towlower(ch); }); + return result; + } + bool migrate_data_and_remove_data_file(Hotkey& old_paste_as_plain_hotkey) { const wchar_t OLD_JSON_KEY_ACTIVATION_SHORTCUT[] = L"ActivationShortcut"; @@ -245,6 +257,61 @@ private: } } + bool has_advanced_ai_provider(const winrt::Windows::Data::Json::JsonObject& propertiesObject) + { + if (!propertiesObject.HasKey(JSON_KEY_PASTE_AI_CONFIGURATION)) + { + return false; + } + + const auto configValue = propertiesObject.GetNamedValue(JSON_KEY_PASTE_AI_CONFIGURATION); + if (configValue.ValueType() != winrt::Windows::Data::Json::JsonValueType::Object) + { + return false; + } + + const auto configObject = configValue.GetObjectW(); + if (!configObject.HasKey(JSON_KEY_PROVIDERS)) + { + return false; + } + + const auto providersValue = configObject.GetNamedValue(JSON_KEY_PROVIDERS); + if (providersValue.ValueType() != winrt::Windows::Data::Json::JsonValueType::Array) + { + return false; + } + + const auto providers = providersValue.GetArray(); + for (const auto providerValue : providers) + { + if (providerValue.ValueType() != winrt::Windows::Data::Json::JsonValueType::Object) + { + continue; + } + + const auto providerObject = providerValue.GetObjectW(); + if (!providerObject.GetNamedBoolean(JSON_KEY_ENABLE_ADVANCED_AI, false)) + { + continue; + } + + if (!providerObject.HasKey(JSON_KEY_SERVICE_TYPE)) + { + continue; + } + + const auto serviceType = providerObject.GetNamedString(JSON_KEY_SERVICE_TYPE, L""); + const auto normalizedServiceType = to_lower_case(serviceType); + if (normalizedServiceType == L"openai" || normalizedServiceType == L"azureopenai") + { + return true; + } + } + + return false; + } + void read_settings(PowerToysSettings::PowerToyValues& settings) { const auto settingsObject = settings.get_raw_json(); @@ -343,14 +410,7 @@ private: { const auto propertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES); - if (propertiesObject.HasKey(JSON_KEY_IS_ADVANCED_AI_ENABLED)) - { - m_is_advanced_ai_enabled = propertiesObject.GetNamedObject(JSON_KEY_IS_ADVANCED_AI_ENABLED).GetNamedBoolean(JSON_KEY_VALUE); - } - else - { - m_is_advanced_ai_enabled = false; - } + m_is_advanced_ai_enabled = has_advanced_ai_provider(propertiesObject); if (propertiesObject.HasKey(JSON_KEY_IS_AI_ENABLED)) { diff --git a/src/modules/AdvancedPaste/UITest-AdvancedPaste/TestFiles/settings.json b/src/modules/AdvancedPaste/UITest-AdvancedPaste/TestFiles/settings.json index 5e52f55548..bc0803796e 100644 --- a/src/modules/AdvancedPaste/UITest-AdvancedPaste/TestFiles/settings.json +++ b/src/modules/AdvancedPaste/UITest-AdvancedPaste/TestFiles/settings.json @@ -1 +1 @@ -{"properties":{"IsAdvancedAIEnabled":{"value":false},"IsAIEnabled":{"value":false},"ShowCustomPreview":{"value":true},"CloseAfterLosingFocus":{"value":false},"advanced-paste-ui-hotkey":{"win":true,"ctrl":false,"alt":false,"shift":true,"code":86,"key":""},"paste-as-plain-hotkey":{"win":true,"ctrl":true,"alt":true,"shift":false,"code":79,"key":""},"paste-as-markdown-hotkey":{"win":true,"ctrl":true,"alt":true,"shift":false,"code":77,"key":""},"paste-as-json-hotkey":{"win":true,"ctrl":true,"alt":true,"shift":false,"code":74,"key":""},"custom-actions":{"value":[]},"additional-actions":{"image-to-text":{"shortcut":{"win":false,"ctrl":false,"alt":false,"shift":false,"code":0,"key":""},"isShown":true},"paste-as-file":{"isShown":true,"paste-as-txt-file":{"shortcut":{"win":false,"ctrl":false,"alt":false,"shift":false,"code":0,"key":""},"isShown":true},"paste-as-png-file":{"shortcut":{"win":false,"ctrl":false,"alt":false,"shift":false,"code":0,"key":""},"isShown":true},"paste-as-html-file":{"shortcut":{"win":false,"ctrl":false,"alt":false,"shift":false,"code":0,"key":""},"isShown":true}},"transcode":{"isShown":true,"transcode-to-mp3":{"shortcut":{"win":false,"ctrl":false,"alt":false,"shift":false,"code":0,"key":""},"isShown":true},"transcode-to-mp4":{"shortcut":{"win":false,"ctrl":false,"alt":false,"shift":false,"code":0,"key":""},"isShown":true}}}},"name":"AdvancedPaste","version":"1"} \ No newline at end of file +{"properties":{"IsAIEnabled":{"value":false},"ShowCustomPreview":{"value":true},"CloseAfterLosingFocus":{"value":false},"advanced-paste-ui-hotkey":{"win":true,"ctrl":false,"alt":false,"shift":true,"code":86,"key":""},"paste-as-plain-hotkey":{"win":true,"ctrl":true,"alt":true,"shift":false,"code":79,"key":""},"paste-as-markdown-hotkey":{"win":true,"ctrl":true,"alt":true,"shift":false,"code":77,"key":""},"paste-as-json-hotkey":{"win":true,"ctrl":true,"alt":true,"shift":false,"code":74,"key":""},"custom-actions":{"value":[]},"additional-actions":{"image-to-text":{"shortcut":{"win":false,"ctrl":false,"alt":false,"shift":false,"code":0,"key":""},"isShown":true},"paste-as-file":{"isShown":true,"paste-as-txt-file":{"shortcut":{"win":false,"ctrl":false,"alt":false,"shift":false,"code":0,"key":""},"isShown":true},"paste-as-png-file":{"shortcut":{"win":false,"ctrl":false,"alt":false,"shift":false,"code":0,"key":""},"isShown":true},"paste-as-html-file":{"shortcut":{"win":false,"ctrl":false,"alt":false,"shift":false,"code":0,"key":""},"isShown":true}},"transcode":{"isShown":true,"transcode-to-mp3":{"shortcut":{"win":false,"ctrl":false,"alt":false,"shift":false,"code":0,"key":""},"isShown":true},"transcode-to-mp4":{"shortcut":{"win":false,"ctrl":false,"alt":false,"shift":false,"code":0,"key":""},"isShown":true}}},"paste-ai-configuration":{"active-provider-id":"","providers":[],"use-shared-credentials":true}},"name":"AdvancedPaste","version":"1"} \ No newline at end of file