Compare commits

...

1 Commits

Author SHA1 Message Date
Leilei Zhang
af98244be4 add telemetry and localization 2025-12-25 11:30:40 +08:00
16 changed files with 723 additions and 52 deletions

View File

@@ -444,6 +444,10 @@ _If you want to find diagnostic data events in the source code, these two links
<td>Microsoft.PowerToys.FancyZones_ZoneWindowKeyUp</td>
<td>Occurs when a key is released while interacting with zones.</td>
</tr>
<tr>
<td>Microsoft.PowerToys.FancyZones_CLICommand</td>
<td>Triggered when a FancyZones CLI command is executed, logging the command name and success status.</td>
</tr>
</table>
### FileExplorerAddOns

View File

@@ -8,6 +8,8 @@ using System.CommandLine.Invocation;
using FancyZonesCLI;
using FancyZonesCLI.CommandLine;
using FancyZonesCLI.Telemetry;
using Microsoft.PowerToys.Telemetry;
namespace FancyZonesCLI.CommandLine.Commands;
@@ -24,12 +26,14 @@ internal abstract class FancyZonesBaseCommand : Command
private void InvokeInternal(InvocationContext context)
{
Logger.LogInfo($"Executing command '{Name}'");
bool successful = false;
if (!FancyZonesCliGuards.IsFancyZonesRunning())
{
Logger.LogWarning($"Command '{Name}' blocked: FancyZones is not running");
context.Console.Error.Write($"Error: FancyZones is not running. Start PowerToys (FancyZones) and retry.{Environment.NewLine}");
context.Console.Error.Write($"{Properties.Resources.error_fancyzones_not_running}{Environment.NewLine}");
context.ExitCode = 1;
LogTelemetry(successful: false);
return;
}
@@ -37,6 +41,7 @@ internal abstract class FancyZonesBaseCommand : Command
{
string output = Execute(context);
context.ExitCode = 0;
successful = true;
Logger.LogInfo($"Command '{Name}' completed successfully");
Logger.LogDebug($"Command '{Name}' output length: {output?.Length ?? 0}");
@@ -52,6 +57,28 @@ internal abstract class FancyZonesBaseCommand : Command
Logger.LogError($"Command '{Name}' failed", ex);
context.Console.Error.Write($"Error: {ex.Message}{Environment.NewLine}");
context.ExitCode = 1;
successful = false;
}
finally
{
LogTelemetry(successful);
}
}
private void LogTelemetry(bool successful)
{
try
{
PowerToysTelemetry.Log.WriteEvent(new FancyZonesCLICommandEvent
{
CommandName = Name,
Successful = successful,
});
}
catch (Exception ex)
{
// Don't fail the command if telemetry logging fails
Logger.LogError($"Failed to log telemetry for command '{Name}'", ex);
}
}
}

View File

@@ -15,7 +15,7 @@ namespace FancyZonesCLI.CommandLine.Commands;
internal sealed partial class GetActiveLayoutCommand : FancyZonesBaseCommand
{
public GetActiveLayoutCommand()
: base("get-active-layout", "Show currently active layout")
: base("get-active-layout", Properties.Resources.cmd_get_active_layout)
{
AddAlias("active");
}
@@ -28,7 +28,7 @@ internal sealed partial class GetActiveLayoutCommand : FancyZonesBaseCommand
if (editorParams.Monitors == null || editorParams.Monitors.Count == 0)
{
throw new InvalidOperationException("Could not get current monitor information.");
throw new InvalidOperationException(Properties.Resources.get_active_layout_no_monitor_info);
}
// Read applied layouts.
@@ -36,11 +36,11 @@ internal sealed partial class GetActiveLayoutCommand : FancyZonesBaseCommand
if (appliedLayouts.AppliedLayouts == null)
{
return "No layouts configured.";
return Properties.Resources.get_active_layout_no_layouts;
}
var sb = new System.Text.StringBuilder();
sb.AppendLine("\n=== Active FancyZones Layout(s) ===\n");
sb.AppendLine($"\n{Properties.Resources.get_active_layout_header}\n");
// Show only layouts for currently connected monitors.
for (int i = 0; i < editorParams.Monitors.Count; i++)
@@ -71,7 +71,7 @@ internal sealed partial class GetActiveLayoutCommand : FancyZonesBaseCommand
}
else
{
sb.AppendLine(" No layout applied");
sb.AppendLine(Properties.Resources.get_active_layout_no_layout);
}
if (i < editorParams.Monitors.Count - 1)

