Merge remote-tracking branch 'origin/main' into dev/migrie/f/powerdock

This commit is contained in:
Mike Griese
2025-12-03 12:52:29 -06:00
10 changed files with 173 additions and 34 deletions

View File

@@ -33,9 +33,12 @@ The **Light Switch** module lets users automatically transition between light an
> **Note:** Using the shortcut overrides the current schedule until the next transition event. > **Note:** Using the shortcut overrides the current schedule until the next transition event.
* **LightSwitchService** * **LightSwitchService.cpp**
Reads settings and applies theming. Runs a check every minute to ensure the state is correct. is the heart beat of the module. Controls ticking every minute and depending on user actions (manual override, settings changing, etc) triggers the state manager to perform the corresponding operation.
* **LightSwitchStateManager.cpp**
handles updating the state based on the signals sent by LightSwitchService.
* **SettingsXAML/LightSwitch** * **SettingsXAML/LightSwitch**
Provides the settings UI for configuring schedules, syncing location, and customizing shortcuts. Provides the settings UI for configuring schedules, syncing location, and customizing shortcuts.

View File

@@ -64,7 +64,7 @@ public sealed class FoundryLocalModelProvider : ILanguageModelProvider
return new OpenAIClient( return new OpenAIClient(
new ApiKeyCredential("none"), new ApiKeyCredential("none"),
new OpenAIClientOptions { Endpoint = endpointUri }) new OpenAIClientOptions { Endpoint = endpointUri, NetworkTimeout = TimeSpan.FromMinutes(5) })
.GetChatClient(modelId) .GetChatClient(modelId)
.AsIChatClient(); .AsIChatClient();
} }

View File

@@ -215,7 +215,6 @@ public sealed class AdvancedAIKernelService : KernelServiceBase
return new OpenAIPromptExecutionSettings return new OpenAIPromptExecutionSettings
{ {
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto(), FunctionChoiceBehavior = FunctionChoiceBehavior.Auto(),
Temperature = 0.01,
}; };
} }
} }

View File

@@ -146,6 +146,7 @@ public sealed class FoundryLocalPasteProvider : IPasteAIProvider
var options = new ChatOptions var options = new ChatOptions
{ {
ModelId = modelReference, ModelId = modelReference,
MaxOutputTokens = 2048,
}; };
if (!string.IsNullOrWhiteSpace(systemPrompt)) if (!string.IsNullOrWhiteSpace(systemPrompt))

View File

@@ -157,8 +157,6 @@ namespace AdvancedPaste.Services.CustomActions
{ {
AIServiceType.OpenAI or AIServiceType.AzureOpenAI => new OpenAIPromptExecutionSettings AIServiceType.OpenAI or AIServiceType.AzureOpenAI => new OpenAIPromptExecutionSettings
{ {
Temperature = 0.01,
MaxTokens = 2000,
FunctionChoiceBehavior = null, FunctionChoiceBehavior = null,
}, },
_ => new PromptExecutionSettings(), _ => new PromptExecutionSettings(),

View File

@@ -350,7 +350,7 @@ namespace Awake.Core
TrayHelper.TimedIcon, TrayHelper.TimedIcon,
TrayIconAction.Update); TrayIconAction.Update);
}, },
_ => HandleTimerCompletion("timed"), () => HandleTimerCompletion("timed"),
_tokenSource.Token); _tokenSource.Token);
} }

View File

@@ -2,21 +2,52 @@
// The Microsoft Corporation licenses this file to you under the MIT license. // The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System; using Windows.Graphics;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.CmdPal.UI.ViewModels; namespace Microsoft.CmdPal.UI.ViewModels;
public sealed class WindowPosition public sealed class WindowPosition
{ {
/// <summary>
/// Gets or sets left position in device pixels.
/// </summary>
public int X { get; set; } public int X { get; set; }
/// <summary>
/// Gets or sets top position in device pixels.
/// </summary>
public int Y { get; set; } public int Y { get; set; }
/// <summary>
/// Gets or sets width in device pixels.
/// </summary>
public int Width { get; set; } public int Width { get; set; }
/// <summary>
/// Gets or sets height in device pixels.
/// </summary>
public int Height { get; set; } public int Height { get; set; }
/// <summary>
/// Gets or sets width of the screen in device pixels where the window is located.
/// </summary>
public int ScreenWidth { get; set; }
/// <summary>
/// Gets or sets height of the screen in device pixels where the window is located.
/// </summary>
public int ScreenHeight { get; set; }
/// <summary>
/// Gets or sets DPI (dots per inch) of the display where the window is located.
/// </summary>
public int Dpi { get; set; }
/// <summary>
/// Converts the window position properties to a <see cref="RectInt32"/> structure representing the physical window rectangle.
/// </summary>
public RectInt32 ToPhysicalWindowRectangle()
{
return new RectInt32(X, Y, Width, Height);
}
} }

