Compare commits
3 Commits
leilzh/adc
...
shawn/fixA
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
792c6c309a | ||
|
|
539492bbd6 | ||
|
|
678742e141 |
1
.github/actions/spell-check/excludes.txt
vendored
@@ -105,7 +105,6 @@
|
||||
^src/common/notifications/BackgroundActivatorDLL/cpp\.hint$
|
||||
^src/common/sysinternals/Eula/
|
||||
^src/modules/cmdpal/doc/initial-sdk-spec/list-elements-mock-002\.pdn$
|
||||
^src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleMarkdownImagesPage\.cs$
|
||||
^src/modules/colorPicker/ColorPickerUI/Shaders/GridShader\.cso$
|
||||
^src/modules/launcher/Plugins/Microsoft\.PowerToys\.Run\.Plugin\.TimeDate/Properties/
|
||||
^src/modules/MouseUtils/MouseJumpUI/MainForm\.resx$
|
||||
|
||||
17
.github/actions/spell-check/expect.txt
vendored
@@ -47,6 +47,7 @@ Allmodule
|
||||
ALLOWUNDO
|
||||
ALLVIEW
|
||||
ALPHATYPE
|
||||
amazonbedrock
|
||||
AModifier
|
||||
amr
|
||||
ANDSCANS
|
||||
@@ -65,7 +66,6 @@ APIIs
|
||||
Apm
|
||||
APPBARDATA
|
||||
APPEXECLINK
|
||||
appext
|
||||
APPLICATIONFRAMEHOST
|
||||
appmanifest
|
||||
APPMODEL
|
||||
@@ -101,6 +101,7 @@ ATX
|
||||
ATRIOX
|
||||
aumid
|
||||
authenticode
|
||||
Authenticode
|
||||
AUTOBUDDY
|
||||
AUTOCHECKBOX
|
||||
AUTOHIDE
|
||||
@@ -187,7 +188,6 @@ CAPTUREBLT
|
||||
CAPTURECHANGED
|
||||
CARETBLINKING
|
||||
CAtl
|
||||
CBN
|
||||
cch
|
||||
CCHDEVICENAME
|
||||
CCHFORMNAME
|
||||
@@ -316,6 +316,7 @@ CURSORINFO
|
||||
cursorpos
|
||||
CURSORSHOWING
|
||||
CURSORWRAP
|
||||
CursorWrap
|
||||
customaction
|
||||
CUSTOMACTIONTEST
|
||||
CUSTOMFORMATPLACEHOLDER
|
||||
@@ -415,9 +416,6 @@ DNLEN
|
||||
DONOTROUND
|
||||
DONTVALIDATEPATH
|
||||
dotnet
|
||||
downsampled
|
||||
downsampling
|
||||
Downsampled
|
||||
downscale
|
||||
DPICHANGED
|
||||
DPIs
|
||||
@@ -434,6 +432,7 @@ DSTINVERT
|
||||
DString
|
||||
DSVG
|
||||
dto
|
||||
DTo
|
||||
DUMMYUNIONNAME
|
||||
dutil
|
||||
DVASPECT
|
||||
@@ -467,6 +466,7 @@ EDITKEYBOARD
|
||||
EDITSHORTCUTS
|
||||
EDITTEXT
|
||||
EFile
|
||||
ekus
|
||||
eku
|
||||
emojis
|
||||
ENABLEDELAYEDEXPANSION
|
||||
@@ -602,7 +602,6 @@ getfilesiginforedist
|
||||
geolocator
|
||||
GETHOTKEY
|
||||
GETICON
|
||||
GETLBTEXT
|
||||
GETMINMAXINFO
|
||||
GETNONCLIENTMETRICS
|
||||
GETPROPERTYSTOREFLAGS
|
||||
@@ -610,7 +609,6 @@ GETSCREENSAVERRUNNING
|
||||
GETSECKEY
|
||||
GETSTICKYKEYS
|
||||
GETTEXTLENGTH
|
||||
GIFs
|
||||
gitmodules
|
||||
GHND
|
||||
GMEM
|
||||
@@ -621,7 +619,6 @@ GPOCA
|
||||
gpp
|
||||
gpu
|
||||
gradians
|
||||
grctlext
|
||||
Gridcustomlayout
|
||||
GSM
|
||||
gtm
|
||||
@@ -695,6 +692,7 @@ hmonitor
|
||||
homies
|
||||
homljgmgpmcbpjbnjpfijnhipfkiclkd
|
||||
HOOKPROC
|
||||
huggingface
|
||||
HORZRES
|
||||
HORZSIZE
|
||||
Hostbackdropbrush
|
||||
@@ -1154,6 +1152,7 @@ NONCLIENTMETRICSW
|
||||
NONELEVATED
|
||||
nonspace
|
||||
nonstd
|
||||
nullrefs
|
||||
NOOWNERZORDER
|
||||
NOPARENTNOTIFY
|
||||
NOPREFIX
|
||||
@@ -1193,8 +1192,8 @@ ntfs
|
||||
NTSTATUS
|
||||
NTSYSAPI
|
||||
NULLCURSOR
|
||||
nullonfailure
|
||||
nullref
|
||||
nullonfailure
|
||||
numberbox
|
||||
nwc
|
||||
ocr
|
||||
|
||||
2
.github/actions/spell-check/patterns.txt
vendored
@@ -253,7 +253,7 @@ _SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING
|
||||
|
||||
# hit-count: 1 file-count: 1
|
||||
# Amazon
|
||||
\bamazon\.com/[-\w]+/(?:dp/[0-9A-Z]+|)
|
||||
\bamazon\.com/[-\w]+/(?:dp/[0-9A-Z]+|)[^"'\s]+
|
||||
|
||||
# hit-count: 3 file-count: 3
|
||||
# imgur
|
||||
|
||||
@@ -52,6 +52,8 @@ extends:
|
||||
name: SHINE-INT-S
|
||||
${{ if eq(parameters.useVSPreview, true) }}:
|
||||
demands: ImageOverride -equals SHINE-VS17-Preview
|
||||
${{ else }}:
|
||||
image: SHINE-VS17-Latest
|
||||
os: windows
|
||||
sdl:
|
||||
tsa:
|
||||
@@ -73,6 +75,7 @@ extends:
|
||||
name: SHINE-INT-L
|
||||
demands:
|
||||
# Our INT agents have a large disk mounted at P:\
|
||||
- WorkFolder -equals P:\_work
|
||||
- ${{ if eq(parameters.useVSPreview, true) }}:
|
||||
- ImageOverride -equals SHINE-VS17-Preview
|
||||
os: windows
|
||||
@@ -123,6 +126,7 @@ extends:
|
||||
parameters:
|
||||
pool:
|
||||
name: SHINE-INT-L
|
||||
image: SHINE-VS17-Latest
|
||||
os: windows
|
||||
official: true
|
||||
codeSign: true
|
||||
|
||||
@@ -111,7 +111,6 @@ jobs:
|
||||
${{ else }}:
|
||||
OutputBuildPlatform: ${{ platform }}
|
||||
variables:
|
||||
NUGET_PACKAGES: 'C:\NuGetPackages' # Some of our build steps cache these here... and it was apparently part of the global environment
|
||||
MakeAppxPath: 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x86\MakeAppx.exe'
|
||||
# Azure DevOps abhors a vacuum
|
||||
# If these are blank, expansion will fail later on... which will result in direct substitution of the variable *names*
|
||||
@@ -140,10 +139,6 @@ jobs:
|
||||
- output: pipelineArtifact
|
||||
artifactName: $(JobOutputArtifactName)
|
||||
targetPath: $(Build.ArtifactStagingDirectory)
|
||||
- output: pipelineArtifact
|
||||
artifactName: $(JobOutputArtifactName)-failure-$(System.JobAttempt)
|
||||
targetPath: $(LogOutputDirectory)
|
||||
condition: or(failed(), canceled())
|
||||
steps:
|
||||
- checkout: self
|
||||
clean: true
|
||||
@@ -400,7 +395,7 @@ jobs:
|
||||
### HACK: On ARM64 builds, building an app with Windows App SDK copies the x64 WebView2 dll instead of the ARM64 one. This task makes sure the right dll is used.
|
||||
- task: CopyFiles@2
|
||||
displayName: HACK Copy core WebView2 ARM64 dll to output directory
|
||||
condition: and(succeeded(), eq(variables['BuildPlatform'], 'arm64'))
|
||||
condition: eq(variables['BuildPlatform'],'arm64')
|
||||
inputs:
|
||||
contents: packages/Microsoft.Web.WebView2.1.0.2903.40/runtimes/win-ARM64/native_uap/Microsoft.Web.WebView2.Core.dll
|
||||
targetFolder: $(Build.SourcesDirectory)/ARM64/Release/WinUI3Apps/
|
||||
@@ -439,11 +434,11 @@ jobs:
|
||||
inputs:
|
||||
testResultsFormat: VSTest
|
||||
testResultsFiles: '**/*.trx'
|
||||
condition: and(succeeded(), ne(variables['BuildPlatform'], 'arm64'))
|
||||
condition: ne(variables['BuildPlatform'],'arm64')
|
||||
|
||||
# Native dlls
|
||||
- task: VSTest@2
|
||||
condition: and(succeeded(), ne(variables['BuildPlatform'], 'arm64')) # No arm64 agents to run the tests.
|
||||
condition: ne(variables['BuildPlatform'],'arm64') # No arm64 agents to run the tests.
|
||||
displayName: 'Native Tests'
|
||||
inputs:
|
||||
platform: '$(BuildPlatform)'
|
||||
|
||||
@@ -51,8 +51,10 @@
|
||||
<PackageVersion Include="Microsoft.AI.Foundry.Local" Version="0.3.0" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.66.0" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.OpenAI" Version="1.66.0" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.Amazon" Version="1.66.0-alpha" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.AzureAIInference" Version="1.66.0-beta" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.Google" Version="1.66.0-alpha" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.HuggingFace" Version="1.66.0-preview" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.MistralAI" Version="1.66.0-alpha" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.Ollama" Version="1.66.0-alpha" />
|
||||
<PackageVersion Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.2" />
|
||||
|
||||
@@ -10,11 +10,11 @@
|
||||
|
||||
<h3 align="center">
|
||||
<a href="#-installation">Installation</a>
|
||||
<span> · </span>
|
||||
<span> . </span>
|
||||
<a href="https://aka.ms/powertoys-docs">Documentation</a>
|
||||
<span> · </span>
|
||||
<span> . </span>
|
||||
<a href="https://aka.ms/powertoys-releaseblog">Blog</a>
|
||||
<span> · </span>
|
||||
<span> . </span>
|
||||
<a href="#-whats-new">Release notes</a>
|
||||
</h3>
|
||||
<br/><br/>
|
||||
|
||||
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 24 KiB |
@@ -216,6 +216,10 @@ namespace winrt::PowerToys::GPOWrapper::implementation
|
||||
{
|
||||
return static_cast<GpoRuleConfigured>(powertoys_gpo::getAllowedAdvancedPasteGoogleValue());
|
||||
}
|
||||
GpoRuleConfigured GPOWrapper::GetAllowedAdvancedPasteAnthropicValue()
|
||||
{
|
||||
return static_cast<GpoRuleConfigured>(powertoys_gpo::getAllowedAdvancedPasteAnthropicValue());
|
||||
}
|
||||
GpoRuleConfigured GPOWrapper::GetAllowedAdvancedPasteOllamaValue()
|
||||
{
|
||||
return static_cast<GpoRuleConfigured>(powertoys_gpo::getAllowedAdvancedPasteOllamaValue());
|
||||
|
||||
@@ -60,6 +60,7 @@ namespace winrt::PowerToys::GPOWrapper::implementation
|
||||
static GpoRuleConfigured GetAllowedAdvancedPasteAzureAIInferenceValue();
|
||||
static GpoRuleConfigured GetAllowedAdvancedPasteMistralValue();
|
||||
static GpoRuleConfigured GetAllowedAdvancedPasteGoogleValue();
|
||||
static GpoRuleConfigured GetAllowedAdvancedPasteAnthropicValue();
|
||||
static GpoRuleConfigured GetAllowedAdvancedPasteOllamaValue();
|
||||
static GpoRuleConfigured GetAllowedAdvancedPasteFoundryLocalValue();
|
||||
static GpoRuleConfigured GetConfiguredNewPlusEnabledValue();
|
||||
|
||||
@@ -64,6 +64,7 @@ namespace PowerToys
|
||||
static GpoRuleConfigured GetAllowedAdvancedPasteAzureAIInferenceValue();
|
||||
static GpoRuleConfigured GetAllowedAdvancedPasteMistralValue();
|
||||
static GpoRuleConfigured GetAllowedAdvancedPasteGoogleValue();
|
||||
static GpoRuleConfigured GetAllowedAdvancedPasteAnthropicValue();
|
||||
static GpoRuleConfigured GetAllowedAdvancedPasteOllamaValue();
|
||||
static GpoRuleConfigured GetAllowedAdvancedPasteFoundryLocalValue();
|
||||
static GpoRuleConfigured GetConfiguredNewPlusEnabledValue();
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
|
||||
|
||||
namespace Microsoft.PowerToys.UITest
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class for configuring PowerToys settings for UI tests.
|
||||
/// </summary>
|
||||
public class SettingsConfigHelper
|
||||
{
|
||||
private static readonly JsonSerializerOptions IndentedJsonOptions = new() { WriteIndented = true };
|
||||
private static readonly SettingsUtils SettingsUtils = new SettingsUtils();
|
||||
|
||||
/// <summary>
|
||||
/// Configures global PowerToys settings to enable only specified modules and disable all others.
|
||||
/// </summary>
|
||||
/// <param name="modulesToEnable">Array of module names to enable (e.g., "Peek", "FancyZones"). All other modules will be disabled.</param>
|
||||
/// <exception cref="ArgumentNullException">Thrown when modulesToEnable is null.</exception>
|
||||
/// <exception cref="InvalidOperationException">Thrown when settings file operations fail.</exception>
|
||||
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "This is test code and will not be trimmed")]
|
||||
[UnconditionalSuppressMessage("AOT", "IL3050", Justification = "This is test code and will not be AOT compiled")]
|
||||
public static void ConfigureGlobalModuleSettings(params string[] modulesToEnable)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(modulesToEnable);
|
||||
|
||||
try
|
||||
{
|
||||
GeneralSettings settings;
|
||||
try
|
||||
{
|
||||
settings = SettingsUtils.GetSettingsOrDefault<GeneralSettings>();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Failed to load settings, creating defaults: {ex.Message}");
|
||||
settings = new GeneralSettings();
|
||||
}
|
||||
|
||||
string settingsJson = settings.ToJsonString();
|
||||
using (JsonDocument doc = JsonDocument.Parse(settingsJson))
|
||||
{
|
||||
var options = new JsonSerializerOptions { WriteIndented = true };
|
||||
var root = doc.RootElement.Clone();
|
||||
|
||||
if (root.TryGetProperty("enabled", out var enabledElement))
|
||||
{
|
||||
var enabledModules = new Dictionary<string, bool>();
|
||||
|
||||
foreach (var property in enabledElement.EnumerateObject())
|
||||
{
|
||||
string moduleName = property.Name;
|
||||
|
||||
bool shouldEnable = Array.Exists(modulesToEnable, m => string.Equals(m, moduleName, StringComparison.Ordinal));
|
||||
enabledModules[moduleName] = shouldEnable;
|
||||
}
|
||||
|
||||
var settingsDict = JsonSerializer.Deserialize<Dictionary<string, object>>(settingsJson);
|
||||
if (settingsDict != null)
|
||||
{
|
||||
settingsDict["enabled"] = enabledModules;
|
||||
settingsJson = JsonSerializer.Serialize(settingsDict, IndentedJsonOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsUtils.SaveSettings(settingsJson);
|
||||
|
||||
string enabledList = modulesToEnable.Length > 0 ? string.Join(", ", modulesToEnable) : "none";
|
||||
Debug.WriteLine($"Successfully updated global settings");
|
||||
Debug.WriteLine($"Enabled modules: {enabledList}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"ERROR in ConfigureGlobalModuleSettings: {ex.Message}");
|
||||
throw new InvalidOperationException($"Failed to configure global module settings: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates a module's settings file. If the file doesn't exist, creates it with default content.
|
||||
/// If the file exists, reads it and applies the provided update function to modify the settings.
|
||||
/// </summary>
|
||||
/// <param name="moduleName">The name of the module (e.g., "Peek", "FancyZones").</param>
|
||||
/// <param name="defaultSettingsContent">The default JSON content to use if the settings file doesn't exist.</param>
|
||||
/// <param name="updateSettingsAction">
|
||||
/// A callback function that modifies the settings dictionary. The function receives the deserialized settings
|
||||
/// and should modify it in-place. The function should accept a Dictionary<string, object> and not return a value.
|
||||
/// Example: (settings) => { ((Dictionary<string, object>)settings["properties"])["SomeSetting"] = newValue; }
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">Thrown when moduleName or updateSettingsAction is null.</exception>
|
||||
/// <exception cref="InvalidOperationException">Thrown when settings file operations fail.</exception>
|
||||
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "This is test code and will not be trimmed")]
|
||||
[UnconditionalSuppressMessage("AOT", "IL3050", Justification = "This is test code and will not be AOT compiled")]
|
||||
public static void UpdateModuleSettings(
|
||||
string moduleName,
|
||||
string defaultSettingsContent,
|
||||
Action<Dictionary<string, object>> updateSettingsAction)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(moduleName);
|
||||
ArgumentNullException.ThrowIfNull(updateSettingsAction);
|
||||
|
||||
try
|
||||
{
|
||||
// Build the path to the module settings file
|
||||
string powerToysSettingsDirectory = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||
"Microsoft",
|
||||
"PowerToys");
|
||||
|
||||
string moduleDirectory = Path.Combine(powerToysSettingsDirectory, moduleName);
|
||||
string settingsPath = Path.Combine(moduleDirectory, "settings.json");
|
||||
|
||||
// Ensure directory exists
|
||||
Directory.CreateDirectory(moduleDirectory);
|
||||
|
||||
// Read existing settings or use default
|
||||
string existingJson = string.Empty;
|
||||
if (File.Exists(settingsPath))
|
||||
{
|
||||
existingJson = File.ReadAllText(settingsPath);
|
||||
}
|
||||
|
||||
Dictionary<string, object>? settings;
|
||||
|
||||
// If file doesn't exist or is empty, create from defaults
|
||||
if (string.IsNullOrWhiteSpace(existingJson))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(defaultSettingsContent))
|
||||
{
|
||||
throw new ArgumentException("Default settings content must be provided when file doesn't exist.", nameof(defaultSettingsContent));
|
||||
}
|
||||
|
||||
settings = JsonSerializer.Deserialize<Dictionary<string, object>>(defaultSettingsContent)
|
||||
?? throw new InvalidOperationException($"Failed to deserialize default settings for {moduleName}");
|
||||
|
||||
Debug.WriteLine($"Created default settings for {moduleName} at {settingsPath}");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Parse existing settings
|
||||
settings = JsonSerializer.Deserialize<Dictionary<string, object>>(existingJson)
|
||||
?? throw new InvalidOperationException($"Failed to deserialize existing settings for {moduleName}");
|
||||
|
||||
Debug.WriteLine($"Loaded existing settings for {moduleName} from {settingsPath}");
|
||||
}
|
||||
|
||||
// Apply the update action to modify settings
|
||||
updateSettingsAction(settings);
|
||||
|
||||
// Serialize and save the updated settings using SettingsUtils
|
||||
string updatedJson = JsonSerializer.Serialize(settings, IndentedJsonOptions);
|
||||
SettingsUtils.SaveSettings(updatedJson, moduleName);
|
||||
|
||||
Debug.WriteLine($"Successfully updated settings for {moduleName}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"ERROR in UpdateModuleSettings for {moduleName}: {ex.Message}");
|
||||
throw new InvalidOperationException($"Failed to update settings for {moduleName}: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<PublishAot>true</PublishAot>
|
||||
<InvariantGlobalization>true</InvariantGlobalization>
|
||||
<TargetFramework>net9.0-windows10.0.26100.0</TargetFramework>
|
||||
<TargetFramework>net9.0-windows10.0.22621.0</TargetFramework>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<PublishTrimmed>false</PublishTrimmed>
|
||||
</PropertyGroup>
|
||||
@@ -21,8 +21,4 @@
|
||||
<PackageReference Include="CoenM.ImageSharp.ImageHash" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\settings-ui\Settings.UI.Library\Settings.UI.Library.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -89,6 +89,7 @@ namespace powertoys_gpo
|
||||
const std::wstring POLICY_ALLOW_ADVANCED_PASTE_AZURE_AI_INFERENCE = L"AllowAdvancedPasteAzureAIInference";
|
||||
const std::wstring POLICY_ALLOW_ADVANCED_PASTE_MISTRAL = L"AllowAdvancedPasteMistral";
|
||||
const std::wstring POLICY_ALLOW_ADVANCED_PASTE_GOOGLE = L"AllowAdvancedPasteGoogle";
|
||||
const std::wstring POLICY_ALLOW_ADVANCED_PASTE_ANTHROPIC = L"AllowAdvancedPasteAnthropic";
|
||||
const std::wstring POLICY_ALLOW_ADVANCED_PASTE_OLLAMA = L"AllowAdvancedPasteOllama";
|
||||
const std::wstring POLICY_ALLOW_ADVANCED_PASTE_FOUNDRY_LOCAL = L"AllowAdvancedPasteFoundryLocal";
|
||||
const std::wstring POLICY_MWB_CLIPBOARD_SHARING_ENABLED = L"MwbClipboardSharingEnabled";
|
||||
@@ -614,6 +615,11 @@ namespace powertoys_gpo
|
||||
return getConfiguredValue(POLICY_ALLOW_ADVANCED_PASTE_GOOGLE);
|
||||
}
|
||||
|
||||
inline gpo_rule_configured_t getAllowedAdvancedPasteAnthropicValue()
|
||||
{
|
||||
return getConfiguredValue(POLICY_ALLOW_ADVANCED_PASTE_ANTHROPIC);
|
||||
}
|
||||
|
||||
inline gpo_rule_configured_t getAllowedAdvancedPasteOllamaValue()
|
||||
{
|
||||
return getConfiguredValue(POLICY_ALLOW_ADVANCED_PASTE_OLLAMA);
|
||||
|
||||
@@ -59,8 +59,10 @@
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" />
|
||||
<!-- Including MessagePack to force version, since it's used by StreamJsonRpc but contains vulnerabilities. After StreamJsonRpc updates the version of MessagePack, we can upgrade StreamJsonRpc instead. -->
|
||||
<PackageReference Include="MessagePack" />
|
||||
<PackageReference Include="Microsoft.SemanticKernel.Connectors.Amazon" />
|
||||
<PackageReference Include="Microsoft.SemanticKernel.Connectors.AzureAIInference" />
|
||||
<PackageReference Include="Microsoft.SemanticKernel.Connectors.Google" />
|
||||
<PackageReference Include="Microsoft.SemanticKernel.Connectors.HuggingFace" />
|
||||
<PackageReference Include="Microsoft.SemanticKernel.Connectors.MistralAI" />
|
||||
<PackageReference Include="Microsoft.SemanticKernel.Connectors.Ollama" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" />
|
||||
|
||||
@@ -11,6 +11,12 @@ using AdvancedPaste.Settings;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.ChatCompletion;
|
||||
using Microsoft.SemanticKernel.Connectors.Amazon;
|
||||
using Microsoft.SemanticKernel.Connectors.AzureAIInference;
|
||||
using Microsoft.SemanticKernel.Connectors.Google;
|
||||
using Microsoft.SemanticKernel.Connectors.HuggingFace;
|
||||
using Microsoft.SemanticKernel.Connectors.MistralAI;
|
||||
using Microsoft.SemanticKernel.Connectors.Ollama;
|
||||
using Microsoft.SemanticKernel.Connectors.OpenAI;
|
||||
|
||||
namespace AdvancedPaste.Services;
|
||||
|
||||
@@ -181,6 +181,8 @@ namespace AdvancedPaste.Services.CustomActions
|
||||
{
|
||||
AIServiceType.Onnx => false,
|
||||
AIServiceType.Ollama => false,
|
||||
AIServiceType.Anthropic => false,
|
||||
AIServiceType.AmazonBedrock => false,
|
||||
_ => true,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -11,8 +11,10 @@ using AdvancedPaste.Models;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.ChatCompletion;
|
||||
using Microsoft.SemanticKernel.Connectors.Amazon;
|
||||
using Microsoft.SemanticKernel.Connectors.AzureAIInference;
|
||||
using Microsoft.SemanticKernel.Connectors.Google;
|
||||
using Microsoft.SemanticKernel.Connectors.HuggingFace;
|
||||
using Microsoft.SemanticKernel.Connectors.MistralAI;
|
||||
using Microsoft.SemanticKernel.Connectors.Ollama;
|
||||
using Microsoft.SemanticKernel.Connectors.OpenAI;
|
||||
@@ -27,8 +29,11 @@ namespace AdvancedPaste.Services.CustomActions
|
||||
AIServiceType.AzureOpenAI,
|
||||
AIServiceType.Mistral,
|
||||
AIServiceType.Google,
|
||||
AIServiceType.HuggingFace,
|
||||
AIServiceType.AzureAIInference,
|
||||
AIServiceType.Ollama,
|
||||
AIServiceType.Anthropic,
|
||||
AIServiceType.AmazonBedrock,
|
||||
};
|
||||
|
||||
public static PasteAIProviderRegistration Registration { get; } = new(SupportedTypes, config => new SemanticKernelPasteProvider(config));
|
||||
@@ -137,12 +142,21 @@ namespace AdvancedPaste.Services.CustomActions
|
||||
case AIServiceType.Google:
|
||||
kernelBuilder.AddGoogleAIGeminiChatCompletion(_config.Model, apiKey: apiKey);
|
||||
break;
|
||||
case AIServiceType.HuggingFace:
|
||||
kernelBuilder.AddHuggingFaceChatCompletion(_config.Model, apiKey: apiKey);
|
||||
break;
|
||||
case AIServiceType.AzureAIInference:
|
||||
kernelBuilder.AddAzureAIInferenceChatCompletion(_config.Model, apiKey: apiKey, endpoint: new Uri(endpoint));
|
||||
break;
|
||||
case AIServiceType.Ollama:
|
||||
kernelBuilder.AddOllamaChatCompletion(_config.Model, endpoint: new Uri(endpoint));
|
||||
break;
|
||||
case AIServiceType.Anthropic:
|
||||
kernelBuilder.AddBedrockChatCompletionService(_config.Model);
|
||||
break;
|
||||
case AIServiceType.AmazonBedrock:
|
||||
kernelBuilder.AddBedrockChatCompletionService(_config.Model);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotSupportedException($"Provider '{_config.ProviderType}' is not supported by {nameof(SemanticKernelPasteProvider)}");
|
||||
@@ -170,6 +184,8 @@ namespace AdvancedPaste.Services.CustomActions
|
||||
return serviceType switch
|
||||
{
|
||||
AIServiceType.Ollama => false,
|
||||
AIServiceType.Anthropic => false,
|
||||
AIServiceType.AmazonBedrock => false,
|
||||
_ => true,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -156,10 +156,16 @@ public sealed class EnhancedVaultCredentialsProvider : IAICredentialsProvider
|
||||
resource = "https://ai.google.dev/";
|
||||
serviceKey = "google";
|
||||
break;
|
||||
case AIServiceType.HuggingFace:
|
||||
resource = "https://huggingface.co/settings/tokens";
|
||||
serviceKey = "huggingface";
|
||||
break;
|
||||
case AIServiceType.FoundryLocal:
|
||||
case AIServiceType.ML:
|
||||
case AIServiceType.Onnx:
|
||||
case AIServiceType.Ollama:
|
||||
case AIServiceType.Anthropic:
|
||||
case AIServiceType.AmazonBedrock:
|
||||
return null;
|
||||
default:
|
||||
return null;
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace AdvancedPaste.Telemetry;
|
||||
public class AdvancedPasteEndpointUsageEvent : EventBase, IEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the AI provider type (e.g., OpenAI, AzureOpenAI, Google).
|
||||
/// Gets or sets the AI provider type (e.g., OpenAI, AzureOpenAI, Anthropic).
|
||||
/// </summary>
|
||||
public string ProviderType { get; set; }
|
||||
|
||||
|
||||
@@ -798,6 +798,7 @@ namespace AdvancedPaste.ViewModels
|
||||
AIServiceType.AzureAIInference => PowerToys.GPOWrapper.GPOWrapper.GetAllowedAdvancedPasteAzureAIInferenceValue() != PowerToys.GPOWrapper.GpoRuleConfigured.Disabled,
|
||||
AIServiceType.Mistral => PowerToys.GPOWrapper.GPOWrapper.GetAllowedAdvancedPasteMistralValue() != PowerToys.GPOWrapper.GpoRuleConfigured.Disabled,
|
||||
AIServiceType.Google => PowerToys.GPOWrapper.GPOWrapper.GetAllowedAdvancedPasteGoogleValue() != PowerToys.GPOWrapper.GpoRuleConfigured.Disabled,
|
||||
AIServiceType.Anthropic => PowerToys.GPOWrapper.GPOWrapper.GetAllowedAdvancedPasteAnthropicValue() != PowerToys.GPOWrapper.GpoRuleConfigured.Disabled,
|
||||
AIServiceType.Ollama => PowerToys.GPOWrapper.GPOWrapper.GetAllowedAdvancedPasteOllamaValue() != PowerToys.GPOWrapper.GpoRuleConfigured.Disabled,
|
||||
AIServiceType.FoundryLocal => PowerToys.GPOWrapper.GPOWrapper.GetAllowedAdvancedPasteFoundryLocalValue() != PowerToys.GPOWrapper.GpoRuleConfigured.Disabled,
|
||||
_ => true, // Allow unknown types by default
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
<PreprocessorDefinitions>_DEBUG;NEWPLUSSHELLEXTENSIONWIN10_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<AdditionalIncludeDirectories>..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories);..\NewShellExtensionContextMenu;$(MSBuildThisFileDirectory)Generated Files</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories);..\NewShellExtensionContextMenu</AdditionalIncludeDirectories>
|
||||
<CompileAsWinRT>false</CompileAsWinRT>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
</ClCompile>
|
||||
@@ -67,7 +67,7 @@
|
||||
<PreprocessorDefinitions>NDEBUG;NEWPLUSSHELLEXTENSIONWIN10_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<AdditionalIncludeDirectories>..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories);..\NewShellExtensionContextMenu;$(MSBuildThisFileDirectory)Generated Files</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories);..\NewShellExtensionContextMenu</AdditionalIncludeDirectories>
|
||||
<CompileAsWinRT>false</CompileAsWinRT>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
</ClCompile>
|
||||
|
||||
@@ -1,548 +0,0 @@
|
||||
//==============================================================================
|
||||
//
|
||||
// Zoomit
|
||||
// Sysinternals - www.sysinternals.com
|
||||
//
|
||||
// GIF recording support using Windows Imaging Component (WIC)
|
||||
//
|
||||
//==============================================================================
|
||||
#include "pch.h"
|
||||
#include "GifRecordingSession.h"
|
||||
#include "CaptureFrameWait.h"
|
||||
#include <shcore.h>
|
||||
|
||||
extern DWORD g_RecordScaling;
|
||||
|
||||
namespace winrt
|
||||
{
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Graphics;
|
||||
using namespace Windows::Graphics::Capture;
|
||||
using namespace Windows::Graphics::DirectX;
|
||||
using namespace Windows::Graphics::DirectX::Direct3D11;
|
||||
using namespace Windows::Storage;
|
||||
using namespace Windows::UI::Composition;
|
||||
}
|
||||
|
||||
namespace util
|
||||
{
|
||||
using namespace robmikh::common::uwp;
|
||||
}
|
||||
|
||||
const float CLEAR_COLOR[] = { 0.0f, 0.0f, 0.0f, 1.0f };
|
||||
|
||||
int32_t EnsureEvenGif(int32_t value)
|
||||
{
|
||||
if (value % 2 == 0)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return value + 1;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// GifRecordingSession::GifRecordingSession
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
GifRecordingSession::GifRecordingSession(
|
||||
winrt::IDirect3DDevice const& device,
|
||||
winrt::GraphicsCaptureItem const& item,
|
||||
RECT const cropRect,
|
||||
uint32_t frameRate,
|
||||
winrt::Streams::IRandomAccessStream const& stream)
|
||||
{
|
||||
m_device = device;
|
||||
m_d3dDevice = GetDXGIInterfaceFromObject<ID3D11Device>(m_device);
|
||||
m_d3dDevice->GetImmediateContext(m_d3dContext.put());
|
||||
m_item = item;
|
||||
m_frameRate = frameRate;
|
||||
m_stream = stream;
|
||||
|
||||
auto itemSize = item.Size();
|
||||
auto inputWidth = EnsureEvenGif(itemSize.Width);
|
||||
auto inputHeight = EnsureEvenGif(itemSize.Height);
|
||||
m_frameWait = std::make_shared<CaptureFrameWait>(m_device, m_item, winrt::SizeInt32{ inputWidth, inputHeight });
|
||||
auto weakPointer{ std::weak_ptr{ m_frameWait } };
|
||||
m_itemClosed = item.Closed(winrt::auto_revoke, [weakPointer](auto&, auto&)
|
||||
{
|
||||
auto sharedPointer{ weakPointer.lock() };
|
||||
if (sharedPointer)
|
||||
{
|
||||
sharedPointer->StopCapture();
|
||||
}
|
||||
});
|
||||
|
||||
// Get crop dimension
|
||||
if ((cropRect.right - cropRect.left) != 0)
|
||||
{
|
||||
m_rcCrop = cropRect;
|
||||
m_frameWait->ShowCaptureBorder(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_rcCrop.left = 0;
|
||||
m_rcCrop.top = 0;
|
||||
m_rcCrop.right = inputWidth;
|
||||
m_rcCrop.bottom = inputHeight;
|
||||
}
|
||||
|
||||
// Apply scaling
|
||||
constexpr int c_minimumSize = 34;
|
||||
auto scaledWidth = MulDiv(m_rcCrop.right - m_rcCrop.left, g_RecordScaling, 100);
|
||||
auto scaledHeight = MulDiv(m_rcCrop.bottom - m_rcCrop.top, g_RecordScaling, 100);
|
||||
m_width = scaledWidth;
|
||||
m_height = scaledHeight;
|
||||
if (m_width < c_minimumSize)
|
||||
{
|
||||
m_width = c_minimumSize;
|
||||
m_height = MulDiv(m_height, m_width, scaledWidth);
|
||||
}
|
||||
if (m_height < c_minimumSize)
|
||||
{
|
||||
m_height = c_minimumSize;
|
||||
m_width = MulDiv(m_width, m_height, scaledHeight);
|
||||
}
|
||||
if (m_width > inputWidth)
|
||||
{
|
||||
m_width = inputWidth;
|
||||
m_height = c_minimumSize, MulDiv(m_height, scaledWidth, m_width);
|
||||
}
|
||||
if (m_height > inputHeight)
|
||||
{
|
||||
m_height = inputHeight;
|
||||
m_width = c_minimumSize, MulDiv(m_width, scaledHeight, m_height);
|
||||
}
|
||||
m_width = EnsureEvenGif(m_width);
|
||||
m_height = EnsureEvenGif(m_height);
|
||||
|
||||
m_frameDelay = (frameRate > 0) ? (100 / frameRate) : 15;
|
||||
|
||||
// Initialize WIC
|
||||
winrt::check_hresult(CoCreateInstance(
|
||||
CLSID_WICImagingFactory,
|
||||
nullptr,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_PPV_ARGS(m_wicFactory.put())));
|
||||
|
||||
// Create WIC stream from IRandomAccessStream
|
||||
winrt::check_hresult(m_wicFactory->CreateStream(m_wicStream.put()));
|
||||
|
||||
// Get the IStream from the IRandomAccessStream
|
||||
winrt::com_ptr<IStream> streamInterop;
|
||||
winrt::check_hresult(CreateStreamOverRandomAccessStream(
|
||||
winrt::get_unknown(stream),
|
||||
IID_PPV_ARGS(streamInterop.put())));
|
||||
winrt::check_hresult(m_wicStream->InitializeFromIStream(streamInterop.get()));
|
||||
|
||||
// Create GIF encoder
|
||||
winrt::check_hresult(m_wicFactory->CreateEncoder(
|
||||
GUID_ContainerFormatGif,
|
||||
nullptr,
|
||||
m_gifEncoder.put()));
|
||||
|
||||
winrt::check_hresult(m_gifEncoder->Initialize(m_wicStream.get(), WICBitmapEncoderNoCache));
|
||||
|
||||
// Set global GIF metadata for looping (NETSCAPE2.0 application extension)
|
||||
try
|
||||
{
|
||||
winrt::com_ptr<IWICMetadataQueryWriter> encoderMetadataWriter;
|
||||
if (SUCCEEDED(m_gifEncoder->GetMetadataQueryWriter(encoderMetadataWriter.put())) && encoderMetadataWriter)
|
||||
{
|
||||
OutputDebugStringW(L"Setting NETSCAPE2.0 looping extension on encoder...\n");
|
||||
|
||||
// Set application extension
|
||||
PROPVARIANT propValue;
|
||||
PropVariantInit(&propValue);
|
||||
propValue.vt = VT_UI1 | VT_VECTOR;
|
||||
propValue.caub.cElems = 11;
|
||||
propValue.caub.pElems = static_cast<UCHAR*>(CoTaskMemAlloc(11));
|
||||
if (propValue.caub.pElems != nullptr)
|
||||
{
|
||||
memcpy(propValue.caub.pElems, "NETSCAPE2.0", 11);
|
||||
HRESULT hr = encoderMetadataWriter->SetMetadataByName(L"/appext/application", &propValue);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
OutputDebugStringW(L"Encoder application extension set successfully\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
OutputDebugStringW(L"Failed to set encoder application extension\n");
|
||||
}
|
||||
PropVariantClear(&propValue);
|
||||
|
||||
// Set loop count (0 = infinite)
|
||||
PropVariantInit(&propValue);
|
||||
propValue.vt = VT_UI1 | VT_VECTOR;
|
||||
propValue.caub.cElems = 5;
|
||||
propValue.caub.pElems = static_cast<UCHAR*>(CoTaskMemAlloc(5));
|
||||
if (propValue.caub.pElems != nullptr)
|
||||
{
|
||||
propValue.caub.pElems[0] = 3;
|
||||
propValue.caub.pElems[1] = 1;
|
||||
propValue.caub.pElems[2] = 0;
|
||||
propValue.caub.pElems[3] = 0;
|
||||
propValue.caub.pElems[4] = 0;
|
||||
hr = encoderMetadataWriter->SetMetadataByName(L"/appext/data", &propValue);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
OutputDebugStringW(L"Encoder loop count set successfully\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
OutputDebugStringW(L"Failed to set encoder loop count\n");
|
||||
}
|
||||
PropVariantClear(&propValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OutputDebugStringW(L"Failed to get encoder metadata writer\n");
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
OutputDebugStringW(L"Warning: Failed to set GIF encoder looping metadata\n");
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// GifRecordingSession::~GifRecordingSession
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
GifRecordingSession::~GifRecordingSession()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// GifRecordingSession::Create
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
std::shared_ptr<GifRecordingSession> GifRecordingSession::Create(
|
||||
winrt::IDirect3DDevice const& device,
|
||||
winrt::GraphicsCaptureItem const& item,
|
||||
RECT const& crop,
|
||||
uint32_t frameRate,
|
||||
winrt::Streams::IRandomAccessStream const& stream)
|
||||
{
|
||||
return std::shared_ptr<GifRecordingSession>(new GifRecordingSession(device, item, crop, frameRate, stream));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// GifRecordingSession::EncodeFrame
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
HRESULT GifRecordingSession::EncodeFrame(ID3D11Texture2D* frameTexture)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create a staging texture for CPU access
|
||||
D3D11_TEXTURE2D_DESC frameDesc;
|
||||
frameTexture->GetDesc(&frameDesc);
|
||||
|
||||
// GIF encoding with palette generation is VERY slow at high resolutions (4K takes 1 second per frame!)
|
||||
|
||||
UINT targetWidth = frameDesc.Width;
|
||||
UINT targetHeight = frameDesc.Height;
|
||||
|
||||
if (frameDesc.Width > static_cast<uint32_t>(m_width) || frameDesc.Height > static_cast<uint32_t>(m_height))
|
||||
{
|
||||
float scaleX = static_cast<float>(m_width) / frameDesc.Width;
|
||||
float scaleY = static_cast<float>(m_height) / frameDesc.Height;
|
||||
float scale = min(scaleX, scaleY);
|
||||
|
||||
targetWidth = static_cast<UINT>(frameDesc.Width * scale);
|
||||
targetHeight = static_cast<UINT>(frameDesc.Height * scale);
|
||||
|
||||
// Ensure even dimensions for GIF
|
||||
targetWidth = (targetWidth / 2) * 2;
|
||||
targetHeight = (targetHeight / 2) * 2;
|
||||
}
|
||||
|
||||
D3D11_TEXTURE2D_DESC stagingDesc = frameDesc;
|
||||
stagingDesc.Usage = D3D11_USAGE_STAGING;
|
||||
stagingDesc.BindFlags = 0;
|
||||
stagingDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
stagingDesc.MiscFlags = 0;
|
||||
|
||||
winrt::com_ptr<ID3D11Texture2D> stagingTexture;
|
||||
winrt::check_hresult(m_d3dDevice->CreateTexture2D(&stagingDesc, nullptr, stagingTexture.put()));
|
||||
|
||||
// Copy the frame to staging texture
|
||||
m_d3dContext->CopyResource(stagingTexture.get(), frameTexture);
|
||||
|
||||
// Map the staging texture
|
||||
D3D11_MAPPED_SUBRESOURCE mappedResource;
|
||||
winrt::check_hresult(m_d3dContext->Map(stagingTexture.get(), 0, D3D11_MAP_READ, 0, &mappedResource));
|
||||
|
||||
// Create a new frame in the GIF
|
||||
winrt::com_ptr<IWICBitmapFrameEncode> frameEncode;
|
||||
winrt::com_ptr<IPropertyBag2> propertyBag;
|
||||
winrt::check_hresult(m_gifEncoder->CreateNewFrame(frameEncode.put(), propertyBag.put()));
|
||||
|
||||
// Initialize the frame encoder with property bag
|
||||
winrt::check_hresult(frameEncode->Initialize(propertyBag.get()));
|
||||
|
||||
// CRITICAL: For GIF, we MUST set size and pixel format BEFORE WriteSource
|
||||
// Use target dimensions (may be downsampled)
|
||||
winrt::check_hresult(frameEncode->SetSize(targetWidth, targetHeight));
|
||||
|
||||
// Set the pixel format to 8-bit indexed (required for GIF)
|
||||
WICPixelFormatGUID pixelFormat = GUID_WICPixelFormat8bppIndexed;
|
||||
winrt::check_hresult(frameEncode->SetPixelFormat(&pixelFormat));
|
||||
|
||||
// Create a WIC bitmap from the BGRA texture data
|
||||
winrt::com_ptr<IWICBitmap> sourceBitmap;
|
||||
winrt::check_hresult(m_wicFactory->CreateBitmapFromMemory(
|
||||
frameDesc.Width,
|
||||
frameDesc.Height,
|
||||
GUID_WICPixelFormat32bppBGRA,
|
||||
mappedResource.RowPitch,
|
||||
frameDesc.Height * mappedResource.RowPitch,
|
||||
static_cast<BYTE*>(mappedResource.pData),
|
||||
sourceBitmap.put()));
|
||||
|
||||
// If we need downsampling, use WIC scaler
|
||||
winrt::com_ptr<IWICBitmapSource> finalSource = sourceBitmap;
|
||||
if (targetWidth != frameDesc.Width || targetHeight != frameDesc.Height)
|
||||
{
|
||||
winrt::com_ptr<IWICBitmapScaler> scaler;
|
||||
winrt::check_hresult(m_wicFactory->CreateBitmapScaler(scaler.put()));
|
||||
winrt::check_hresult(scaler->Initialize(
|
||||
sourceBitmap.get(),
|
||||
targetWidth,
|
||||
targetHeight,
|
||||
WICBitmapInterpolationModeHighQualityCubic));
|
||||
finalSource = scaler;
|
||||
|
||||
OutputDebugStringW((L"Downsampled from " + std::to_wstring(frameDesc.Width) + L"x" + std::to_wstring(frameDesc.Height) +
|
||||
L" to " + std::to_wstring(targetWidth) + L"x" + std::to_wstring(targetHeight) + L"\n").c_str());
|
||||
}
|
||||
|
||||
// Use WriteSource - WIC will handle the BGRA to 8bpp indexed conversion
|
||||
winrt::check_hresult(frameEncode->WriteSource(finalSource.get(), nullptr));
|
||||
|
||||
try
|
||||
{
|
||||
winrt::com_ptr<IWICMetadataQueryWriter> frameMetadataWriter;
|
||||
if (SUCCEEDED(frameEncode->GetMetadataQueryWriter(frameMetadataWriter.put())) && frameMetadataWriter)
|
||||
{
|
||||
// Set the frame delay in the metadata (in hundredths of a second)
|
||||
PROPVARIANT propValue;
|
||||
PropVariantInit(&propValue);
|
||||
propValue.vt = VT_UI2;
|
||||
propValue.uiVal = static_cast<USHORT>(m_frameDelay);
|
||||
frameMetadataWriter->SetMetadataByName(L"/grctlext/Delay", &propValue);
|
||||
PropVariantClear(&propValue);
|
||||
|
||||
// Set disposal method (2 = restore to background, needed for animation)
|
||||
PropVariantInit(&propValue);
|
||||
propValue.vt = VT_UI1;
|
||||
propValue.bVal = 2; // Disposal method: restore to background color
|
||||
frameMetadataWriter->SetMetadataByName(L"/grctlext/Disposal", &propValue);
|
||||
PropVariantClear(&propValue);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Metadata setting failed, continue anyway
|
||||
OutputDebugStringW(L"Warning: Failed to set GIF frame metadata\n");
|
||||
}
|
||||
|
||||
// Commit the frame
|
||||
OutputDebugStringW(L"About to commit frame to encoder...\n");
|
||||
winrt::check_hresult(frameEncode->Commit());
|
||||
OutputDebugStringW(L"Frame committed successfully\n");
|
||||
|
||||
// Unmap the staging texture
|
||||
m_d3dContext->Unmap(stagingTexture.get(), 0);
|
||||
|
||||
// Increment and log frame count
|
||||
m_frameCount++;
|
||||
OutputDebugStringW((L"GIF Frame #" + std::to_wstring(m_frameCount) + L" fully encoded and committed\n").c_str());
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
catch (const winrt::hresult_error& error)
|
||||
{
|
||||
OutputDebugStringW(error.message().c_str());
|
||||
return error.code();
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// GifRecordingSession::StartAsync
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
winrt::IAsyncAction GifRecordingSession::StartAsync()
|
||||
{
|
||||
auto expected = false;
|
||||
if (m_isRecording.compare_exchange_strong(expected, true))
|
||||
{
|
||||
auto self = shared_from_this();
|
||||
|
||||
try
|
||||
{
|
||||
// Start capturing frames
|
||||
auto frameStartTime = std::chrono::high_resolution_clock::now();
|
||||
int captureAttempts = 0;
|
||||
int successfulCaptures = 0;
|
||||
int duplicatedFrames = 0;
|
||||
|
||||
// Keep track of the last frame to duplicate when needed
|
||||
winrt::com_ptr<ID3D11Texture2D> lastCroppedTexture;
|
||||
|
||||
while (m_isRecording && !m_closed)
|
||||
{
|
||||
captureAttempts++;
|
||||
auto frame = m_frameWait->TryGetNextFrame();
|
||||
|
||||
winrt::com_ptr<ID3D11Texture2D> croppedTexture;
|
||||
|
||||
if (frame)
|
||||
{
|
||||
successfulCaptures++;
|
||||
auto contentSize = frame->ContentSize;
|
||||
auto frameTexture = GetDXGIInterfaceFromObject<ID3D11Texture2D>(frame->FrameTexture);
|
||||
D3D11_TEXTURE2D_DESC desc = {};
|
||||
frameTexture->GetDesc(&desc);
|
||||
|
||||
// Use the smaller of the crop size or content size
|
||||
auto width = min(m_rcCrop.right - m_rcCrop.left, contentSize.Width);
|
||||
auto height = min(m_rcCrop.bottom - m_rcCrop.top, contentSize.Height);
|
||||
|
||||
D3D11_TEXTURE2D_DESC croppedDesc = {};
|
||||
croppedDesc.Width = width;
|
||||
croppedDesc.Height = height;
|
||||
croppedDesc.MipLevels = 1;
|
||||
croppedDesc.ArraySize = 1;
|
||||
croppedDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
croppedDesc.SampleDesc.Count = 1;
|
||||
croppedDesc.Usage = D3D11_USAGE_DEFAULT;
|
||||
croppedDesc.BindFlags = D3D11_BIND_RENDER_TARGET;
|
||||
|
||||
winrt::check_hresult(m_d3dDevice->CreateTexture2D(&croppedDesc, nullptr, croppedTexture.put()));
|
||||
|
||||
// Set the content region to copy and clamp the coordinates
|
||||
D3D11_BOX region = {};
|
||||
region.left = std::clamp(m_rcCrop.left, static_cast<LONG>(0), static_cast<LONG>(desc.Width));
|
||||
region.right = std::clamp(m_rcCrop.left + width, static_cast<LONG>(0), static_cast<LONG>(desc.Width));
|
||||
region.top = std::clamp(m_rcCrop.top, static_cast<LONG>(0), static_cast<LONG>(desc.Height));
|
||||
region.bottom = std::clamp(m_rcCrop.top + height, static_cast<LONG>(0), static_cast<LONG>(desc.Height));
|
||||
region.back = 1;
|
||||
|
||||
// Copy the cropped region
|
||||
m_d3dContext->CopySubresourceRegion(
|
||||
croppedTexture.get(),
|
||||
0,
|
||||
0, 0, 0,
|
||||
frameTexture.get(),
|
||||
0,
|
||||
®ion);
|
||||
|
||||
// Save this as the last frame for duplication
|
||||
lastCroppedTexture = croppedTexture;
|
||||
}
|
||||
else if (lastCroppedTexture)
|
||||
{
|
||||
// No new frame, duplicate the last one
|
||||
duplicatedFrames++;
|
||||
croppedTexture = lastCroppedTexture;
|
||||
}
|
||||
|
||||
// Encode the frame (either new or duplicated)
|
||||
if (croppedTexture)
|
||||
{
|
||||
HRESULT hr = EncodeFrame(croppedTexture.get());
|
||||
if (FAILED(hr))
|
||||
{
|
||||
CloseInternal();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for the next frame interval
|
||||
co_await winrt::resume_after(std::chrono::milliseconds(1000 / m_frameRate));
|
||||
}
|
||||
|
||||
// Commit the GIF encoder
|
||||
if (m_gifEncoder)
|
||||
{
|
||||
auto frameEndTime = std::chrono::high_resolution_clock::now();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(frameEndTime - frameStartTime).count();
|
||||
|
||||
OutputDebugStringW(L"Recording stopped. Committing GIF encoder...\n");
|
||||
OutputDebugStringW((L"Total frames captured: " + std::to_wstring(m_frameCount) + L"\n").c_str());
|
||||
OutputDebugStringW((L"Capture attempts: " + std::to_wstring(captureAttempts) + L"\n").c_str());
|
||||
OutputDebugStringW((L"Successful captures: " + std::to_wstring(successfulCaptures) + L"\n").c_str());
|
||||
OutputDebugStringW((L"Duplicated frames: " + std::to_wstring(duplicatedFrames) + L"\n").c_str());
|
||||
OutputDebugStringW((L"Recording duration: " + std::to_wstring(duration) + L"ms\n").c_str());
|
||||
OutputDebugStringW((L"Actual FPS: " + std::to_wstring(m_frameCount * 1000.0 / duration) + L"\n").c_str());
|
||||
|
||||
winrt::check_hresult(m_gifEncoder->Commit());
|
||||
OutputDebugStringW(L"GIF encoder committed successfully\n");
|
||||
}
|
||||
}
|
||||
catch (const winrt::hresult_error& error)
|
||||
{
|
||||
OutputDebugStringW(L"Error in GIF recording: ");
|
||||
OutputDebugStringW(error.message().c_str());
|
||||
OutputDebugStringW(L"\n");
|
||||
|
||||
// Try to commit the encoder even on error
|
||||
if (m_gifEncoder)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_gifEncoder->Commit();
|
||||
}
|
||||
catch (...) {}
|
||||
}
|
||||
|
||||
CloseInternal();
|
||||
}
|
||||
}
|
||||
co_return;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// GifRecordingSession::Close
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
void GifRecordingSession::Close()
|
||||
{
|
||||
auto expected = false;
|
||||
if (m_closed.compare_exchange_strong(expected, true))
|
||||
{
|
||||
expected = true;
|
||||
if (!m_isRecording.compare_exchange_strong(expected, false))
|
||||
{
|
||||
CloseInternal();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_frameWait->StopCapture();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// GifRecordingSession::CloseInternal
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
void GifRecordingSession::CloseInternal()
|
||||
{
|
||||
m_frameWait->StopCapture();
|
||||
m_itemClosed.revoke();
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
//==============================================================================
|
||||
//
|
||||
// Zoomit
|
||||
// Sysinternals - www.sysinternals.com
|
||||
//
|
||||
// GIF recording support using Windows Imaging Component (WIC)
|
||||
//
|
||||
//==============================================================================
|
||||
#pragma once
|
||||
|
||||
#include "CaptureFrameWait.h"
|
||||
#include <d3d11_4.h>
|
||||
#include <vector>
|
||||
|
||||
class GifRecordingSession : public std::enable_shared_from_this<GifRecordingSession>
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] static std::shared_ptr<GifRecordingSession> Create(
|
||||
winrt::Direct3D11::IDirect3DDevice const& device,
|
||||
winrt::GraphicsCaptureItem const& item,
|
||||
RECT const& cropRect,
|
||||
uint32_t frameRate,
|
||||
winrt::Streams::IRandomAccessStream const& stream);
|
||||
~GifRecordingSession();
|
||||
|
||||
winrt::IAsyncAction StartAsync();
|
||||
void EnableCursorCapture(bool enable = true) { m_frameWait->EnableCursorCapture(enable); }
|
||||
void Close();
|
||||
|
||||
private:
|
||||
GifRecordingSession(
|
||||
winrt::Direct3D11::IDirect3DDevice const& device,
|
||||
winrt::Capture::GraphicsCaptureItem const& item,
|
||||
RECT const cropRect,
|
||||
uint32_t frameRate,
|
||||
winrt::Streams::IRandomAccessStream const& stream);
|
||||
void CloseInternal();
|
||||
HRESULT EncodeFrame(ID3D11Texture2D* texture);
|
||||
|
||||
private:
|
||||
winrt::Direct3D11::IDirect3DDevice m_device{ nullptr };
|
||||
winrt::com_ptr<ID3D11Device> m_d3dDevice;
|
||||
winrt::com_ptr<ID3D11DeviceContext> m_d3dContext;
|
||||
RECT m_rcCrop;
|
||||
uint32_t m_frameRate;
|
||||
|
||||
winrt::GraphicsCaptureItem m_item{ nullptr };
|
||||
winrt::GraphicsCaptureItem::Closed_revoker m_itemClosed;
|
||||
std::shared_ptr<CaptureFrameWait> m_frameWait;
|
||||
|
||||
winrt::Streams::IRandomAccessStream m_stream{ nullptr };
|
||||
|
||||
// WIC components for GIF encoding
|
||||
winrt::com_ptr<IWICImagingFactory> m_wicFactory;
|
||||
winrt::com_ptr<IWICStream> m_wicStream;
|
||||
winrt::com_ptr<IWICBitmapEncoder> m_gifEncoder;
|
||||
winrt::com_ptr<IWICMetadataQueryWriter> m_encoderMetadataWriter;
|
||||
|
||||
std::atomic<bool> m_isRecording = false;
|
||||
std::atomic<bool> m_closed = false;
|
||||
|
||||
uint32_t m_frameWidth=0;
|
||||
uint32_t m_frameHeight=0;
|
||||
uint32_t m_frameDelay=0;
|
||||
uint32_t m_frameCount = 0;
|
||||
|
||||
int32_t m_width=0;
|
||||
int32_t m_height=0;
|
||||
};
|
||||
@@ -32,18 +32,18 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE
|
||||
1 TEXTINCLUDE
|
||||
BEGIN
|
||||
"resource.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE
|
||||
2 TEXTINCLUDE
|
||||
BEGIN
|
||||
"#include ""winres.h""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE
|
||||
3 TEXTINCLUDE
|
||||
BEGIN
|
||||
"#include ""binres.rc""\0"
|
||||
END
|
||||
@@ -121,8 +121,8 @@ FONT 8, "MS Shell Dlg", 0, 0, 0x0
|
||||
BEGIN
|
||||
DEFPUSHBUTTON "OK",IDOK,166,306,50,14
|
||||
PUSHBUTTON "Cancel",IDCANCEL,223,306,50,14
|
||||
LTEXT "ZoomIt v9.20",IDC_VERSION,42,7,73,10
|
||||
LTEXT "Copyright <EFBFBD> 2006-2025 Mark Russinovich",IDC_COPYRIGHT,42,17,231,8
|
||||
LTEXT "ZoomIt v9.10",IDC_VERSION,42,7,73,10
|
||||
LTEXT "Copyright © 2006-2025 Mark Russinovich",IDC_COPYRIGHT,42,17,231,8
|
||||
CONTROL "<a HREF=""https://www.sysinternals.com"">Sysinternals - www.sysinternals.com</a>",IDC_LINK,
|
||||
"SysLink",WS_TABSTOP,42,26,150,9
|
||||
ICON "APPICON",IDC_STATIC,12,9,20,20
|
||||
@@ -272,15 +272,13 @@ BEGIN
|
||||
LTEXT "Note: Recording is only available on Windows 10 (version 1903) and higher.",IDC_STATIC,7,77,246,19
|
||||
LTEXT "Scaling:",IDC_STATIC,30,115,26,8
|
||||
COMBOBOX IDC_RECORD_SCALING,61,114,26,30,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | CBS_OEMCONVERT | CBS_SORT | WS_VSCROLL | WS_TABSTOP
|
||||
LTEXT "Format:",IDC_STATIC,30,132,26,8
|
||||
COMBOBOX IDC_RECORD_FORMAT,61,131,60,30,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | CBS_OEMCONVERT | WS_VSCROLL | WS_TABSTOP
|
||||
LTEXT "Frame Rate:",IDC_STATIC,119,115,44,8,NOT WS_VISIBLE
|
||||
COMBOBOX IDC_RECORD_FRAME_RATE,166,114,42,30,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | CBS_OEMCONVERT | CBS_SORT | NOT WS_VISIBLE | WS_VSCROLL | WS_TABSTOP
|
||||
LTEXT "To crop the portion of the screen that will be recorded, enter the hotkey with the Shift key in the opposite mode. ",IDC_STATIC,7,32,246,19
|
||||
LTEXT "To record a specific window, enter the hotkey with the Alt key in the opposite mode.",IDC_STATIC,7,55,246,19
|
||||
CONTROL "&Capture audio input:",IDC_CAPTURE_AUDIO,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,7,149,83,10
|
||||
COMBOBOX IDC_MICROPHONE,81,164,172,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
LTEXT "Microphone:",IDC_STATIC,32,166,47,8
|
||||
CONTROL "&Capture audio input:",IDC_CAPTURE_AUDIO,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,7,137,83,10
|
||||
COMBOBOX IDC_MICROPHONE,81,152,172,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
LTEXT "Microphone:",IDC_STATIC,32,154,47,8
|
||||
END
|
||||
|
||||
SNIP DIALOGEX 0, 0, 260, 68
|
||||
|
||||
@@ -234,7 +234,6 @@
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GifRecordingSession.cpp" />
|
||||
<ClCompile Include="pch.cpp" />
|
||||
<ClCompile Include="SelectRectangle.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Use</PrecompiledHeader>
|
||||
@@ -289,7 +288,6 @@
|
||||
<ClInclude Include="AudioSampleGenerator.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\..\common\sysinternals\Eula\Eula.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)..\ZoomItModuleInterface\Trace.h" />
|
||||
<ClInclude Include="GifRecordingSession.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="Registry.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
|
||||
@@ -54,9 +54,6 @@
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\..\common\sysinternals\WindowsVersions.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GifRecordingSession.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Registry.h">
|
||||
@@ -98,9 +95,6 @@
|
||||
<ClInclude Include="ZoomItSettings.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GifRecordingSession.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="appicon.ico">
|
||||
|
||||
@@ -3,13 +3,6 @@
|
||||
#include "Registry.h"
|
||||
#include "DemoType.h"
|
||||
|
||||
// Recording format enum
|
||||
enum class RecordingFormat
|
||||
{
|
||||
GIF = 0,
|
||||
MP4 = 1
|
||||
};
|
||||
|
||||
DWORD g_ToggleKey = (HOTKEYF_CONTROL << 8)| '1';
|
||||
DWORD g_LiveZoomToggleKey = ((HOTKEYF_CONTROL) << 8)| '4';
|
||||
DWORD g_DrawToggleKey = ((HOTKEYF_CONTROL) << 8)| '2';
|
||||
@@ -45,10 +38,8 @@ BOOLEAN g_DemoTypeUserDriven = false;
|
||||
TCHAR g_DemoTypeFile[MAX_PATH] = {0};
|
||||
DWORD g_DemoTypeSpeedSlider = static_cast<int>(((MIN_TYPING_SPEED - MAX_TYPING_SPEED) / 2) + MAX_TYPING_SPEED);
|
||||
DWORD g_RecordFrameRate = 30;
|
||||
DWORD g_RecordScaling = 100;
|
||||
DWORD g_RecordScalingGIF = 50;
|
||||
DWORD g_RecordScalingMP4 = 100;
|
||||
RecordingFormat g_RecordingFormat = RecordingFormat::GIF;
|
||||
// Divide by 100 to get actual scaling
|
||||
DWORD g_RecordScaling = 100;
|
||||
BOOLEAN g_CaptureAudio = FALSE;
|
||||
TCHAR g_MicrophoneDeviceId[MAX_PATH] = {0};
|
||||
|
||||
@@ -88,9 +79,7 @@ REG_SETTING RegSettings[] = {
|
||||
{ L"ZoominSliderLevel", SETTING_TYPE_DWORD, 0, &g_SliderZoomLevel, static_cast<DOUBLE>(g_SliderZoomLevel) },
|
||||
{ L"Font", SETTING_TYPE_BINARY, sizeof g_LogFont, &g_LogFont, static_cast<DOUBLE>(0) },
|
||||
{ L"RecordFrameRate", SETTING_TYPE_DWORD, 0, &g_RecordFrameRate, static_cast<DOUBLE>(g_RecordFrameRate) },
|
||||
{ L"RecordingFormat", SETTING_TYPE_DWORD, 0, &g_RecordingFormat, static_cast<DOUBLE>(0) },
|
||||
{ L"RecordScalingGIF", SETTING_TYPE_DWORD, 0, &g_RecordScalingGIF, static_cast<DOUBLE>(g_RecordScalingGIF) },
|
||||
{ L"RecordScalingMP4", SETTING_TYPE_DWORD, 0, &g_RecordScalingMP4, static_cast<DOUBLE>(g_RecordScalingMP4) },
|
||||
{ L"RecordScaling", SETTING_TYPE_DWORD, 0, &g_RecordScaling, static_cast<DOUBLE>(g_RecordScaling) },
|
||||
{ L"CaptureAudio", SETTING_TYPE_BOOLEAN, 0, &g_CaptureAudio, static_cast<DOUBLE>(g_CaptureAudio) },
|
||||
{ L"MicrophoneDeviceId", SETTING_TYPE_STRING, sizeof(g_MicrophoneDeviceId), g_MicrophoneDeviceId, static_cast<DOUBLE>(0) },
|
||||
{ NULL, SETTING_TYPE_DWORD, 0, NULL, static_cast<DOUBLE>(0) }
|
||||
|
||||
@@ -93,7 +93,6 @@
|
||||
#define IDC_DEMOTYPE_SLIDER2 1074
|
||||
#define IDC_DEMOTYPE_STATIC2 1074
|
||||
#define IDC_COPYRIGHT 1075
|
||||
#define IDC_RECORD_FORMAT 1076
|
||||
#define IDC_PEN_WIDTH 1105
|
||||
#define IDC_TIMER 1106
|
||||
#define IDC_SMOOTH_IMAGE 1107
|
||||
|
||||
@@ -14,9 +14,6 @@ namespace winrt::PowerToys::ZoomItSettingsInterop::implementation
|
||||
const unsigned int SPECIAL_SEMANTICS_SHORTCUT = 1;
|
||||
const unsigned int SPECIAL_SEMANTICS_COLOR = 2;
|
||||
const unsigned int SPECIAL_SEMANTICS_LOG_FONT = 3;
|
||||
const unsigned int SPECIAL_SEMANTICS_RECORDING_FORMAT = 4;
|
||||
const unsigned int SPECIAL_SEMANTICS_RECORD_SCALING_GIF = 5;
|
||||
const unsigned int SPECIAL_SEMANTICS_RECORD_SCALING_MP4 = 6;
|
||||
|
||||
std::vector<unsigned char> base64_decode(const std::wstring& base64_string)
|
||||
{
|
||||
@@ -75,9 +72,6 @@ namespace winrt::PowerToys::ZoomItSettingsInterop::implementation
|
||||
{ L"PenColor", SPECIAL_SEMANTICS_COLOR },
|
||||
{ L"BreakPenColor", SPECIAL_SEMANTICS_COLOR },
|
||||
{ L"Font", SPECIAL_SEMANTICS_LOG_FONT },
|
||||
{ L"RecordingFormat", SPECIAL_SEMANTICS_RECORDING_FORMAT },
|
||||
{ L"RecordScalingGIF", SPECIAL_SEMANTICS_RECORD_SCALING_GIF },
|
||||
{ L"RecordScalingMP4", SPECIAL_SEMANTICS_RECORD_SCALING_MP4 },
|
||||
};
|
||||
|
||||
hstring ZoomItSettings::LoadSettingsJson()
|
||||
@@ -109,11 +103,6 @@ namespace winrt::PowerToys::ZoomItSettingsInterop::implementation
|
||||
value & 0xFF);
|
||||
_settings.add_property(curSetting->ValueName, hotkey.get_json());
|
||||
}
|
||||
else if (special_semantics->second == SPECIAL_SEMANTICS_RECORDING_FORMAT)
|
||||
{
|
||||
std::wstring formatString = (value == 0) ? L"GIF" : L"MP4";
|
||||
_settings.add_property(L"RecordFormat", formatString);
|
||||
}
|
||||
else if (special_semantics->second == SPECIAL_SEMANTICS_COLOR)
|
||||
{
|
||||
/* PowerToys settings likes colors as #FFFFFF strings.
|
||||
@@ -167,9 +156,6 @@ namespace winrt::PowerToys::ZoomItSettingsInterop::implementation
|
||||
curSetting++;
|
||||
}
|
||||
|
||||
DWORD recordScaling = (g_RecordingFormat == static_cast<RecordingFormat>(0)) ? g_RecordScalingGIF : g_RecordScalingMP4;
|
||||
_settings.add_property<DWORD>(L"RecordScaling", recordScaling);
|
||||
|
||||
return _settings.get_raw_json().Stringify();
|
||||
}
|
||||
|
||||
@@ -181,8 +167,6 @@ namespace winrt::PowerToys::ZoomItSettingsInterop::implementation
|
||||
PowerToysSettings::PowerToyValues valuesFromSettings =
|
||||
PowerToysSettings::PowerToyValues::from_json_string(json, L"ZoomIt");
|
||||
|
||||
bool formatChanged = false;
|
||||
|
||||
PREG_SETTING curSetting = RegSettings;
|
||||
while (curSetting->ValueName)
|
||||
{
|
||||
@@ -228,42 +212,6 @@ namespace winrt::PowerToys::ZoomItSettingsInterop::implementation
|
||||
*static_cast<PDWORD>(curSetting->Setting) = value;
|
||||
}
|
||||
}
|
||||
else if (special_semantics->second == SPECIAL_SEMANTICS_RECORDING_FORMAT)
|
||||
{
|
||||
// Convert string ("GIF" or "MP4") to DWORD enum value (0=GIF, 1=MP4)
|
||||
auto possibleValue = valuesFromSettings.get_string_value(L"RecordFormat");
|
||||
if (possibleValue.has_value())
|
||||
{
|
||||
RecordingFormat oldFormat = g_RecordingFormat;
|
||||
DWORD formatValue = (possibleValue.value() == L"GIF") ? 0 : 1;
|
||||
RecordingFormat newFormat = static_cast<RecordingFormat>(formatValue);
|
||||
|
||||
*static_cast<PDWORD>(curSetting->Setting) = formatValue;
|
||||
|
||||
if (oldFormat != newFormat)
|
||||
{
|
||||
formatChanged = true;
|
||||
|
||||
if (oldFormat == static_cast<RecordingFormat>(0))
|
||||
{
|
||||
g_RecordScalingGIF = g_RecordScaling;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_RecordScalingMP4 = g_RecordScaling;
|
||||
}
|
||||
|
||||
if (newFormat == static_cast<RecordingFormat>(0))
|
||||
{
|
||||
g_RecordScaling = g_RecordScalingGIF;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_RecordScaling = g_RecordScalingMP4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (special_semantics->second == SPECIAL_SEMANTICS_COLOR)
|
||||
{
|
||||
/* PowerToys settings likes colors as #FFFFFF strings.
|
||||
@@ -327,22 +275,6 @@ namespace winrt::PowerToys::ZoomItSettingsInterop::implementation
|
||||
}
|
||||
curSetting++;
|
||||
}
|
||||
|
||||
auto recordScalingValue = valuesFromSettings.get_uint_value(L"RecordScaling");
|
||||
if (recordScalingValue.has_value() && !formatChanged)
|
||||
{
|
||||
g_RecordScaling = recordScalingValue.value();
|
||||
|
||||
if (g_RecordingFormat == static_cast<RecordingFormat>(0))
|
||||
{
|
||||
g_RecordScalingGIF = recordScalingValue.value();
|
||||
}
|
||||
else
|
||||
{
|
||||
g_RecordScalingMP4 = recordScalingValue.value();
|
||||
}
|
||||
}
|
||||
|
||||
reg.WriteRegSettings(RegSettings);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +88,9 @@ namespace Awake.Core.Native
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool GetCursorPos(out Point lpPoint);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern bool ScreenToClient(IntPtr hWnd, ref Point lpPoint);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern bool GetMessage(out Msg lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax);
|
||||
|
||||
|
||||
@@ -61,8 +61,9 @@ namespace Awake.Core
|
||||
|
||||
Bridge.SetForegroundWindow(hWnd);
|
||||
|
||||
// Get cursor position in screen coordinates
|
||||
// Get cursor position and convert it to client coordinates
|
||||
Bridge.GetCursorPos(out Models.Point cursorPos);
|
||||
Bridge.ScreenToClient(hWnd, ref cursorPos);
|
||||
|
||||
// Set menu information
|
||||
MenuInfo menuInfo = new()
|
||||
|
||||
@@ -51,10 +51,10 @@ internal sealed partial class GlobalErrorHandler
|
||||
// without its exception being observed. It is NOT raised immediately
|
||||
// when the Task faults; timing depends on GC finalization.
|
||||
e.SetObserved();
|
||||
HandleException(e.Exception, Context.UnobservedTaskException);
|
||||
HandleException(e.Exception, Context.UnobservedTaskException, isRecoverable: true);
|
||||
}
|
||||
|
||||
private static void HandleException(Exception ex, Context context)
|
||||
private void HandleException(Exception ex, Context context, bool isRecoverable = false)
|
||||
{
|
||||
Logger.LogError($"Unhandled exception detected ({context})", ex);
|
||||
|
||||
@@ -70,25 +70,10 @@ internal sealed partial class GlobalErrorHandler
|
||||
|
||||
StoreReport(report, storeOnDesktop: false);
|
||||
|
||||
string message;
|
||||
string caption;
|
||||
try
|
||||
{
|
||||
message = ResourceLoaderInstance.GetString("GlobalErrorHandler_CrashMessageBox_Message");
|
||||
caption = ResourceLoaderInstance.GetString("GlobalErrorHandler_CrashMessageBox_Caption");
|
||||
}
|
||||
catch
|
||||
{
|
||||
// The resource loader may not be available if the exception occurred during startup.
|
||||
// Fall back to hardcoded strings in that case.
|
||||
message = "Command Palette has encountered a fatal error and must close.";
|
||||
caption = "Command Palette - Fatal error";
|
||||
}
|
||||
|
||||
PInvoke.MessageBox(
|
||||
HWND.Null,
|
||||
message,
|
||||
caption,
|
||||
"Command Palette has encountered a fatal error and must close.\n\nAn error report has been saved to your desktop.",
|
||||
"Unhandled Error",
|
||||
MESSAGEBOX_STYLE.MB_ICONERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@ public sealed partial class MainWindow : WindowEx,
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
HideWindow();
|
||||
|
||||
_hwnd = new HWND(WinRT.Interop.WindowNative.GetWindowHandle(this).ToInt32());
|
||||
|
||||
|
||||
@@ -431,8 +431,8 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
|
||||
<data name="Run_Radio_Position_LastPosition.Content" xml:space="preserve">
|
||||
<value>Last Position</value>
|
||||
<comment>Reopen the window where it was last closed</comment>
|
||||
</data>
|
||||
<data name="TrayMenu_Settings" xml:space="preserve">
|
||||
</data>
|
||||
<data name="TrayMenu_Settings" xml:space="preserve">
|
||||
<value>Settings</value>
|
||||
</data>
|
||||
<data name="TrayMenu_Close" xml:space="preserve">
|
||||
@@ -493,34 +493,28 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
|
||||
<data name="Settings_ExtensionsPage_Reloading_Text.Text" xml:space="preserve">
|
||||
<value>Reloading extensions..</value>
|
||||
</data>
|
||||
<data name="Settings_ExtensionsPage_Banner_Header.Text" xml:space="preserve">
|
||||
<data name="Settings_ExtensionsPage_Banner_Header.Text" xml:space="preserve">
|
||||
<value>Discover more extensions</value>
|
||||
</data>
|
||||
<data name="Settings_ExtensionsPage_Banner_Description.Text" xml:space="preserve">
|
||||
<data name="Settings_ExtensionsPage_Banner_Description.Text" xml:space="preserve">
|
||||
<value>Find more extensions on the Microsoft Store or WinGet.</value>
|
||||
</data>
|
||||
<data name="Settings_ExtensionsPage_Banner_Hyperlink.Content" xml:space="preserve">
|
||||
<data name="Settings_ExtensionsPage_Banner_Hyperlink.Content" xml:space="preserve">
|
||||
<value>Learn how to create your own extensions</value>
|
||||
</data>
|
||||
<data name="Settings_ExtensionsPage_FindExtensions_MicrosoftStore.[using:Microsoft.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
|
||||
<data name="Settings_ExtensionsPage_FindExtensions_MicrosoftStore.[using:Microsoft.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
|
||||
<value>Find extensions on the Microsoft Store</value>
|
||||
</data>
|
||||
<data name="Settings_ExtensionsPage_FindExtensions_MicrosoftStore.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||
<data name="Settings_ExtensionsPage_FindExtensions_MicrosoftStore.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||
<value>Microsoft Store</value>
|
||||
</data>
|
||||
<data name="Settings_ExtensionsPage_FindExtensions_WinGet.[using:Microsoft.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
|
||||
<data name="Settings_ExtensionsPage_FindExtensions_WinGet.[using:Microsoft.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
|
||||
<value>Find extensions on WinGet</value>
|
||||
</data>
|
||||
<data name="Settings_ExtensionsPage_FindExtensions_WinGet.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||
<data name="Settings_ExtensionsPage_FindExtensions_WinGet.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||
<value>Microsoft Store</value>
|
||||
</data>
|
||||
<data name="Settings_ExtensionsPage_SearchBox_Placeholder.PlaceholderText" xml:space="preserve">
|
||||
<data name="Settings_ExtensionsPage_SearchBox_Placeholder.PlaceholderText" xml:space="preserve">
|
||||
<value>Search extensions</value>
|
||||
</data>
|
||||
<data name="GlobalErrorHandler_CrashMessageBox_Message" xml:space="preserve">
|
||||
<value>Command Palette has encountered a fatal error and must close.</value>
|
||||
</data>
|
||||
<data name="GlobalErrorHandler_CrashMessageBox_Caption" xml:space="preserve">
|
||||
<value>Command Palette - Fatal error</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -5,7 +5,7 @@
|
||||
<XesUseOneStoreVersioning>true</XesUseOneStoreVersioning>
|
||||
<XesBaseYearForStoreVersion>2025</XesBaseYearForStoreVersion>
|
||||
<VersionMajor>0</VersionMajor>
|
||||
<VersionMinor>7</VersionMinor>
|
||||
<VersionMinor>6</VersionMinor>
|
||||
<VersionInfoProductName>Microsoft Command Palette</VersionInfoProductName>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
@@ -15,6 +15,8 @@ namespace Microsoft.CmdPal.Ext.Apps.Programs;
|
||||
|
||||
public sealed partial class AppListItem : ListItem
|
||||
{
|
||||
private static readonly Tag _appTag = new("App");
|
||||
|
||||
private readonly AppCommand _appCommand;
|
||||
private readonly AppItem _app;
|
||||
private readonly Lazy<Details> _details;
|
||||
@@ -46,6 +48,7 @@ public sealed partial class AppListItem : ListItem
|
||||
_app = app;
|
||||
Title = app.Name;
|
||||
Subtitle = app.Subtitle;
|
||||
Tags = [_appTag];
|
||||
Icon = Icons.GenericAppIcon;
|
||||
|
||||
MoreCommands = AddPinCommands(_app.Commands!, isPinned);
|
||||
|
||||
@@ -63,7 +63,7 @@ internal sealed partial class SampleListPageWithDetails : ListPage
|
||||
Details = new Details()
|
||||
{
|
||||
Title = "Hero Image Example",
|
||||
HeroImage = new IconInfo("https://m.media-amazon.com/images/M/MV5BNDBkMzVmNGQtYTM2OC00OWRjLTk5OWMtNzNkMDI4NjFjNTZmXkEyXkFqcGdeQXZ3ZXNsZXk@._V1_QL75_UX500_CR0,0,500,281_.jpg"), /* #no-spell-check-line */
|
||||
HeroImage = new IconInfo("https://m.media-amazon.com/images/M/MV5BNDBkMzVmNGQtYTM2OC00OWRjLTk5OWMtNzNkMDI4NjFjNTZmXkEyXkFqcGdeQXZ3ZXNsZXk@._V1_QL75_UX500_CR0,0,500,281_.jpg"),
|
||||
Body = "It is literally an image of a hero",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -10,7 +10,7 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using ImageResizer.Properties;
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using Moq.Protected;
|
||||
@@ -101,9 +101,7 @@ namespace ImageResizer.Models
|
||||
private static ResizeBatch CreateBatch(Action<string> executeAction)
|
||||
{
|
||||
var mock = new Mock<ResizeBatch> { CallBase = true };
|
||||
mock.Protected()
|
||||
.Setup("Execute", ItExpr.IsAny<string>(), ItExpr.IsAny<Settings>())
|
||||
.Callback((string file, Settings settings) => executeAction(file));
|
||||
mock.Protected().Setup("Execute", ItExpr.IsAny<string>()).Callback(executeAction);
|
||||
|
||||
return mock.Object;
|
||||
}
|
||||
|
||||
@@ -87,14 +87,9 @@ namespace ImageResizer.Models
|
||||
public IEnumerable<ResizeError> Process(Action<int, double> reportProgress, CancellationToken cancellationToken)
|
||||
{
|
||||
double total = Files.Count;
|
||||
int completed = 0;
|
||||
var completed = 0;
|
||||
var errors = new ConcurrentBag<ResizeError>();
|
||||
|
||||
// NOTE: Settings.Default is captured once before parallel processing.
|
||||
// Any changes to settings on disk during this batch will NOT be reflected until the next batch.
|
||||
// This improves performance and predictability by avoiding repeated mutex acquisition and behaviour change results in a batch.
|
||||
var settings = Settings.Default;
|
||||
|
||||
// TODO: If we ever switch to Windows.Graphics.Imaging, we can get a lot more throughput by using the async
|
||||
// APIs and a custom SynchronizationContext
|
||||
Parallel.ForEach(
|
||||
@@ -102,12 +97,13 @@ namespace ImageResizer.Models
|
||||
new ParallelOptions
|
||||
{
|
||||
CancellationToken = cancellationToken,
|
||||
MaxDegreeOfParallelism = Environment.ProcessorCount,
|
||||
},
|
||||
(file, state, i) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
Execute(file, settings);
|
||||
Execute(file);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -115,13 +111,14 @@ namespace ImageResizer.Models
|
||||
}
|
||||
|
||||
Interlocked.Increment(ref completed);
|
||||
|
||||
reportProgress(completed, total);
|
||||
});
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
protected virtual void Execute(string file, Settings settings)
|
||||
=> new ResizeOperation(file, DestinationDirectory, settings).Execute();
|
||||
protected virtual void Execute(string file)
|
||||
=> new ResizeOperation(file, DestinationDirectory, Settings.Default).Execute();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -461,42 +461,33 @@ namespace ImageResizer.Properties
|
||||
{
|
||||
}
|
||||
|
||||
if (App.Current?.Dispatcher != null)
|
||||
// Needs to be called on the App UI thread as the properties are bound to the UI.
|
||||
App.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
// Needs to be called on the App UI thread as the properties are bound to the UI.
|
||||
App.Current.Dispatcher.Invoke(() => ReloadCore(jsonSettings));
|
||||
}
|
||||
else
|
||||
{
|
||||
ReloadCore(jsonSettings);
|
||||
}
|
||||
ShrinkOnly = jsonSettings.ShrinkOnly;
|
||||
Replace = jsonSettings.Replace;
|
||||
IgnoreOrientation = jsonSettings.IgnoreOrientation;
|
||||
RemoveMetadata = jsonSettings.RemoveMetadata;
|
||||
JpegQualityLevel = jsonSettings.JpegQualityLevel;
|
||||
PngInterlaceOption = jsonSettings.PngInterlaceOption;
|
||||
TiffCompressOption = jsonSettings.TiffCompressOption;
|
||||
FileName = jsonSettings.FileName;
|
||||
KeepDateModified = jsonSettings.KeepDateModified;
|
||||
FallbackEncoder = jsonSettings.FallbackEncoder;
|
||||
CustomSize = jsonSettings.CustomSize;
|
||||
SelectedSizeIndex = jsonSettings.SelectedSizeIndex;
|
||||
|
||||
if (jsonSettings.Sizes.Count > 0)
|
||||
{
|
||||
Sizes.Clear();
|
||||
Sizes.AddRange(jsonSettings.Sizes);
|
||||
|
||||
// Ensure Ids are unique and handle missing Ids
|
||||
IdRecoveryHelper.RecoverInvalidIds(Sizes);
|
||||
}
|
||||
});
|
||||
|
||||
_jsonMutex.ReleaseMutex();
|
||||
}
|
||||
|
||||
private void ReloadCore(Settings jsonSettings)
|
||||
{
|
||||
ShrinkOnly = jsonSettings.ShrinkOnly;
|
||||
Replace = jsonSettings.Replace;
|
||||
IgnoreOrientation = jsonSettings.IgnoreOrientation;
|
||||
RemoveMetadata = jsonSettings.RemoveMetadata;
|
||||
JpegQualityLevel = jsonSettings.JpegQualityLevel;
|
||||
PngInterlaceOption = jsonSettings.PngInterlaceOption;
|
||||
TiffCompressOption = jsonSettings.TiffCompressOption;
|
||||
FileName = jsonSettings.FileName;
|
||||
KeepDateModified = jsonSettings.KeepDateModified;
|
||||
FallbackEncoder = jsonSettings.FallbackEncoder;
|
||||
CustomSize = jsonSettings.CustomSize;
|
||||
SelectedSizeIndex = jsonSettings.SelectedSizeIndex;
|
||||
|
||||
if (jsonSettings.Sizes.Count > 0)
|
||||
{
|
||||
Sizes.Clear();
|
||||
Sizes.AddRange(jsonSettings.Sizes);
|
||||
|
||||
// Ensure Ids are unique and handle missing Ids
|
||||
IdRecoveryHelper.RecoverInvalidIds(Sizes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,15 +24,13 @@ using Windows.Storage;
|
||||
|
||||
namespace Peek.FilePreviewer.Previewers.MediaPreviewer
|
||||
{
|
||||
public partial class AudioPreviewer : ObservableObject, IDisposable, IAudioPreviewer
|
||||
public partial class AudioPreviewer : ObservableObject, IAudioPreviewer
|
||||
{
|
||||
private MediaSource? _mediaSource;
|
||||
|
||||
[ObservableProperty]
|
||||
private PreviewState _state;
|
||||
|
||||
[ObservableProperty]
|
||||
private AudioPreviewData? _preview;
|
||||
private AudioPreviewData _preview;
|
||||
|
||||
private IFileSystemItem Item { get; }
|
||||
|
||||
@@ -42,6 +40,7 @@ namespace Peek.FilePreviewer.Previewers.MediaPreviewer
|
||||
{
|
||||
Item = file;
|
||||
Dispatcher = DispatcherQueue.GetForCurrentThread();
|
||||
Preview = new AudioPreviewData();
|
||||
}
|
||||
|
||||
public async Task CopyAsync()
|
||||
@@ -64,23 +63,19 @@ namespace Peek.FilePreviewer.Previewers.MediaPreviewer
|
||||
{
|
||||
State = PreviewState.Loading;
|
||||
|
||||
Preview = new AudioPreviewData();
|
||||
|
||||
var thumbnailTask = LoadThumbnailAsync(cancellationToken);
|
||||
var sourceTask = LoadSourceAsync(cancellationToken);
|
||||
var metadataTask = LoadMetadataAsync(cancellationToken);
|
||||
|
||||
await Task.WhenAll(thumbnailTask, sourceTask, metadataTask);
|
||||
|
||||
if (sourceTask.Result && metadataTask.Result)
|
||||
if (!thumbnailTask.Result || !sourceTask.Result || !metadataTask.Result)
|
||||
{
|
||||
State = PreviewState.Loaded;
|
||||
State = PreviewState.Error;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Release all resources on error.
|
||||
Unload();
|
||||
State = PreviewState.Error;
|
||||
State = PreviewState.Loaded;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,15 +88,12 @@ namespace Peek.FilePreviewer.Previewers.MediaPreviewer
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (Preview != null)
|
||||
{
|
||||
var thumbnail = await ThumbnailHelper.GetThumbnailAsync(Item.Path, cancellationToken)
|
||||
?? await ThumbnailHelper.GetIconAsync(Item.Path, cancellationToken);
|
||||
var thumbnail = await ThumbnailHelper.GetThumbnailAsync(Item.Path, cancellationToken)
|
||||
?? await ThumbnailHelper.GetIconAsync(Item.Path, cancellationToken);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
Preview.Thumbnail = thumbnail ?? new SvgImageSource(new Uri("ms-appx:///Assets/Peek/DefaultFileIcon.svg"));
|
||||
}
|
||||
Preview.Thumbnail = thumbnail ?? new SvgImageSource(new Uri("ms-appx:///Assets/Peek/DefaultFileIcon.svg"));
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -118,11 +110,7 @@ namespace Peek.FilePreviewer.Previewers.MediaPreviewer
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (Preview != null)
|
||||
{
|
||||
_mediaSource = MediaSource.CreateFromStorageFile(storageFile);
|
||||
Preview.MediaSource = _mediaSource;
|
||||
}
|
||||
Preview.MediaSource = MediaSource.CreateFromStorageFile(storageFile);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -135,11 +123,6 @@ namespace Peek.FilePreviewer.Previewers.MediaPreviewer
|
||||
|
||||
await Dispatcher.RunOnUiThread(() =>
|
||||
{
|
||||
if (Preview == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
Preview.Title = PropertyStoreHelper.TryGetStringProperty(Item.Path, PropertyKey.MusicTitle)
|
||||
?? Item.Name[..^Item.Extension.Length];
|
||||
@@ -177,22 +160,6 @@ namespace Peek.FilePreviewer.Previewers.MediaPreviewer
|
||||
return _supportedFileTypes.Contains(item.Extension);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Unload();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explicitly unloads the preview and releases file resources.
|
||||
/// </summary>
|
||||
public void Unload()
|
||||
{
|
||||
_mediaSource?.Dispose();
|
||||
_mediaSource = null;
|
||||
Preview = null;
|
||||
}
|
||||
|
||||
private static readonly HashSet<string> _supportedFileTypes = new()
|
||||
{
|
||||
".aac",
|
||||
|
||||
@@ -25,8 +25,6 @@ namespace Peek.FilePreviewer.Previewers
|
||||
{
|
||||
public partial class VideoPreviewer : ObservableObject, IVideoPreviewer, IDisposable
|
||||
{
|
||||
private MediaSource? _mediaSource;
|
||||
|
||||
[ObservableProperty]
|
||||
private MediaSource? preview;
|
||||
|
||||
@@ -58,7 +56,6 @@ namespace Peek.FilePreviewer.Previewers
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Unload();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
@@ -148,8 +145,7 @@ namespace Peek.FilePreviewer.Previewers
|
||||
MissingCodecName = missingCodecName;
|
||||
}
|
||||
|
||||
_mediaSource = MediaSource.CreateFromStorageFile(storageFile);
|
||||
Preview = _mediaSource;
|
||||
Preview = MediaSource.CreateFromStorageFile(storageFile);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -159,16 +155,6 @@ namespace Peek.FilePreviewer.Previewers
|
||||
return !(VideoTask?.Result ?? true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explicitly unloads the preview and releases file resources.
|
||||
/// </summary>
|
||||
public void Unload()
|
||||
{
|
||||
_mediaSource?.Dispose();
|
||||
_mediaSource = null;
|
||||
Preview = null;
|
||||
}
|
||||
|
||||
private static readonly HashSet<string> _supportedFileTypes = new()
|
||||
{
|
||||
".mp4", ".3g2", ".3gp", ".3gp2", ".3gpp", ".asf", ".avi", ".m2t", ".m2ts",
|
||||
|
||||
@@ -9,7 +9,6 @@ using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.PowerToys.UITest;
|
||||
@@ -36,105 +35,6 @@ public class PeekFilePreviewTests : UITestBase
|
||||
{
|
||||
}
|
||||
|
||||
static PeekFilePreviewTests()
|
||||
{
|
||||
FixSettingsFileBeforeTests();
|
||||
}
|
||||
|
||||
private static readonly JsonSerializerOptions IndentedJsonOptions = new() { WriteIndented = true };
|
||||
|
||||
private static void FixSettingsFileBeforeTests()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Default Peek settings
|
||||
string peekSettingsContent = @"{
|
||||
""name"": ""Peek"",
|
||||
""version"": ""1.0"",
|
||||
""properties"": {
|
||||
""ActivationShortcut"": {
|
||||
""win"": false,
|
||||
""ctrl"": true,
|
||||
""alt"": false,
|
||||
""shift"": false,
|
||||
""code"": 32,
|
||||
""key"": ""Space""
|
||||
},
|
||||
""AlwaysRunNotElevated"": {
|
||||
""value"": true
|
||||
},
|
||||
""CloseAfterLosingFocus"": {
|
||||
""value"": false
|
||||
},
|
||||
""ConfirmFileDelete"": {
|
||||
""value"": true
|
||||
},
|
||||
""EnableSpaceToActivate"": {
|
||||
""value"": false
|
||||
}
|
||||
}
|
||||
}";
|
||||
|
||||
// Update Peek module settings
|
||||
SettingsConfigHelper.UpdateModuleSettings(
|
||||
"Peek",
|
||||
peekSettingsContent,
|
||||
(settings) =>
|
||||
{
|
||||
// Get or ensure properties section exists
|
||||
Dictionary<string, object> properties;
|
||||
|
||||
if (settings.TryGetValue("properties", out var propertiesObj))
|
||||
{
|
||||
if (propertiesObj is Dictionary<string, object> dict)
|
||||
{
|
||||
properties = dict;
|
||||
}
|
||||
else if (propertiesObj is JsonElement jsonElem)
|
||||
{
|
||||
properties = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonElem.GetRawText())
|
||||
?? throw new InvalidOperationException("Failed to deserialize properties");
|
||||
}
|
||||
else
|
||||
{
|
||||
properties = new Dictionary<string, object>();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
properties = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
// Update the required properties
|
||||
properties["ActivationShortcut"] = new Dictionary<string, object>
|
||||
{
|
||||
{ "win", false },
|
||||
{ "ctrl", true },
|
||||
{ "alt", false },
|
||||
{ "shift", false },
|
||||
{ "code", 32 },
|
||||
{ "key", "Space" },
|
||||
};
|
||||
|
||||
properties["EnableSpaceToActivate"] = new Dictionary<string, object>
|
||||
{
|
||||
{ "value", false },
|
||||
};
|
||||
|
||||
settings["properties"] = properties;
|
||||
});
|
||||
|
||||
// Disable all modules except Peek in global settings
|
||||
SettingsConfigHelper.ConfigureGlobalModuleSettings("Peek");
|
||||
|
||||
Debug.WriteLine("Successfully updated all settings - Peek shortcut configured and all modules except Peek disabled");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Assert.Fail($"ERROR in FixSettingsFileBeforeTests: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
[TestInitialize]
|
||||
public void TestInitialize()
|
||||
{
|
||||
|
||||
@@ -212,7 +212,7 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_L => new[] { "ļ", "₺" }, // ₺ is in VK_T for other languages, but not VK_L, so we add it here.
|
||||
LetterKey.VK_M => new[] { "ṁ" },
|
||||
LetterKey.VK_N => new[] { "ņ", "ṅ", "ⁿ", "ℕ", "№" },
|
||||
LetterKey.VK_O => new[] { "ȯ", "∅", "⌀" },
|
||||
LetterKey.VK_O => new[] { "ȯ", "∅" },
|
||||
LetterKey.VK_P => new[] { "ṗ", "℗", "∏", "¶" },
|
||||
LetterKey.VK_Q => new[] { "ℚ" },
|
||||
LetterKey.VK_R => new[] { "ṙ", "®", "ℝ" },
|
||||
|
||||
@@ -71,7 +71,6 @@
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalDependencies>windowscodecs.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WindowsPackageType>None</WindowsPackageType>
|
||||
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
|
||||
<WindowsAppSdkUndockedRegFreeWinRTInitialize>true</WindowsAppSdkUndockedRegFreeWinRTInitialize>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
|
||||
@@ -17,7 +17,10 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
FoundryLocal,
|
||||
Mistral,
|
||||
Google,
|
||||
HuggingFace,
|
||||
AzureAIInference,
|
||||
Ollama,
|
||||
Anthropic,
|
||||
AmazonBedrock,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,8 +29,11 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
"ml" or "windowsml" or "winml" => AIServiceType.ML,
|
||||
"mistral" => AIServiceType.Mistral,
|
||||
"google" or "googleai" or "googlegemini" => AIServiceType.Google,
|
||||
"huggingface" => AIServiceType.HuggingFace,
|
||||
"azureaiinference" or "azureinference" => AIServiceType.AzureAIInference,
|
||||
"ollama" => AIServiceType.Ollama,
|
||||
"anthropic" => AIServiceType.Anthropic,
|
||||
"amazonbedrock" or "bedrock" => AIServiceType.AmazonBedrock,
|
||||
_ => AIServiceType.Unknown,
|
||||
};
|
||||
}
|
||||
@@ -49,8 +52,11 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
AIServiceType.ML => "ML",
|
||||
AIServiceType.Mistral => "Mistral",
|
||||
AIServiceType.Google => "Google",
|
||||
AIServiceType.HuggingFace => "HuggingFace",
|
||||
AIServiceType.AzureAIInference => "AzureAIInference",
|
||||
AIServiceType.Ollama => "Ollama",
|
||||
AIServiceType.Anthropic => "Anthropic",
|
||||
AIServiceType.AmazonBedrock => "AmazonBedrock",
|
||||
AIServiceType.Unknown => string.Empty,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(serviceType), serviceType, "Unsupported AI service type."),
|
||||
};
|
||||
@@ -70,8 +76,11 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
AIServiceType.ML => "ml",
|
||||
AIServiceType.Mistral => "mistral",
|
||||
AIServiceType.Google => "google",
|
||||
AIServiceType.HuggingFace => "huggingface",
|
||||
AIServiceType.AzureAIInference => "azureaiinference",
|
||||
AIServiceType.Ollama => "ollama",
|
||||
AIServiceType.Anthropic => "anthropic",
|
||||
AIServiceType.AmazonBedrock => "amazonbedrock",
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -15,6 +15,31 @@ public static class AIServiceTypeRegistry
|
||||
{
|
||||
private static readonly Dictionary<AIServiceType, AIServiceTypeMetadata> MetadataMap = new()
|
||||
{
|
||||
[AIServiceType.AmazonBedrock] = new AIServiceTypeMetadata
|
||||
{
|
||||
ServiceType = AIServiceType.AmazonBedrock,
|
||||
DisplayName = "Amazon Bedrock",
|
||||
IsAvailableInUI = false, // Currently disabled in UI
|
||||
IconPath = "ms-appx:///Assets/Settings/Icons/Models/Bedrock.svg",
|
||||
IsOnlineService = true,
|
||||
LegalDescription = "AdvancedPaste_AmazonBedrock_LegalDescription",
|
||||
TermsLabel = "AdvancedPaste_AmazonBedrock_TermsLabel",
|
||||
TermsUri = new Uri("https://aws.amazon.com/service-terms/"),
|
||||
PrivacyLabel = "AdvancedPaste_AmazonBedrock_PrivacyLabel",
|
||||
PrivacyUri = new Uri("https://aws.amazon.com/privacy/"),
|
||||
},
|
||||
[AIServiceType.Anthropic] = new AIServiceTypeMetadata
|
||||
{
|
||||
ServiceType = AIServiceType.Anthropic,
|
||||
DisplayName = "Anthropic",
|
||||
IconPath = "ms-appx:///Assets/Settings/Icons/Models/Anthropic.svg",
|
||||
IsOnlineService = true,
|
||||
LegalDescription = "AdvancedPaste_Anthropic_LegalDescription",
|
||||
TermsLabel = "AdvancedPaste_Anthropic_TermsLabel",
|
||||
TermsUri = new Uri("https://privacy.claude.com/en/collections/10672567-policies-terms-of-service"),
|
||||
PrivacyLabel = "AdvancedPaste_Anthropic_PrivacyLabel",
|
||||
PrivacyUri = new Uri("https://privacy.claude.com/en/"),
|
||||
},
|
||||
[AIServiceType.AzureAIInference] = new AIServiceTypeMetadata
|
||||
{
|
||||
ServiceType = AIServiceType.AzureAIInference,
|
||||
@@ -60,6 +85,14 @@ public static class AIServiceTypeRegistry
|
||||
PrivacyLabel = "AdvancedPaste_Google_PrivacyLabel",
|
||||
PrivacyUri = new Uri("https://support.google.com/gemini/answer/13594961"),
|
||||
},
|
||||
[AIServiceType.HuggingFace] = new AIServiceTypeMetadata
|
||||
{
|
||||
ServiceType = AIServiceType.HuggingFace,
|
||||
DisplayName = "Hugging Face",
|
||||
IconPath = "ms-appx:///Assets/Settings/Icons/Models/HuggingFace.svg",
|
||||
IsOnlineService = true,
|
||||
IsAvailableInUI = false, // Currently disabled in UI
|
||||
},
|
||||
[AIServiceType.Mistral] = new AIServiceTypeMetadata
|
||||
{
|
||||
ServiceType = AIServiceType.Mistral,
|
||||
|
||||
@@ -87,8 +87,6 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
|
||||
public IntProperty RecordScaling { get; set; }
|
||||
|
||||
public StringProperty RecordFormat { get; set; }
|
||||
|
||||
public BoolProperty CaptureAudio { get; set; }
|
||||
|
||||
public StringProperty MicrophoneDeviceId { get; set; }
|
||||
|
||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1,10 @@
|
||||
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_2092_1822)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.718 2.34668H12.12L16.5 13.3333H14.098L9.718 2.34668ZM4.87933 2.34668H7.39067L11.7707 13.3333H9.32133L8.426 11.026H3.84467L2.94867 13.3327H0.5L4.88 2.34801L4.87933 2.34668ZM7.634 8.98601L6.13533 5.12468L4.63667 8.98668H7.63333L7.634 8.98601Z" fill="black"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_2092_1822">
|
||||
<rect width="16" height="16" fill="white" transform="translate(0.5)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 585 B |
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.5 KiB |
@@ -9,10 +9,7 @@
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
<Button
|
||||
x:Uid="ShortcutConflictControl_Automation"
|
||||
Click="ShortcutConflictBtn_Click"
|
||||
Style="{StaticResource SubtleButtonStyle}">
|
||||
<Button Click="ShortcutConflictBtn_Click" Style="{StaticResource SubtleButtonStyle}">
|
||||
<Grid ColumnSpacing="16">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
|
||||
@@ -111,8 +111,7 @@
|
||||
x:Uid="UpdateAvailableInfoBar"
|
||||
IsClosable="False"
|
||||
IsOpen="{x:Bind ViewModel.IsUpdateAvailable, Mode=OneWay}"
|
||||
Severity="Success"
|
||||
Tapped="UpdateInfoBar_Tapped" />
|
||||
Severity="Success" />
|
||||
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
@@ -145,6 +144,7 @@
|
||||
<Button
|
||||
x:Name="SettingsBtn"
|
||||
x:Uid="SettingsBtn"
|
||||
Padding="8"
|
||||
Click="SettingsBtn_Click"
|
||||
Style="{StaticResource FlyoutButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
|
||||
@@ -10,7 +10,6 @@ using Microsoft.PowerToys.Settings.UI.Controls;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events;
|
||||
using Microsoft.PowerToys.Settings.UI.ViewModels;
|
||||
using Microsoft.PowerToys.Settings.UI.Views;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
@@ -184,14 +183,5 @@ namespace Microsoft.PowerToys.Settings.UI.Flyout
|
||||
// Closing manually the flyout since no window will steal the focus
|
||||
App.GetFlyoutWindow()?.Hide();
|
||||
}
|
||||
|
||||
private void UpdateInfoBar_Tapped(object sender, Microsoft.UI.Xaml.Input.TappedRoutedEventArgs e)
|
||||
{
|
||||
// Hide the flyout before opening settings window
|
||||
App.GetFlyoutWindow()?.Hide();
|
||||
|
||||
// Open Settings window directly to General page where update controls are located
|
||||
App.OpenSettingsWindow(typeof(GeneralPage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,6 +317,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
bool requiresEndpoint = serviceKind is AIServiceType.AzureOpenAI
|
||||
or AIServiceType.AzureAIInference
|
||||
or AIServiceType.Mistral
|
||||
or AIServiceType.HuggingFace
|
||||
or AIServiceType.Ollama;
|
||||
bool requiresDeployment = serviceKind == AIServiceType.AzureOpenAI;
|
||||
bool requiresApiVersion = serviceKind == AIServiceType.AzureOpenAI;
|
||||
@@ -873,6 +874,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
AIServiceType.AzureOpenAI => "https://your-resource.openai.azure.com/",
|
||||
AIServiceType.AzureAIInference => "https://{resource-name}.cognitiveservices.azure.com/",
|
||||
AIServiceType.Mistral => "https://api.mistral.ai/v1/",
|
||||
AIServiceType.HuggingFace => "https://api-inference.huggingface.co/models/",
|
||||
AIServiceType.Ollama => "http://localhost:11434/",
|
||||
_ => "https://your-resource.openai.azure.com/",
|
||||
};
|
||||
|
||||
@@ -240,7 +240,6 @@
|
||||
<ComboBox
|
||||
x:Name="Languages_ComboBox"
|
||||
MinWidth="{StaticResource SettingActionControlMinWidth}"
|
||||
AutomationProperties.Name="{Binding ElementName=LanguageHeader, Path=Header}"
|
||||
DisplayMemberPath="Language"
|
||||
ItemsSource="{Binding Languages, Mode=TwoWay}"
|
||||
SelectedIndex="{Binding LanguagesIndex, Mode=TwoWay}" />
|
||||
@@ -263,10 +262,7 @@
|
||||
<tkcontrols:SettingsCard.Description>
|
||||
<HyperlinkButton x:Uid="Windows_Color_Settings" Click="OpenColorsSettings_Click" />
|
||||
</tkcontrols:SettingsCard.Description>
|
||||
<ComboBox
|
||||
MinWidth="{StaticResource SettingActionControlMinWidth}"
|
||||
AutomationProperties.Name="{Binding ElementName=ColorModeHeader, Path=Header}"
|
||||
SelectedIndex="{x:Bind ViewModel.ThemeIndex, Mode=TwoWay}">
|
||||
<ComboBox MinWidth="{StaticResource SettingActionControlMinWidth}" SelectedIndex="{x:Bind ViewModel.ThemeIndex, Mode=TwoWay}">
|
||||
<ComboBoxItem x:Uid="Radio_Theme_Dark" />
|
||||
<ComboBoxItem x:Uid="Radio_Theme_Light" />
|
||||
<ComboBoxItem x:Uid="Radio_Theme_Default" />
|
||||
@@ -277,16 +273,12 @@
|
||||
Name="GeneralPageRunAtStartUp"
|
||||
x:Uid="GeneralPage_RunAtStartUp"
|
||||
IsEnabled="{x:Bind ViewModel.IsRunAtStartupGPOManaged, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}">
|
||||
<ToggleSwitch
|
||||
x:Uid="ToggleSwitch"
|
||||
AutomationProperties.Name="{Binding ElementName=GeneralPageRunAtStartUp, Path=Header}"
|
||||
IsOn="{x:Bind ViewModel.Startup, Mode=TwoWay}" />
|
||||
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind ViewModel.Startup, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
<controls:GPOInfoControl ShowWarning="{x:Bind ViewModel.IsRunAtStartupGPOManaged, Mode=OneWay}">
|
||||
<tkcontrols:SettingsCard x:Name="ShowSystemTrayIconCard" x:Uid="ShowSystemTrayIcon">
|
||||
<tkcontrols:SettingsCard x:Uid="ShowSystemTrayIcon">
|
||||
<ToggleSwitch
|
||||
x:Uid="ShowSystemTrayIcon_ToggleSwitch"
|
||||
AutomationProperties.Name="{Binding ElementName=ShowSystemTrayIconCard, Path=Header}"
|
||||
IsOn="{x:Bind ViewModel.ShowSysTrayIcon, Mode=TwoWay}"
|
||||
Toggled="ShowSystemTrayIcon_Toggled" />
|
||||
</tkcontrols:SettingsCard>
|
||||
@@ -406,16 +398,12 @@
|
||||
<tkcontrols:SettingsCard.HeaderIcon>
|
||||
<PathIcon Data="M1859 1758q14 23 21 47t7 51q0 40-15 75t-41 61-61 41-75 15H354q-40 0-75-15t-61-41-41-61-15-75q0-27 6-51t21-47l569-992q10-14 10-34V128H640V0h768v128h-128v604q0 19 10 35l569 991zM896 732q0 53-27 99l-331 577h972l-331-577q-27-46-27-99V128H896v604zm799 1188q26 0 44-19t19-45q0-10-2-17t-8-16l-164-287H464l-165 287q-9 15-9 33 0 26 18 45t46 19h1341z" />
|
||||
</tkcontrols:SettingsCard.HeaderIcon>
|
||||
<ToggleSwitch
|
||||
x:Uid="ToggleSwitch"
|
||||
AutomationProperties.Name="{Binding ElementName=GeneralPageEnableExperimentation, Path=Header}"
|
||||
IsOn="{x:Bind ViewModel.EnableExperimentation, Mode=TwoWay}" />
|
||||
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind ViewModel.EnableExperimentation, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
</controls:GPOInfoControl>
|
||||
</controls:SettingsGroup>
|
||||
<controls:SettingsGroup x:Uid="General_DiagnosticsAndFeedback">
|
||||
<tkcontrols:SettingsExpander
|
||||
x:Name="GeneralPageEnableDataDiagnostics"
|
||||
x:Uid="GeneralPage_EnableDataDiagnostics"
|
||||
HeaderIcon="{ui:FontIcon Glyph=}"
|
||||
IsEnabled="{x:Bind ViewModel.IsDataDiagnosticsGPOManaged, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}"
|
||||
@@ -433,19 +421,10 @@
|
||||
NavigateUri="https://aka.ms/powertoys-data-and-privacy-documentation" />
|
||||
</StackPanel>
|
||||
</tkcontrols:SettingsExpander.Description>
|
||||
<ToggleSwitch
|
||||
x:Uid="ToggleSwitch"
|
||||
AutomationProperties.Name="{Binding ElementName=GeneralPageEnableDataDiagnostics, Path=Header}"
|
||||
IsOn="{x:Bind ViewModel.EnableDataDiagnostics, Mode=TwoWay}" />
|
||||
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind ViewModel.EnableDataDiagnostics, Mode=TwoWay}" />
|
||||
<tkcontrols:SettingsExpander.Items>
|
||||
<tkcontrols:SettingsCard
|
||||
x:Name="GeneralPageEnableViewDiagnosticData"
|
||||
x:Uid="GeneralPage_EnableViewDiagnosticData"
|
||||
IsEnabled="{x:Bind ViewModel.EnableDataDiagnostics, Mode=TwoWay}">
|
||||
<ToggleSwitch
|
||||
x:Uid="ToggleSwitch"
|
||||
AutomationProperties.Name="{Binding ElementName=GeneralPageEnableViewDiagnosticData, Path=Header}"
|
||||
IsOn="{x:Bind ViewModel.EnableViewDataDiagnostics, Mode=TwoWay}" />
|
||||
<tkcontrols:SettingsCard x:Uid="GeneralPage_EnableViewDiagnosticData" IsEnabled="{x:Bind ViewModel.EnableDataDiagnostics, Mode=TwoWay}">
|
||||
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind ViewModel.EnableViewDataDiagnostics, Mode=TwoWay}" />
|
||||
<tkcontrols:SettingsCard.Description>
|
||||
<StackPanel Orientation="Vertical">
|
||||
<TextBlock
|
||||
|
||||
@@ -23,10 +23,7 @@
|
||||
Name="NewPlusEnableToggle"
|
||||
x:Uid="NewPlus_Enable_Toggle"
|
||||
HeaderIcon="{ui:BitmapIcon Source=/Assets/Settings/Icons/NewPlus.png}">
|
||||
<ToggleSwitch
|
||||
x:Uid="ToggleSwitch"
|
||||
AutomationProperties.Name="{Binding ElementName=NewPlusEnableToggle, Path=Header}"
|
||||
IsOn="{x:Bind ViewModel.IsEnabled, Mode=TwoWay}" />
|
||||
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind ViewModel.IsEnabled, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
</controls:GPOInfoControl>
|
||||
<controls:SettingsGroup x:Uid="NewPlus_Templates" IsEnabled="{x:Bind ViewModel.IsEnabled, Mode=OneWay}">
|
||||
@@ -63,18 +60,12 @@
|
||||
Name="NewPlusHideFileExtensionToggle"
|
||||
x:Uid="NewPlus_Hide_File_Extension_Toggle"
|
||||
IsEnabled="{x:Bind ViewModel.IsHideFileExtSettingsCardEnabled, Mode=OneWay}">
|
||||
<ToggleSwitch
|
||||
x:Uid="HideFileExtensionToggle"
|
||||
AutomationProperties.Name="{Binding ElementName=NewPlusHideFileExtensionToggle, Path=Header}"
|
||||
IsOn="{x:Bind ViewModel.HideFileExtension, Mode=TwoWay}" />
|
||||
<ToggleSwitch x:Uid="HideFileExtensionToggle" IsOn="{x:Bind ViewModel.HideFileExtension, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
</controls:GPOInfoControl>
|
||||
<tkcontrols:SettingsCard Name="NewPlusHideStartingDigitsToggle" x:Uid="NewPlus_Hide_Starting_Digits_Toggle">
|
||||
|
||||
<ToggleSwitch
|
||||
x:Uid="HideStartingDigitsToggle"
|
||||
AutomationProperties.Name="{Binding ElementName=NewPlusHideStartingDigitsToggle, Path=Header}"
|
||||
IsOn="{x:Bind ViewModel.HideStartingDigits, Mode=TwoWay}" />
|
||||
<ToggleSwitch x:Uid="HideStartingDigitsToggle" IsOn="{x:Bind ViewModel.HideStartingDigits, Mode=TwoWay}" />
|
||||
<tkcontrols:SettingsCard.Description>
|
||||
<TextBlock x:Uid="NewPlus_Hide_Starting_Digits_Description" />
|
||||
</tkcontrols:SettingsCard.Description>
|
||||
@@ -88,10 +79,7 @@
|
||||
x:Uid="NewPlus_Behaviour_Replace_Variables_Toggle"
|
||||
IsEnabled="{x:Bind ViewModel.IsReplaceVariablesSettingsCardEnabled, Mode=OneWay}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="4">
|
||||
<ToggleSwitch
|
||||
x:Uid="ReplaceVariablesToggle"
|
||||
AutomationProperties.Name="{Binding ElementName=NewPlusBehaviourReplaceVariablesToggle, Path=Header}"
|
||||
IsOn="{x:Bind ViewModel.ReplaceVariables, Mode=TwoWay}" />
|
||||
<ToggleSwitch x:Uid="ReplaceVariablesToggle" IsOn="{x:Bind ViewModel.ReplaceVariables, Mode=TwoWay}" />
|
||||
<Button
|
||||
x:Uid="FileCreationButton"
|
||||
Width="28"
|
||||
|
||||
@@ -67,7 +67,6 @@
|
||||
Header="{x:Bind Path=DisplayLabel}">
|
||||
<ComboBox
|
||||
MinWidth="{StaticResource SettingActionControlMinWidth}"
|
||||
AutomationProperties.Name="{x:Bind Path=DisplayLabel}"
|
||||
DisplayMemberPath="Key"
|
||||
ItemsSource="{x:Bind Path=ComboBoxItems}"
|
||||
SelectedValue="{x:Bind ComboBoxValue, Mode=TwoWay}"
|
||||
@@ -111,7 +110,6 @@
|
||||
Header="{x:Bind Path=DisplayLabel}">
|
||||
<NumberBox
|
||||
MinWidth="{StaticResource SettingActionControlMinWidth}"
|
||||
AutomationProperties.Name="{x:Bind Path=DisplayLabel}"
|
||||
LargeChange="{x:Bind NumberBoxLargeChange, Mode=OneWay}"
|
||||
Maximum="{x:Bind NumberBoxMax, Mode=OneWay}"
|
||||
Minimum="{x:Bind NumberBoxMin, Mode=OneWay}"
|
||||
@@ -177,7 +175,6 @@
|
||||
IsEnabled="{x:Bind SecondSettingIsEnabled, Mode=OneWay}">
|
||||
<ComboBox
|
||||
MinWidth="{StaticResource SettingActionControlMinWidth}"
|
||||
AutomationProperties.Name="{x:Bind Path=SecondDisplayLabel}"
|
||||
DisplayMemberPath="Key"
|
||||
ItemsSource="{x:Bind Path=ComboBoxItems}"
|
||||
SelectedValue="{x:Bind ComboBoxValue, Mode=TwoWay}"
|
||||
@@ -293,7 +290,6 @@
|
||||
IsEnabled="{x:Bind SecondSettingIsEnabled, Mode=OneWay}">
|
||||
<NumberBox
|
||||
MinWidth="{StaticResource SettingActionControlMinWidth}"
|
||||
AutomationProperties.Name="{x:Bind Path=SecondDisplayLabel}"
|
||||
LargeChange="{x:Bind NumberBoxLargeChange, Mode=OneWay}"
|
||||
Maximum="{x:Bind NumberBoxMax, Mode=OneWay}"
|
||||
Minimum="{x:Bind NumberBoxMin, Mode=OneWay}"
|
||||
|
||||
@@ -22,10 +22,7 @@
|
||||
Name="PowerRenameToggleEnable"
|
||||
x:Uid="PowerRename_Toggle_Enable"
|
||||
HeaderIcon="{ui:BitmapIcon Source=/Assets/Settings/Icons/PowerRename.png}">
|
||||
<ToggleSwitch
|
||||
x:Uid="ToggleSwitch"
|
||||
AutomationProperties.Name="{Binding ElementName=PowerRenameToggleEnable, Path=Header}"
|
||||
IsOn="{x:Bind ViewModel.IsEnabled, Mode=TwoWay}" />
|
||||
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind ViewModel.IsEnabled, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
</controls:GPOInfoControl>
|
||||
<controls:SettingsGroup x:Uid="PowerRename_ShellIntegration" IsEnabled="{x:Bind ViewModel.IsEnabled, Mode=OneWay}">
|
||||
@@ -33,10 +30,7 @@
|
||||
Name="PowerRenameToggleContextMenu"
|
||||
x:Uid="PowerRename_Toggle_ContextMenu"
|
||||
IsExpanded="False">
|
||||
<ComboBox
|
||||
MinWidth="{StaticResource SettingActionControlMinWidth}"
|
||||
AutomationProperties.Name="{Binding ElementName=PowerRenameToggleContextMenu, Path=Header}"
|
||||
SelectedIndex="{x:Bind ViewModel.EnabledOnContextExtendedMenu, Mode=TwoWay, Converter={StaticResource BoolToComboBoxIndexConverter}}">
|
||||
<ComboBox MinWidth="{StaticResource SettingActionControlMinWidth}" SelectedIndex="{x:Bind ViewModel.EnabledOnContextExtendedMenu, Mode=TwoWay, Converter={StaticResource BoolToComboBoxIndexConverter}}">
|
||||
<ComboBoxItem x:Uid="PowerRename_Toggle_StandardContextMenu" />
|
||||
<ComboBoxItem x:Uid="PowerRename_Toggle_ExtendedContextMenu" />
|
||||
</ComboBox>
|
||||
@@ -59,10 +53,7 @@
|
||||
Name="PowerRenameToggleAutoComplete"
|
||||
x:Uid="PowerRename_Toggle_AutoComplete"
|
||||
IsExpanded="True">
|
||||
<ToggleSwitch
|
||||
x:Uid="ToggleSwitch"
|
||||
AutomationProperties.Name="{Binding ElementName=PowerRenameToggleAutoComplete, Path=Header}"
|
||||
IsOn="{x:Bind ViewModel.MRUEnabled, Mode=TwoWay}" />
|
||||
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind ViewModel.MRUEnabled, Mode=TwoWay}" />
|
||||
<tkcontrols:SettingsExpander.Items>
|
||||
<tkcontrols:SettingsCard
|
||||
Name="PowerRenameToggleMaxDispListNum"
|
||||
@@ -70,7 +61,6 @@
|
||||
IsEnabled="{x:Bind ViewModel.GlobalAndMruEnabled, Mode=OneWay}">
|
||||
<NumberBox
|
||||
MinWidth="{StaticResource SettingActionControlMinWidth}"
|
||||
AutomationProperties.Name="{Binding ElementName=PowerRenameToggleMaxDispListNum, Path=Header}"
|
||||
Maximum="20"
|
||||
Minimum="0"
|
||||
SpinButtonPlacementMode="Compact"
|
||||
@@ -83,18 +73,12 @@
|
||||
Name="PowerRenameToggleRestoreFlagsOnLaunch"
|
||||
x:Uid="PowerRename_Toggle_RestoreFlagsOnLaunch"
|
||||
HeaderIcon="{ui:FontIcon Glyph=}">
|
||||
<ToggleSwitch
|
||||
x:Uid="ToggleSwitch"
|
||||
AutomationProperties.Name="{Binding ElementName=PowerRenameToggleRestoreFlagsOnLaunch, Path=Header}"
|
||||
IsOn="{x:Bind ViewModel.RestoreFlagsOnLaunch, Mode=TwoWay}" />
|
||||
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind ViewModel.RestoreFlagsOnLaunch, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
</controls:SettingsGroup>
|
||||
<controls:SettingsGroup x:Uid="PowerRename_BehaviorHeader" IsEnabled="{x:Bind ViewModel.IsEnabled, Mode=OneWay}">
|
||||
<tkcontrols:SettingsCard Name="PowerRenameToggleUseBoostLib" x:Uid="PowerRename_Toggle_UseBoostLib">
|
||||
<ToggleSwitch
|
||||
x:Uid="ToggleSwitch"
|
||||
AutomationProperties.Name="{Binding ElementName=PowerRenameToggleUseBoostLib, Path=Header}"
|
||||
IsOn="{x:Bind ViewModel.UseBoostLib, Mode=TwoWay}" />
|
||||
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind ViewModel.UseBoostLib, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
</controls:SettingsGroup>
|
||||
</StackPanel>
|
||||
|
||||
@@ -241,12 +241,6 @@
|
||||
<ComboBoxItem>1.0</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</tkcontrols:SettingsCard>
|
||||
<tkcontrols:SettingsCard Name="ZoomItRecordFormat" x:Uid="ZoomIt_Record_Format">
|
||||
<ComboBox MinWidth="{StaticResource SettingActionControlMinWidth}" SelectedIndex="{x:Bind Path=ViewModel.RecordFormatIndex, Mode=TwoWay}">
|
||||
<ComboBoxItem>GIF</ComboBoxItem>
|
||||
<ComboBoxItem>MP4</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</tkcontrols:SettingsCard>
|
||||
<tkcontrols:SettingsCard Name="ZoomItRecordCaptureAudio" x:Uid="ZoomIt_Record_CaptureAudio">
|
||||
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind ViewModel.RecordCaptureAudio, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
|
||||
@@ -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.
|
||||
-->
|
||||
@@ -708,6 +708,15 @@ Please review the placeholder content that represents the final terms and usage
|
||||
<data name="AdvancedPaste_Google_PrivacyLabel" xml:space="preserve">
|
||||
<value>Google Privacy Policy</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_Anthropic_LegalDescription" xml:space="preserve">
|
||||
<value>Your API key connects directly to Anthropic services. By setting up this provider, you agree to comply with Anthropic's usage policies and data handling practices.</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_Anthropic_TermsLabel" xml:space="preserve">
|
||||
<value>Anthropic Terms of Service</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_Anthropic_PrivacyLabel" xml:space="preserve">
|
||||
<value>Anthropic Privacy Policy</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_Mistral_LegalDescription" xml:space="preserve">
|
||||
<value>Your API key connects directly to Mistral services. By setting up this provider, you agree to comply with Mistral's usage policies and data handling practices.</value>
|
||||
</data>
|
||||
@@ -717,6 +726,15 @@ Please review the placeholder content that represents the final terms and usage
|
||||
<data name="AdvancedPaste_Mistral_PrivacyLabel" xml:space="preserve">
|
||||
<value>Mistral Privacy Policy</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_AmazonBedrock_LegalDescription" xml:space="preserve">
|
||||
<value>Your API key connects directly to Amazon services. By setting up this provider, you agree to comply with Amazon's usage policies and data handling practices.</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_AmazonBedrock_TermsLabel" xml:space="preserve">
|
||||
<value>AWS Service Terms</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_AmazonBedrock_PrivacyLabel" xml:space="preserve">
|
||||
<value>AWS Privacy Notice</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_Ollama_TermsLabel" xml:space="preserve">
|
||||
<value>Ollama Terms of Service</value>
|
||||
</data>
|
||||
@@ -2251,7 +2269,7 @@ Take a moment to preview the various utilities listed or view our comprehensive
|
||||
<value>Press the **Restart as administrator** button from the File Locksmith UI to also get information on elevated processes that might be using the files.</value>
|
||||
</data>
|
||||
<data name="Oobe_FileExplorer_HowToEnable.Text" xml:space="preserve">
|
||||
<value>Select **View** which is located at the top of File Explorer, followed by **Show**, and then **Preview pane**.
|
||||
<value>Select **View** which is located at the top of File Explorer, followed by **Show**, and then **Preview pane**.
|
||||
From there, simply click on one of the supported files in the File Explorer and observe the content on the preview pane!</value>
|
||||
</data>
|
||||
<data name="Oobe_HowToCreateMappings.Text" xml:space="preserve">
|
||||
@@ -5028,9 +5046,6 @@ To record a specific window, enter the hotkey with the Alt key in the opposite m
|
||||
<data name="ZoomIt_Record_Scaling.Header" xml:space="preserve">
|
||||
<value>Scaling</value>
|
||||
</data>
|
||||
<data name="ZoomIt_Record_Format.Header" xml:space="preserve">
|
||||
<value>Format</value>
|
||||
</data>
|
||||
<data name="ZoomIt_Record_CaptureAudio.Header" xml:space="preserve">
|
||||
<value>Capture audio input</value>
|
||||
</data>
|
||||
@@ -5565,9 +5580,6 @@ To record a specific window, enter the hotkey with the Alt key in the opposite m
|
||||
<data name="ShortcutConflictControl_Title.Text" xml:space="preserve">
|
||||
<value>Shortcut conflicts</value>
|
||||
</data>
|
||||
<data name="ShortcutConflictControl_Automation.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||
<value>Shortcut conflicts</value>
|
||||
</data>
|
||||
<data name="ShortcutConflictControl_NoConflictsFound" xml:space="preserve">
|
||||
<value>No conflicts found</value>
|
||||
</data>
|
||||
|
||||
@@ -10,8 +10,10 @@ using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.IO.Abstractions;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using global::PowerToys.GPOWrapper;
|
||||
using Microsoft.PowerToys.Settings.UI.Helpers;
|
||||
@@ -55,16 +57,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
|
||||
private Func<string, int> SendConfigMSG { get; }
|
||||
|
||||
private static readonly HashSet<string> CustomActionNonPersistedProperties = new(StringComparer.Ordinal)
|
||||
{
|
||||
nameof(AdvancedPasteCustomAction.CanMoveUp),
|
||||
nameof(AdvancedPasteCustomAction.CanMoveDown),
|
||||
nameof(AdvancedPasteCustomAction.IsValid),
|
||||
nameof(AdvancedPasteCustomAction.HasConflict),
|
||||
nameof(AdvancedPasteCustomAction.Tooltip),
|
||||
nameof(AdvancedPasteCustomAction.SubActions),
|
||||
};
|
||||
|
||||
public AdvancedPasteViewModel(
|
||||
ISettingsUtils settingsUtils,
|
||||
ISettingsRepository<GeneralSettings> settingsRepository,
|
||||
@@ -566,9 +558,11 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
AIServiceType.OpenAI => "gpt-4",
|
||||
AIServiceType.AzureOpenAI => "gpt-4",
|
||||
AIServiceType.Mistral => "mistral-large-latest",
|
||||
AIServiceType.Google => "gemini-2.5-pro",
|
||||
AIServiceType.Google => "gemini-1.5-pro",
|
||||
AIServiceType.AzureAIInference => "gpt-4o-mini",
|
||||
AIServiceType.Ollama => "llama3",
|
||||
AIServiceType.Anthropic => "claude-3-5-sonnet",
|
||||
AIServiceType.AmazonBedrock => "anthropic.claude-3-haiku",
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
@@ -595,6 +589,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
AIServiceType.AzureAIInference => GPOWrapper.GetAllowedAdvancedPasteAzureAIInferenceValue(),
|
||||
AIServiceType.Mistral => GPOWrapper.GetAllowedAdvancedPasteMistralValue(),
|
||||
AIServiceType.Google => GPOWrapper.GetAllowedAdvancedPasteGoogleValue(),
|
||||
AIServiceType.Anthropic => GPOWrapper.GetAllowedAdvancedPasteAnthropicValue(),
|
||||
_ => GpoRuleConfigured.Unavailable,
|
||||
};
|
||||
|
||||
@@ -849,6 +844,9 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
"azureaiinference" => "https://azure.microsoft.com/products/ai-services/ai-inference",
|
||||
"mistral" => "https://console.mistral.ai/account/api-keys",
|
||||
"google" => "https://ai.google.dev/",
|
||||
"huggingface" => "https://huggingface.co/settings/tokens",
|
||||
"anthropic" => "https://console.anthropic.com/account/keys",
|
||||
"amazonbedrock" => "https://aws.amazon.com/bedrock/",
|
||||
"ollama" => "https://ollama.com/",
|
||||
_ => "https://platform.openai.com/api-keys",
|
||||
};
|
||||
@@ -1002,7 +1000,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
|
||||
private void OnCustomActionPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(e.PropertyName) && !CustomActionNonPersistedProperties.Contains(e.PropertyName))
|
||||
if (typeof(AdvancedPasteCustomAction).GetProperty(e.PropertyName).GetCustomAttribute<JsonIgnoreAttribute>() == null)
|
||||
{
|
||||
SaveCustomActions();
|
||||
}
|
||||
|
||||
@@ -652,54 +652,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public int RecordFormatIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_zoomItSettings.Properties.RecordFormat.Value == "GIF")
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (_zoomItSettings.Properties.RecordFormat.Value == "MP4")
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
int format = 0;
|
||||
if (_zoomItSettings.Properties.RecordFormat.Value == "GIF")
|
||||
{
|
||||
format = 0;
|
||||
}
|
||||
|
||||
if (_zoomItSettings.Properties.RecordFormat.Value == "MP4")
|
||||
{
|
||||
format = 1;
|
||||
}
|
||||
|
||||
if (format != value)
|
||||
{
|
||||
_zoomItSettings.Properties.RecordFormat.Value = value == 0 ? "GIF" : "MP4";
|
||||
OnPropertyChanged(nameof(RecordFormatIndex));
|
||||
NotifySettingsChanged();
|
||||
|
||||
// Reload settings to get the new format's scaling value
|
||||
var reloadedSettings = global::PowerToys.ZoomItSettingsInterop.ZoomItSettings.LoadSettingsJson();
|
||||
var reloaded = JsonSerializer.Deserialize<ZoomItSettings>(reloadedSettings, _serializerOptions);
|
||||
if (reloaded != null && reloaded.Properties != null)
|
||||
{
|
||||
_zoomItSettings.Properties.RecordScaling.Value = reloaded.Properties.RecordScaling.Value;
|
||||
OnPropertyChanged(nameof(RecordScalingIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool RecordCaptureAudio
|
||||
{
|
||||
get => _zoomItSettings.Properties.CaptureAudio.Value;
|
||||
|
||||
@@ -92,6 +92,7 @@ void ReportGPOValues(const std::filesystem::path &tmpDir)
|
||||
report << "getAllowedAdvancedPasteAzureAIInferenceValue: " << gpo_rule_configured_to_string(powertoys_gpo::getAllowedAdvancedPasteAzureAIInferenceValue()) << std::endl;
|
||||
report << "getAllowedAdvancedPasteMistralValue: " << gpo_rule_configured_to_string(powertoys_gpo::getAllowedAdvancedPasteMistralValue()) << std::endl;
|
||||
report << "getAllowedAdvancedPasteGoogleValue: " << gpo_rule_configured_to_string(powertoys_gpo::getAllowedAdvancedPasteGoogleValue()) << std::endl;
|
||||
report << "getAllowedAdvancedPasteAnthropicValue: " << gpo_rule_configured_to_string(powertoys_gpo::getAllowedAdvancedPasteAnthropicValue()) << std::endl;
|
||||
report << "getAllowedAdvancedPasteOllamaValue: " << gpo_rule_configured_to_string(powertoys_gpo::getAllowedAdvancedPasteOllamaValue()) << std::endl;
|
||||
report << "getAllowedAdvancedPasteFoundryLocalValue: " << gpo_rule_configured_to_string(powertoys_gpo::getAllowedAdvancedPasteFoundryLocalValue()) << std::endl;
|
||||
report << "getConfiguredMwbClipboardSharingEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredMwbClipboardSharingEnabledValue()) << std::endl;
|
||||
|
||||