View File

@@ -15,7 +15,7 @@ namespace FancyZonesCLI.CommandLine.Commands;
internal sealed partial class GetHotkeysCommand : FancyZonesBaseCommand
{
public GetHotkeysCommand()
: base("get-hotkeys", "List all layout hotkeys")
: base("get-hotkeys", Properties.Resources.cmd_get_hotkeys)
{
AddAlias("hk");
}
@@ -26,12 +26,12 @@ internal sealed partial class GetHotkeysCommand : FancyZonesBaseCommand
if (hotkeys.LayoutHotkeys == null || hotkeys.LayoutHotkeys.Count == 0)
{
return "No hotkeys configured.";
return Properties.Resources.get_hotkeys_no_hotkeys;
}
var sb = new System.Text.StringBuilder();
sb.AppendLine("=== Layout Hotkeys ===\n");
sb.AppendLine("Press Win + Ctrl + Alt + <number> to switch layouts:\n");
sb.AppendLine($"{Properties.Resources.get_hotkeys_header}\n");
sb.AppendLine($"{Properties.Resources.get_hotkeys_instruction}\n");
foreach (var hotkey in hotkeys.LayoutHotkeys.OrderBy(h => h.Key))
{

View File

@@ -15,7 +15,7 @@ namespace FancyZonesCLI.CommandLine.Commands;
internal sealed partial class GetLayoutsCommand : FancyZonesBaseCommand
{
public GetLayoutsCommand()
: base("get-layouts", "List available layouts")
: base("get-layouts", Properties.Resources.cmd_get_layouts)
{
AddAlias("ls");
}
@@ -61,7 +61,7 @@ internal sealed partial class GetLayoutsCommand : FancyZonesBaseCommand
if (customLayouts.CustomLayouts != null)
{
sb.AppendLine(CultureInfo.InvariantCulture, $"=== Custom Layouts ({customLayouts.CustomLayouts.Count} total) ===");
sb.AppendLine(string.Format(CultureInfo.InvariantCulture, Properties.Resources.get_layouts_custom_header, customLayouts.CustomLayouts.Count));
for (int i = 0; i < customLayouts.CustomLayouts.Count; i++)
{
@@ -92,8 +92,8 @@ internal sealed partial class GetLayoutsCommand : FancyZonesBaseCommand
// Add note for canvas layouts.
if (isCanvasLayout)
{
sb.AppendLine("\n Note: Canvas layout preview is approximate.");
sb.AppendLine(" Open FancyZones Editor for precise zone boundaries.");
sb.AppendLine($"\n {Properties.Resources.get_layouts_canvas_note}");
sb.AppendLine($" {Properties.Resources.get_layouts_canvas_detail}");
}
if (i < customLayouts.CustomLayouts.Count - 1)
@@ -102,7 +102,7 @@ internal sealed partial class GetLayoutsCommand : FancyZonesBaseCommand
}
}
sb.AppendLine("\nUse 'FancyZonesCLI.exe set-layout <UUID>' to apply a layout.");
sb.AppendLine($"\n{Properties.Resources.get_layouts_usage}");
}
return sb.ToString().TrimEnd();

View File

@@ -15,7 +15,7 @@ namespace FancyZonesCLI.CommandLine.Commands;
internal sealed partial class GetMonitorsCommand : FancyZonesBaseCommand
{
public GetMonitorsCommand()
: base("get-monitors", "List monitors and FancyZones metadata")
: base("get-monitors", Properties.Resources.cmd_get_monitors)
{
AddAlias("m");
}
@@ -31,19 +31,19 @@ internal sealed partial class GetMonitorsCommand : FancyZonesBaseCommand
}
catch (Exception ex)
{
throw new InvalidOperationException($"Failed to read monitor information. {ex.Message}{Environment.NewLine}Note: Ensure FancyZones is running to get current monitor information.", ex);
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Properties.Resources.get_monitors_error, ex.Message), ex);
}
if (editorParams.Monitors == null || editorParams.Monitors.Count == 0)
{
return "No monitors found.";
return Properties.Resources.get_monitors_no_monitors;
}
// Also read applied layouts to show which layout is active on each monitor.
var appliedLayouts = FancyZonesDataIO.ReadAppliedLayouts();
var sb = new System.Text.StringBuilder();
sb.AppendLine(CultureInfo.InvariantCulture, $"=== Monitors ({editorParams.Monitors.Count} total) ===");
sb.AppendLine(string.Format(CultureInfo.InvariantCulture, Properties.Resources.get_monitors_header, editorParams.Monitors.Count));
sb.AppendLine();
for (int i = 0; i < editorParams.Monitors.Count; i++)