View File

@@ -19,6 +19,7 @@ using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.CmdPal.UI.ViewModels.Messages; using Microsoft.CmdPal.UI.ViewModels.Messages;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.PowerToys.Telemetry; using Microsoft.PowerToys.Telemetry;
using Microsoft.UI;
using Microsoft.UI.Composition; using Microsoft.UI.Composition;
using Microsoft.UI.Composition.SystemBackdrops; using Microsoft.UI.Composition.SystemBackdrops;
using Microsoft.UI.Input; using Microsoft.UI.Input;
@@ -34,6 +35,8 @@ using Windows.UI.WindowManagement;
using Windows.Win32; using Windows.Win32;
using Windows.Win32.Foundation; using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Dwm; using Windows.Win32.Graphics.Dwm;
using Windows.Win32.Graphics.Gdi;
using Windows.Win32.UI.HiDpi;
using Windows.Win32.UI.Input.KeyboardAndMouse; using Windows.Win32.UI.Input.KeyboardAndMouse;
using Windows.Win32.UI.WindowsAndMessaging; using Windows.Win32.UI.WindowsAndMessaging;
using WinRT; using WinRT;
@@ -50,6 +53,9 @@ public sealed partial class MainWindow : WindowEx,
IRecipient<QuitMessage>, IRecipient<QuitMessage>,
IDisposable IDisposable
{ {
private const int DefaultWidth = 800;
private const int DefaultHeight = 480;
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "Stylistically, window messages are WM_")] [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "Stylistically, window messages are WM_")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1306:Field names should begin with lower-case letter", Justification = "Stylistically, window messages are WM_")] [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1306:Field names should begin with lower-case letter", Justification = "Stylistically, window messages are WM_")]
private readonly uint WM_TASKBAR_RESTART; private readonly uint WM_TASKBAR_RESTART;
@@ -176,22 +182,8 @@ public sealed partial class MainWindow : WindowEx,
return; return;
} }
AppWindow.Resize(new SizeInt32 { Width = savedPosition.Width, Height = savedPosition.Height }); var newRect = EnsureWindowIsVisible(savedPosition.ToPhysicalWindowRectangle(), new SizeInt32(savedPosition.ScreenWidth, savedPosition.ScreenHeight), savedPosition.Dpi);
AppWindow.MoveAndResize(newRect);
var savedRect = new RectInt32(savedPosition.X, savedPosition.Y, savedPosition.Width, savedPosition.Height);
var displayArea = DisplayArea.GetFromRect(savedRect, DisplayAreaFallback.Nearest);
var workArea = displayArea.WorkArea;
var maxX = workArea.X + Math.Max(0, workArea.Width - savedPosition.Width);
var maxY = workArea.Y + Math.Max(0, workArea.Height - savedPosition.Height);
var targetPoint = new PointInt32
{
X = Math.Clamp(savedPosition.X, workArea.X, maxX),
Y = Math.Clamp(savedPosition.Y, workArea.Y, maxY),
};
AppWindow.Move(targetPoint);
} }
private void PositionCentered(DisplayArea displayArea) private void PositionCentered(DisplayArea displayArea)
@@ -210,12 +202,16 @@ public sealed partial class MainWindow : WindowEx,
private void UpdateWindowPositionInMemory() private void UpdateWindowPositionInMemory()
{ {
var displayArea = DisplayArea.GetFromWindowId(AppWindow.Id, DisplayAreaFallback.Nearest) ?? DisplayArea.Primary;
_currentWindowPosition = new WindowPosition _currentWindowPosition = new WindowPosition
{ {
X = AppWindow.Position.X, X = AppWindow.Position.X,
Y = AppWindow.Position.Y, Y = AppWindow.Position.Y,
Width = AppWindow.Size.Width, Width = AppWindow.Size.Width,
Height = AppWindow.Size.Height, Height = AppWindow.Size.Height,
Dpi = (int)this.GetDpiForWindow(),
ScreenWidth = displayArea.WorkArea.Width,
ScreenHeight = displayArea.WorkArea.Height,
}; };
} }
@@ -290,8 +286,8 @@ public sealed partial class MainWindow : WindowEx,
{ {
if (target == MonitorBehavior.ToLast) if (target == MonitorBehavior.ToLast)
{ {
AppWindow.Resize(new SizeInt32 { Width = _currentWindowPosition.Width, Height = _currentWindowPosition.Height }); var newRect = EnsureWindowIsVisible(_currentWindowPosition.ToPhysicalWindowRectangle(), new SizeInt32(_currentWindowPosition.ScreenWidth, _currentWindowPosition.ScreenHeight), _currentWindowPosition.Dpi);
AppWindow.Move(new PointInt32 { X = _currentWindowPosition.X, Y = _currentWindowPosition.Y }); AppWindow.MoveAndResize(newRect);
} }
else else
{ {
@@ -398,6 +394,114 @@ public sealed partial class MainWindow : WindowEx,
PInvoke.SetWindowPos(hwnd, HWND.HWND_TOPMOST, 0, 0, 0, 0, SET_WINDOW_POS_FLAGS.SWP_NOMOVE | SET_WINDOW_POS_FLAGS.SWP_NOSIZE); PInvoke.SetWindowPos(hwnd, HWND.HWND_TOPMOST, 0, 0, 0, 0, SET_WINDOW_POS_FLAGS.SWP_NOMOVE | SET_WINDOW_POS_FLAGS.SWP_NOSIZE);
} }
/// <summary>
/// Ensures that the window rectangle is visible on-screen.
/// </summary>
/// <param name="windowRect">The window rectangle in physical pixels.</param>
/// <param name="originalScreen">The desktop area the window was positioned on.</param>
/// <param name="originalDpi">The window's original DPI.</param>
/// <returns>
/// A window rectangle in physical pixels, moved to the nearest display and resized
/// if the DPI has changed.
/// </returns>
private static RectInt32 EnsureWindowIsVisible(RectInt32 windowRect, SizeInt32 originalScreen, int originalDpi)
{
var displayArea = DisplayArea.GetFromRect(windowRect, DisplayAreaFallback.Nearest);
if (displayArea is null)
{
return windowRect;
}
var workArea = displayArea.WorkArea;
if (workArea.Width <= 0 || workArea.Height <= 0)
{
// Fallback, nothing reasonable to do
return windowRect;
}
var effectiveDpi = GetEffectiveDpiFromDisplayId(displayArea);
if (originalDpi <= 0)
{
originalDpi = effectiveDpi; // use current DPI as baseline (no scaling adjustment needed)
}
var hasInvalidSize = windowRect.Width <= 0 || windowRect.Height <= 0;
if (hasInvalidSize)
{
windowRect = new RectInt32(windowRect.X, windowRect.Y, DefaultWidth, DefaultHeight);
}
// If we have a DPI change, scale the window rectangle accordingly
if (effectiveDpi != originalDpi)
{
var scalingFactor = effectiveDpi / (double)originalDpi;
windowRect = new RectInt32(
(int)Math.Round(windowRect.X * scalingFactor),
(int)Math.Round(windowRect.Y * scalingFactor),
(int)Math.Round(windowRect.Width * scalingFactor),
(int)Math.Round(windowRect.Height * scalingFactor));
}
var targetWidth = Math.Min(windowRect.Width, workArea.Width);
var targetHeight = Math.Min(windowRect.Height, workArea.Height);
// Ensure at least some minimum visible area (e.g., 100 pixels)
// This helps prevent the window from being entirely offscreen, regardless of display scaling.
const int minimumVisibleSize = 100;
var isOffscreen =
windowRect.X + minimumVisibleSize > workArea.X + workArea.Width ||
windowRect.X + windowRect.Width - minimumVisibleSize < workArea.X ||
windowRect.Y + minimumVisibleSize > workArea.Y + workArea.Height ||
windowRect.Y + windowRect.Height - minimumVisibleSize < workArea.Y;
// if the work area size has changed, re-center the window
var workAreaSizeChanged =
originalScreen.Width != workArea.Width ||
originalScreen.Height != workArea.Height;
int targetX;
int targetY;
var recenter = isOffscreen || workAreaSizeChanged || hasInvalidSize;
if (recenter)
{
targetX = workArea.X + ((workArea.Width - targetWidth) / 2);
targetY = workArea.Y + ((workArea.Height - targetHeight) / 2);
}
else
{
targetX = windowRect.X;
targetY = windowRect.Y;
}
return new RectInt32(targetX, targetY, targetWidth, targetHeight);
}
private static int GetEffectiveDpiFromDisplayId(DisplayArea displayArea)
{
var effectiveDpi = 96;
var hMonitor = (HMONITOR)Win32Interop.GetMonitorFromDisplayId(displayArea.DisplayId);
if (!hMonitor.IsNull)
{
var hr = PInvoke.GetDpiForMonitor(hMonitor, MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI, out var dpiX, out _);
if (hr == 0)
{
effectiveDpi = (int)dpiX;
}
else
{
Logger.LogWarning($"GetDpiForMonitor failed with HRESULT: 0x{hr.Value:X8} on display {displayArea.DisplayId}");
}
}
if (effectiveDpi <= 0)
{
effectiveDpi = 96;
}
return effectiveDpi;
}
private DisplayArea GetScreen(HWND currentHwnd, MonitorBehavior target) private DisplayArea GetScreen(HWND currentHwnd, MonitorBehavior target)
{ {
// Leaving a note here, in case we ever need it: // Leaving a note here, in case we ever need it:
@@ -554,6 +658,9 @@ public sealed partial class MainWindow : WindowEx,
Y = _currentWindowPosition.Y, Y = _currentWindowPosition.Y,
Width = _currentWindowPosition.Width, Width = _currentWindowPosition.Width,
Height = _currentWindowPosition.Height, Height = _currentWindowPosition.Height,
Dpi = _currentWindowPosition.Dpi,
ScreenWidth = _currentWindowPosition.ScreenWidth,
ScreenHeight = _currentWindowPosition.ScreenHeight,
}; };
SettingsModel.SaveSettings(settings); SettingsModel.SaveSettings(settings);

View File

@@ -24,13 +24,13 @@
<ApplicationIcon>Resources\ImageResizer.ico</ApplicationIcon> <ApplicationIcon>Resources\ImageResizer.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <!-- <PropertyGroup>
<ApplicationManifest>ImageResizerUI.dev.manifest</ApplicationManifest> <ApplicationManifest>ImageResizerUI.dev.manifest</ApplicationManifest>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(CIBuild)'=='true'"> <PropertyGroup Condition="'$(CIBuild)'=='true'">
<ApplicationManifest>ImageResizerUI.prod.manifest</ApplicationManifest> <ApplicationManifest>ImageResizerUI.prod.manifest</ApplicationManifest>
</PropertyGroup> </PropertyGroup> -->
<ItemGroup> <ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx"> <EmbeddedResource Update="Properties\Resources.resx">

View File

@@ -3410,7 +3410,7 @@ Activate by holding the key for the character you want to add an accent to, then
<value>An AI powered tool to put your clipboard content into any format you need, focused towards developer workflows.</value> <value>An AI powered tool to put your clipboard content into any format you need, focused towards developer workflows.</value>
</data> </data>
<data name="AdvancedPaste_EnableAISettingsCardDescription.Text" xml:space="preserve"> <data name="AdvancedPaste_EnableAISettingsCardDescription.Text" xml:space="preserve">
<value>Transform your clipboard content with the power of AI. An cloud or local endpoint is required.</value> <value>Transform your clipboard content with the power of AI. A cloud or local endpoint is required.</value>
</data> </data>
<data name="AdvancedPaste_EnableAISettingsCardDescriptionLearnMore.Content" xml:space="preserve"> <data name="AdvancedPaste_EnableAISettingsCardDescriptionLearnMore.Content" xml:space="preserve">
<value>Learn more</value> <value>Learn more</value>