View File

@@ -5,6 +5,7 @@
using System;
using System.CommandLine.Invocation;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Threading;
@@ -13,7 +14,7 @@ namespace FancyZonesCLI.CommandLine.Commands;
internal sealed partial class OpenEditorCommand : FancyZonesBaseCommand
{
public OpenEditorCommand()
: base("open-editor", "Launch FancyZones layout editor")
: base("open-editor", Properties.Resources.cmd_open_editor)
{
AddAlias("e");
}
@@ -38,7 +39,7 @@ internal sealed partial class OpenEditorCommand : FancyZonesBaseCommand
}
catch (Exception ex)
{
throw new InvalidOperationException($"Failed to request FancyZones Editor launch. {ex.Message}", ex);
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Properties.Resources.open_editor_error, ex.Message), ex);
}
}
}

View File

@@ -5,6 +5,7 @@
using System;
using System.CommandLine.Invocation;
using System.Diagnostics;
using System.Globalization;
using System.IO;
namespace FancyZonesCLI.CommandLine.Commands;
@@ -12,7 +13,7 @@ namespace FancyZonesCLI.CommandLine.Commands;
internal sealed partial class OpenSettingsCommand : FancyZonesBaseCommand
{
public OpenSettingsCommand()
: base("open-settings", "Open FancyZones settings page")
: base("open-settings", Properties.Resources.cmd_open_settings)
{
AddAlias("settings");
}
@@ -37,14 +38,14 @@ internal sealed partial class OpenSettingsCommand : FancyZonesBaseCommand
if (process == null)
{
throw new InvalidOperationException("PowerToys.exe failed to start.");
throw new InvalidOperationException(Properties.Resources.open_settings_error_not_started);
}
return string.Empty;
}
catch (Exception ex)
{
throw new InvalidOperationException($"Failed to open FancyZones Settings. {ex.Message}", ex);
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Properties.Resources.open_settings_error, ex.Message), ex);
}
}
}

View File

@@ -5,6 +5,7 @@
using System;
using System.CommandLine;
using System.CommandLine.Invocation;
using System.Globalization;
using FancyZonesEditorCommon.Data;
using FancyZonesEditorCommon.Utils;
@@ -16,11 +17,11 @@ internal sealed partial class RemoveHotkeyCommand : FancyZonesBaseCommand
private readonly Argument<int> _key;
public RemoveHotkeyCommand()
: base("remove-hotkey", "Remove hotkey assignment")
: base("remove-hotkey", Properties.Resources.cmd_remove_hotkey)
{
AddAlias("rhk");
_key = new Argument<int>("key", "Hotkey index (0-9)");
_key = new Argument<int>("key", Properties.Resources.remove_hotkey_arg_key);
AddArgument(_key);
}
@@ -33,14 +34,14 @@ internal sealed partial class RemoveHotkeyCommand : FancyZonesBaseCommand
if (hotkeysWrapper.LayoutHotkeys == null)
{
return "No hotkeys configured.";
return Properties.Resources.remove_hotkey_no_hotkeys;
}
var hotkeysList = hotkeysWrapper.LayoutHotkeys;
var removed = hotkeysList.RemoveAll(h => h.Key == key);
if (removed == 0)
{
return $"No hotkey assigned to key {key}";
return string.Format(CultureInfo.InvariantCulture, Properties.Resources.remove_hotkey_not_found, key);
}
// Save.

View File

@@ -6,6 +6,7 @@ using System;
using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.Invocation;
using System.Globalization;
using System.Linq;
using FancyZonesEditorCommon.Data;
@@ -19,12 +20,12 @@ internal sealed partial class SetHotkeyCommand : FancyZonesBaseCommand
private readonly Argument<string> _layout;
public SetHotkeyCommand()
: base("set-hotkey", "Assign hotkey (0-9) to a custom layout")
: base("set-hotkey", Properties.Resources.cmd_set_hotkey)
{
AddAlias("shk");
_key = new Argument<int>("key", "Hotkey index (0-9)");
_layout = new Argument<string>("layout", "Custom layout UUID");
_key = new Argument<int>("key", Properties.Resources.set_hotkey_arg_key);
_layout = new Argument<string>("layout", Properties.Resources.set_hotkey_arg_layout);
AddArgument(_key);
AddArgument(_layout);
@@ -38,7 +39,7 @@ internal sealed partial class SetHotkeyCommand : FancyZonesBaseCommand
if (key < 0 || key > 9)
{
throw new InvalidOperationException("Key must be between 0 and 9.");
throw new InvalidOperationException(Properties.Resources.set_hotkey_error_invalid_key);
}
// Editor only allows assigning hotkeys to existing custom layouts.
@@ -59,7 +60,7 @@ internal sealed partial class SetHotkeyCommand : FancyZonesBaseCommand
if (!matchedLayout.HasValue)
{
throw new InvalidOperationException($"Layout '{layout}' is not a custom layout UUID.");
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Properties.Resources.set_hotkey_error_not_custom, layout));
}
string layoutName = matchedLayout.Value.Name;

View File

@@ -26,14 +26,14 @@ internal sealed partial class SetLayoutCommand : FancyZonesBaseCommand
private readonly Option<bool> _all;
public SetLayoutCommand()
: base("set-layout", "Set layout by UUID or template name")
: base("set-layout", Properties.Resources.cmd_set_layout)
{
AddAlias("s");
_layoutId = new Argument<string>("layout", "Layout UUID or template type (e.g. focus, columns)");
_layoutId = new Argument<string>("layout", Properties.Resources.set_layout_arg_layout);
AddArgument(_layoutId);
_monitor = new Option<int?>(AliasesMonitor, "Apply to monitor N (1-based)");
_monitor = new Option<int?>(AliasesMonitor, Properties.Resources.set_layout_opt_monitor);
_monitor.AddValidator(result =>
{
if (result.Tokens.Count == 0)
@@ -44,11 +44,11 @@ internal sealed partial class SetLayoutCommand : FancyZonesBaseCommand
int? monitor = result.GetValueOrDefault<int?>();
if (monitor.HasValue && monitor.Value < 1)
{
result.ErrorMessage = "Monitor index must be >= 1.";
result.ErrorMessage = Properties.Resources.set_layout_error_monitor_index;
}
});
_all = new Option<bool>(AliasesAll, "Apply to all monitors");
_all = new Option<bool>(AliasesAll, Properties.Resources.set_layout_opt_all);
AddOption(_monitor);
AddOption(_all);
@@ -60,7 +60,7 @@ internal sealed partial class SetLayoutCommand : FancyZonesBaseCommand
if (monitor.HasValue && all)
{
commandResult.ErrorMessage = "Cannot specify both --monitor and --all.";
commandResult.ErrorMessage = Properties.Resources.set_layout_error_both_options;
}
});
}
@@ -97,15 +97,15 @@ internal sealed partial class SetLayoutCommand : FancyZonesBaseCommand
{
if (all)
{
return string.Format(CultureInfo.InvariantCulture, "Layout '{0}' applied to all monitors.", layout);
return string.Format(CultureInfo.InvariantCulture, Properties.Resources.set_layout_success_all, layout);
}
if (monitor.HasValue)
{
return string.Format(CultureInfo.InvariantCulture, "Layout '{0}' applied to monitor {1}.", layout, monitor.Value);
return string.Format(CultureInfo.InvariantCulture, Properties.Resources.set_layout_success_monitor, layout, monitor.Value);
}
return string.Format(CultureInfo.InvariantCulture, "Layout '{0}' applied to monitor 1.", layout);
return string.Format(CultureInfo.InvariantCulture, Properties.Resources.set_layout_success_default, layout);
}
private static (CustomLayouts.CustomLayoutWrapper? TargetCustomLayout, LayoutTemplates.TemplateLayoutWrapper? TargetTemplate) ResolveTargetLayout(string layout)
@@ -127,10 +127,7 @@ internal sealed partial class SetLayoutCommand : FancyZonesBaseCommand
if (!targetCustomLayout.HasValue && !targetTemplate.HasValue)
{
throw new InvalidOperationException(
$"Layout '{layout}' not found{Environment.NewLine}" +
"Tip: For templates, use the type name (e.g., 'focus', 'columns', 'rows', 'grid', 'priority-grid')" +
$"{Environment.NewLine} For custom layouts, use the UUID from 'get-layouts'");
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Properties.Resources.set_layout_error_not_found, layout));
}
return (targetCustomLayout, targetTemplate);
@@ -197,7 +194,7 @@ internal sealed partial class SetLayoutCommand : FancyZonesBaseCommand
int monitorIndex = monitor.Value - 1; // Convert to 0-based.
if (monitorIndex < 0 || monitorIndex >= editorParams.Monitors.Count)
{
throw new InvalidOperationException($"Monitor {monitor.Value} not found. Available monitors: 1-{editorParams.Monitors.Count}");
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Properties.Resources.set_layout_error_monitor_not_found, monitor.Value, editorParams.Monitors.Count));
}
result.Add(monitorIndex);
@@ -250,7 +247,7 @@ internal sealed partial class SetLayoutCommand : FancyZonesBaseCommand
if (newLayouts.Count == 0)
{
throw new InvalidOperationException("Internal error - no monitors to update.");
throw new InvalidOperationException(Properties.Resources.set_layout_error_no_monitors);
}
return newLayouts;
@@ -306,7 +303,7 @@ internal sealed partial class SetLayoutCommand : FancyZonesBaseCommand
}
else
{
throw new InvalidOperationException($"Unsupported custom layout type '{targetCustomLayout.Value.Type}'.");
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Properties.Resources.set_layout_error_unsupported_type, targetCustomLayout.Value.Type));
}
return (
@@ -329,7 +326,7 @@ internal sealed partial class SetLayoutCommand : FancyZonesBaseCommand
targetTemplate.Value.SensitivityRadius);
}
throw new InvalidOperationException("Internal error - no layout selected.");
throw new InvalidOperationException(Properties.Resources.set_layout_error_no_layout);
}
private static AppliedLayouts.AppliedLayoutsListWrapper MergeWithHistoricalLayouts(

View File

@@ -13,7 +13,7 @@
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)</OutputPath>
<AssemblyName>FancyZonesCLI</AssemblyName>
<NoWarn>$(NoWarn);SA1500;SA1402;CA1852</NoWarn>
<NoWarn>$(NoWarn);SA1500;SA1402;CA1852;CA1863;CA1305</NoWarn>
</PropertyGroup>
<ItemGroup>
@@ -24,6 +24,22 @@
<ItemGroup>
<ProjectReference Include="..\FancyZonesEditorCommon\FancyZonesEditorCommon.csproj" />
<ProjectReference Include="..\..\..\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DependentUpon>Resources.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<!-- Force using WindowsDesktop runtime to ensure consistent dll versions with other projects -->

View File

@@ -0,0 +1,353 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace FancyZonesCLI.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("FancyZonesCLI.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
internal static string error_fancyzones_not_running {
get {
return ResourceManager.GetString("error_fancyzones_not_running", resourceCulture);
}
}
internal static string cmd_get_active_layout {
get {
return ResourceManager.GetString("cmd_get_active_layout", resourceCulture);
}
}
internal static string get_active_layout_no_monitor_info {
get {
return ResourceManager.GetString("get_active_layout_no_monitor_info", resourceCulture);
}
}
internal static string get_active_layout_no_layouts {
get {
return ResourceManager.GetString("get_active_layout_no_layouts", resourceCulture);
}
}
internal static string get_active_layout_header {
get {
return ResourceManager.GetString("get_active_layout_header", resourceCulture);
}
}
internal static string get_active_layout_no_layout {
get {
return ResourceManager.GetString("get_active_layout_no_layout", resourceCulture);
}
}
internal static string cmd_get_layouts {
get {
return ResourceManager.GetString("cmd_get_layouts", resourceCulture);
}
}
internal static string get_layouts_templates_header {
get {
return ResourceManager.GetString("get_layouts_templates_header", resourceCulture);
}
}
internal static string get_layouts_custom_header {
get {
return ResourceManager.GetString("get_layouts_custom_header", resourceCulture);
}
}
internal static string get_layouts_canvas_note {
get {
return ResourceManager.GetString("get_layouts_canvas_note", resourceCulture);
}
}
internal static string get_layouts_canvas_detail {
get {
return ResourceManager.GetString("get_layouts_canvas_detail", resourceCulture);
}
}
internal static string get_layouts_usage {
get {
return ResourceManager.GetString("get_layouts_usage", resourceCulture);
}
}
internal static string cmd_get_monitors {
get {
return ResourceManager.GetString("cmd_get_monitors", resourceCulture);
}
}
internal static string get_monitors_error {
get {
return ResourceManager.GetString("get_monitors_error", resourceCulture);
}
}
internal static string get_monitors_no_monitors {
get {
return ResourceManager.GetString("get_monitors_no_monitors", resourceCulture);
}
}
internal static string get_monitors_header {
get {
return ResourceManager.GetString("get_monitors_header", resourceCulture);
}
}
internal static string cmd_set_layout {
get {
return ResourceManager.GetString("cmd_set_layout", resourceCulture);
}
}
internal static string set_layout_arg_layout {
get {
return ResourceManager.GetString("set_layout_arg_layout", resourceCulture);
}
}
internal static string set_layout_opt_monitor {
get {
return ResourceManager.GetString("set_layout_opt_monitor", resourceCulture);
}
}
internal static string set_layout_opt_all {
get {
return ResourceManager.GetString("set_layout_opt_all", resourceCulture);
}
}
internal static string set_layout_error_monitor_index {
get {
return ResourceManager.GetString("set_layout_error_monitor_index", resourceCulture);
}
}
internal static string set_layout_error_both_options {
get {
return ResourceManager.GetString("set_layout_error_both_options", resourceCulture);
}
}
internal static string set_layout_error_not_found {
get {
return ResourceManager.GetString("set_layout_error_not_found", resourceCulture);
}
}
internal static string set_layout_error_monitor_not_found {
get {
return ResourceManager.GetString("set_layout_error_monitor_not_found", resourceCulture);
}
}
internal static string set_layout_error_no_monitors {
get {
return ResourceManager.GetString("set_layout_error_no_monitors", resourceCulture);
}
}
internal static string set_layout_error_unsupported_type {
get {
return ResourceManager.GetString("set_layout_error_unsupported_type", resourceCulture);
}
}
internal static string set_layout_error_no_layout {
get {
return ResourceManager.GetString("set_layout_error_no_layout", resourceCulture);
}
}
internal static string set_layout_success_all {
get {
return ResourceManager.GetString("set_layout_success_all", resourceCulture);
}
}
internal static string set_layout_success_monitor {
get {
return ResourceManager.GetString("set_layout_success_monitor", resourceCulture);
}
}
internal static string set_layout_success_default {
get {
return ResourceManager.GetString("set_layout_success_default", resourceCulture);
}
}
internal static string cmd_open_editor {
get {
return ResourceManager.GetString("cmd_open_editor", resourceCulture);
}
}
internal static string open_editor_error {
get {
return ResourceManager.GetString("open_editor_error", resourceCulture);
}
}
internal static string cmd_open_settings {
get {
return ResourceManager.GetString("cmd_open_settings", resourceCulture);
}
}
internal static string open_settings_error_not_started {
get {
return ResourceManager.GetString("open_settings_error_not_started", resourceCulture);
}
}
internal static string open_settings_error {
get {
return ResourceManager.GetString("open_settings_error", resourceCulture);
}
}
internal static string cmd_set_hotkey {
get {
return ResourceManager.GetString("cmd_set_hotkey", resourceCulture);
}
}
internal static string set_hotkey_arg_key {
get {
return ResourceManager.GetString("set_hotkey_arg_key", resourceCulture);
}
}
internal static string set_hotkey_arg_layout {
get {
return ResourceManager.GetString("set_hotkey_arg_layout", resourceCulture);
}
}
internal static string set_hotkey_error_invalid_key {
get {
return ResourceManager.GetString("set_hotkey_error_invalid_key", resourceCulture);
}
}
internal static string set_hotkey_error_not_custom {
get {
return ResourceManager.GetString("set_hotkey_error_not_custom", resourceCulture);
}
}
internal static string cmd_remove_hotkey {
get {
return ResourceManager.GetString("cmd_remove_hotkey", resourceCulture);
}
}
internal static string remove_hotkey_arg_key {
get {
return ResourceManager.GetString("remove_hotkey_arg_key", resourceCulture);
}
}
internal static string remove_hotkey_no_hotkeys {
get {
return ResourceManager.GetString("remove_hotkey_no_hotkeys", resourceCulture);
}
}
internal static string remove_hotkey_not_found {
get {
return ResourceManager.GetString("remove_hotkey_not_found", resourceCulture);
}
}
internal static string cmd_get_hotkeys {
get {
return ResourceManager.GetString("cmd_get_hotkeys", resourceCulture);
}
}
internal static string get_hotkeys_no_hotkeys {
get {
return ResourceManager.GetString("get_hotkeys_no_hotkeys", resourceCulture);
}
}
internal static string get_hotkeys_header {
get {
return ResourceManager.GetString("get_hotkeys_header", resourceCulture);
}
}
internal static string get_hotkeys_instruction {
get {
return ResourceManager.GetString("get_hotkeys_instruction", resourceCulture);
}
}
internal static string editor_params_timeout {
get {
return ResourceManager.GetString("editor_params_timeout", resourceCulture);
}
}
}
}

View File

@@ -0,0 +1,233 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<!-- Base Command -->
<data name="error_fancyzones_not_running" xml:space="preserve">
<value>Error: FancyZones is not running. Start PowerToys (FancyZones) and retry.</value>
</data>
<!-- GetActiveLayoutCommand -->
<data name="cmd_get_active_layout" xml:space="preserve">
<value>Show currently active layout</value>
</data>
<data name="get_active_layout_no_monitor_info" xml:space="preserve">
<value>Could not get current monitor information.</value>
</data>
<data name="get_active_layout_no_layouts" xml:space="preserve">
<value>No layouts configured.</value>
</data>
<data name="get_active_layout_header" xml:space="preserve">
<value>=== Active FancyZones Layout(s) ===</value>
</data>
<data name="get_active_layout_no_layout" xml:space="preserve">
<value> No layout applied</value>
</data>
<!-- GetLayoutsCommand -->
<data name="cmd_get_layouts" xml:space="preserve">
<value>List available layouts</value>
</data>
<data name="get_layouts_templates_header" xml:space="preserve">
<value>=== Built-in Template Layouts ({0} total) ===</value>
</data>
<data name="get_layouts_custom_header" xml:space="preserve">
<value>=== Custom Layouts ({0} total) ===</value>
</data>
<data name="get_layouts_canvas_note" xml:space="preserve">
<value>Note: Canvas layout preview is approximate.</value>
</data>
<data name="get_layouts_canvas_detail" xml:space="preserve">
<value>Open FancyZones Editor for precise zone boundaries.</value>
</data>
<data name="get_layouts_usage" xml:space="preserve">
<value>Use 'FancyZonesCLI.exe set-layout &lt;UUID&gt;' to apply a layout.</value>
</data>
<!-- GetMonitorsCommand -->
<data name="cmd_get_monitors" xml:space="preserve">
<value>List monitors and FancyZones metadata</value>
</data>
<data name="get_monitors_error" xml:space="preserve">
<value>Failed to read monitor information. {0}
Note: Ensure FancyZones is running to get current monitor information.</value>
</data>
<data name="get_monitors_no_monitors" xml:space="preserve">
<value>No monitors found.</value>
</data>
<data name="get_monitors_header" xml:space="preserve">
<value>=== Monitors ({0} total) ===</value>
</data>
<!-- SetLayoutCommand -->
<data name="cmd_set_layout" xml:space="preserve">
<value>Set layout by UUID or template name</value>
</data>
<data name="set_layout_arg_layout" xml:space="preserve">
<value>Layout UUID or template type (e.g. focus, columns)</value>
</data>
<data name="set_layout_opt_monitor" xml:space="preserve">
<value>Apply to monitor N (1-based)</value>
</data>
<data name="set_layout_opt_all" xml:space="preserve">
<value>Apply to all monitors</value>
</data>
<data name="set_layout_error_monitor_index" xml:space="preserve">
<value>Monitor index must be &gt;= 1.</value>
</data>
<data name="set_layout_error_both_options" xml:space="preserve">
<value>Cannot specify both --monitor and --all.</value>
</data>
<data name="set_layout_error_not_found" xml:space="preserve">
<value>Layout '{0}' not found
Tip: For templates, use the type name (e.g., 'focus', 'columns', 'rows', 'grid', 'priority-grid')
For custom layouts, use the UUID from 'get-layouts'</value>
</data>
<data name="set_layout_error_monitor_not_found" xml:space="preserve">
<value>Monitor {0} not found. Available monitors: 1-{1}</value>
</data>
<data name="set_layout_error_no_monitors" xml:space="preserve">
<value>Internal error - no monitors to update.</value>
</data>
<data name="set_layout_error_unsupported_type" xml:space="preserve">
<value>Unsupported custom layout type '{0}'.</value>
</data>
<data name="set_layout_error_no_layout" xml:space="preserve">
<value>Internal error - no layout selected.</value>
</data>
<data name="set_layout_success_all" xml:space="preserve">
<value>Layout '{0}' applied to all monitors.</value>
</data>
<data name="set_layout_success_monitor" xml:space="preserve">
<value>Layout '{0}' applied to monitor {1}.</value>
</data>
<data name="set_layout_success_default" xml:space="preserve">
<value>Layout '{0}' applied to monitor 1.</value>
</data>
<!-- OpenEditorCommand -->
<data name="cmd_open_editor" xml:space="preserve">
<value>Launch FancyZones layout editor</value>
</data>
<data name="open_editor_error" xml:space="preserve">
<value>Failed to request FancyZones Editor launch. {0}</value>
</data>
<!-- OpenSettingsCommand -->
<data name="cmd_open_settings" xml:space="preserve">
<value>Open FancyZones settings page</value>
</data>
<data name="open_settings_error_not_started" xml:space="preserve">
<value>PowerToys.exe failed to start.</value>
</data>
<data name="open_settings_error" xml:space="preserve">
<value>Failed to open FancyZones Settings. {0}</value>
</data>
<!-- SetHotkeyCommand -->
<data name="cmd_set_hotkey" xml:space="preserve">
<value>Assign hotkey (0-9) to a custom layout</value>
</data>
<data name="set_hotkey_arg_key" xml:space="preserve">
<value>Hotkey index (0-9)</value>
</data>
<data name="set_hotkey_arg_layout" xml:space="preserve">
<value>Custom layout UUID</value>
</data>
<data name="set_hotkey_error_invalid_key" xml:space="preserve">
<value>Key must be between 0 and 9.</value>
</data>
<data name="set_hotkey_error_not_custom" xml:space="preserve">
<value>Layout '{0}' is not a custom layout UUID.</value>
</data>
<!-- RemoveHotkeyCommand -->
<data name="cmd_remove_hotkey" xml:space="preserve">
<value>Remove hotkey assignment</value>
</data>
<data name="remove_hotkey_arg_key" xml:space="preserve">
<value>Hotkey index (0-9)</value>
</data>
<data name="remove_hotkey_no_hotkeys" xml:space="preserve">
<value>No hotkeys configured.</value>
</data>
<data name="remove_hotkey_not_found" xml:space="preserve">
<value>No hotkey assigned to key {0}</value>
</data>
<!-- GetHotkeysCommand -->
<data name="cmd_get_hotkeys" xml:space="preserve">
<value>List all layout hotkeys</value>
</data>
<data name="get_hotkeys_no_hotkeys" xml:space="preserve">
<value>No hotkeys configured.</value>
</data>
<data name="get_hotkeys_header" xml:space="preserve">
<value>=== Layout Hotkeys ===</value>
</data>
<data name="get_hotkeys_instruction" xml:space="preserve">
<value>Press Win + Ctrl + Alt + &lt;number&gt; to switch layouts:</value>
</data>
<!-- EditorParametersRefresh -->
<data name="editor_params_timeout" xml:space="preserve">
<value>Could not get current monitor information (timed out after {0}ms waiting for '{1}').</value>
</data>
</root>

View File

@@ -0,0 +1,36 @@
// 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.Diagnostics.CodeAnalysis;
using System.Diagnostics.Tracing;
using Microsoft.PowerToys.Telemetry;
using Microsoft.PowerToys.Telemetry.Events;
namespace FancyZonesCLI.Telemetry
{
/// <summary>
/// Telemetry event for FancyZones CLI command execution.
/// </summary>
[EventData]
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
public class FancyZonesCLICommandEvent : EventBase, IEvent
{
public FancyZonesCLICommandEvent()
{
EventName = "FancyZones_CLICommand";
}
/// <summary>
/// Gets or sets the name of the CLI command that was executed.
/// </summary>
public string CommandName { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the command executed successfully.
/// </summary>
public bool Successful { get; set; }
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
}
}

View File

@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Globalization;
using System.IO;
using System.Text.Json;
using System.Threading;
@@ -60,7 +61,7 @@ internal static class EditorParametersRefresh
var finalParams = FancyZonesDataIO.ReadEditorParameters();
if (finalParams.Monitors == null || finalParams.Monitors.Count == 0)
{
throw new InvalidOperationException($"Could not get current monitor information (timed out after {maxWaitMilliseconds}ms waiting for '{Path.GetFileName(filePath)}').");
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Properties.Resources.editor_params_timeout, maxWaitMilliseconds, Path.GetFileName(filePath)));
}
return finalParams;