mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-06 03:07:04 +02:00
Setting search (#41285)
<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? --> ## Summary of the Pull Request <!-- Please review the items on the PR checklist before submitting--> ## PR Checklist - [ ] Closes: #xxx - [ ] **Communication:** I've discussed this with core contributors already. If the work hasn't been agreed, this work might be rejected - [x] **Tests:** Added/updated and all pass - [ ] **Localization:** All end-user-facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx <!-- Provide a more detailed description of the PR, other things fixed, or any additional comments/features here --> ## Detailed Description of the Pull Request / Additional comments <!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well --> ## Validation Steps Performed Localized search: <img width="1576" height="480" alt="image" src="https://github.com/user-attachments/assets/dd6e5e9f-419b-40b1-b796-f0799481ecfc" /> ## AI summary This pull request introduces infrastructure and code to support search functionality for PowerToys settings, including a new search index specification, a dedicated search library, and updates to the solution configuration. The main changes are the addition of a spec describing how settings should be indexed and navigated, the creation of a new `Common.Search` project with a fuzz search implementation, and updates to the solution file to include these new components. **Settings Search Feature Implementation** * Documentation: * Added a detailed specification (`settings-search.md`) describing the structure of PowerToys settings pages, how to index settings, navigation logic, runtime search, result grouping, build-time indexing strategy, and corner cases. * New Search Library: * Added the new `Common.Search` project to the solution, including its project file and implementation of a fuzz search service (`FuzzSearchService<T>`), match options, match results, and search precision scoring. [[1]](diffhunk://#diff-ddc06fa41e4e723e54181b0cb85cdd00f57f75725d51ceefa242d4d651a9a363R1-R8) [[2]](diffhunk://#diff-1a2ca29fc33bcccf338a7843a040ca2c31ba821e8cab7064fab0dbb1224d454cR1-R39) [[3]](diffhunk://#diff-242764d948b795f39653a84d9b6bfcdc52730100deab2e3a0995be95bb8e7868R1-R10) [[4]](diffhunk://#diff-61e525491ed916ebd65dabb66dd4f5dc720320d7e295ef1e0bd6d506ea0f7df6R1-R67) [[5]](diffhunk://#diff-a775f6de2e8d42982829b4161668f49dedbbd9dcbb05ce20003de7e62275c57aR1-R12) * Solution Configuration: * Updated `PowerToys.sln` to include `Common.Search` and `Settings.UI.XamlIndexBuilder` projects, and configured their build settings for various platforms and mapped project dependencies. [[1]](diffhunk://#diff-ca837ce490070b91656ffffe31cbad8865ba9174e0f020231f77baf35ff3f811R714-R716) [[2]](diffhunk://#diff-ca837ce490070b91656ffffe31cbad8865ba9174e0f020231f77baf35ff3f811R2704-R2727) [[3]](diffhunk://#diff-ca837ce490070b91656ffffe31cbad8865ba9174e0f020231f77baf35ff3f811R2889) [[4]](diffhunk://#diff-ca837ce490070b91656ffffe31cbad8865ba9174e0f020231f77baf35ff3f811R3157-R3158) **Spell-check Dictionary Updates** * Added new terms related to navigation and settings UI components (such as `Navigatable`, `NavigatablePage`, `settingscard`, `Tru`, `tweakable`) to the spell-check dictionary to support the new search and indexing features. [[1]](diffhunk://#diff-5dcab162c1b233a49973ae010f2b88c7ec4844382abd705e6154685e62bd5c4dR1020-R1021) [[2]](diffhunk://#diff-5dcab162c1b233a49973ae010f2b88c7ec4844382abd705e6154685e62bd5c4dR1498) [[3]](diffhunk://#diff-5dcab162c1b233a49973ae010f2b88c7ec4844382abd705e6154685e62bd5c4dR1755-R1761) --------- Co-authored-by: Niels Laute <niels.laute@live.nl> Co-authored-by: Gordon Lam (SH) <yeelam@microsoft.com> Co-authored-by: Gordon Lam <73506701+yeelam-gordon@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
// 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.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Microsoft.PowerToys.Tools.XamlIndexBuilder
|
||||
{
|
||||
public static class ModuleIconResolver
|
||||
{
|
||||
// Hardcoded page-level overrides for module -> icon path
|
||||
private static readonly System.Collections.Generic.Dictionary<string, string> FileNameOverrides = new System.Collections.Generic.Dictionary<string, string>(System.StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
// Example overrides; expand as needed
|
||||
{ "FancyZonesPage.xaml", "/Assets/Settings/Icons/FancyZones.png" },
|
||||
{ "FileLocksmithPage.xaml", "/Assets/Settings/Icons/FileLocksmith.png" },
|
||||
{ "CmdNotFoundPage.xaml", "/Assets/Settings/Icons/CommandNotFound.png" },
|
||||
{ "PowerLauncherPage.xaml", "/Assets/Settings/Icons/PowerToysRun.png" },
|
||||
};
|
||||
|
||||
// Contract:
|
||||
// - Input: absolute path to the module XAML file (e.g., FancyZonesPage.xaml)
|
||||
// - Output: app-relative icon path (e.g., "/Assets/Settings/Icons/FancyZones.png"), or null if not found
|
||||
// - Strategy: take the first SettingsCard under the page and read its HeaderIcon value
|
||||
public static string ResolveIconFromFirstSettingsCard(string xamlFilePath)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(xamlFilePath))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var doc = XDocument.Load(xamlFilePath);
|
||||
|
||||
// Prefer looking inside SettingsPageControl.ModuleContent to avoid picking cards in Resources/DataTemplates
|
||||
var pageControl = doc.Descendants().FirstOrDefault(e => e.Name.LocalName == "SettingsPageControl");
|
||||
|
||||
if (pageControl != null)
|
||||
{
|
||||
// Locate the property element <SettingsPageControl.ModuleContent>
|
||||
var moduleContent = pageControl
|
||||
.Elements()
|
||||
.FirstOrDefault(e => e.Name.LocalName.EndsWith(".ModuleContent", System.StringComparison.OrdinalIgnoreCase))
|
||||
?? pageControl
|
||||
.Descendants()
|
||||
.FirstOrDefault(e => e.Name.LocalName.EndsWith(".ModuleContent", System.StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (moduleContent != null)
|
||||
{
|
||||
// Find the first SettingsCard under ModuleContent and try to read its HeaderIcon
|
||||
var firstCardUnderModule = moduleContent
|
||||
.Descendants()
|
||||
.FirstOrDefault(e => e.Name.LocalName == "SettingsCard");
|
||||
|
||||
if (firstCardUnderModule != null)
|
||||
{
|
||||
var icon = Program.ExtractIconValue(firstCardUnderModule);
|
||||
if (!string.IsNullOrWhiteSpace(icon))
|
||||
{
|
||||
return icon;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to hardcoded overrides by file name
|
||||
var fileName = Path.GetFileName(xamlFilePath);
|
||||
if (!string.IsNullOrEmpty(fileName) && FileNameOverrides.TryGetValue(fileName, out var overrideIcon))
|
||||
{
|
||||
return overrideIcon;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Non-fatal: let caller decide fallback
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
314
src/settings-ui/Settings.UI.XamlIndexBuilder/Program.cs
Normal file
314
src/settings-ui/Settings.UI.XamlIndexBuilder/Program.cs
Normal file
@@ -0,0 +1,314 @@
|
||||
// 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.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Microsoft.PowerToys.Tools.XamlIndexBuilder
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
private static readonly HashSet<string> ExcludedXamlFiles = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"ShellPage.xaml",
|
||||
};
|
||||
|
||||
private static JsonSerializerOptions serializeOption = new()
|
||||
{
|
||||
WriteIndented = true,
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
};
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
if (args.Length < 2)
|
||||
{
|
||||
Debug.WriteLine("Usage: XamlIndexBuilder <xaml-directory> <output-json-file>");
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
string xamlDirectory = args[0];
|
||||
string outputFile = args[1];
|
||||
|
||||
if (!Directory.Exists(xamlDirectory))
|
||||
{
|
||||
Debug.WriteLine($"Error: Directory '{xamlDirectory}' does not exist.");
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var searchableElements = new List<SettingEntry>();
|
||||
var xamlFiles = Directory.GetFiles(xamlDirectory, "*.xaml", SearchOption.AllDirectories);
|
||||
|
||||
foreach (var xamlFile in xamlFiles)
|
||||
{
|
||||
var fileName = Path.GetFileName(xamlFile);
|
||||
if (ExcludedXamlFiles.Contains(fileName))
|
||||
{
|
||||
// Skip ShellPage.xaml as it contains many elements not relevant for search
|
||||
continue;
|
||||
}
|
||||
|
||||
Debug.WriteLine($"Processing: {fileName}");
|
||||
var elements = ExtractSearchableElements(xamlFile);
|
||||
searchableElements.AddRange(elements);
|
||||
}
|
||||
|
||||
searchableElements = searchableElements.OrderBy(e => e.PageTypeName).ThenBy(e => e.ElementName).ToList();
|
||||
|
||||
string json = JsonSerializer.Serialize(searchableElements, serializeOption);
|
||||
File.WriteAllText(outputFile, json);
|
||||
|
||||
Debug.WriteLine($"Successfully generated index with {searchableElements.Count} elements.");
|
||||
Debug.WriteLine($"Output written to: {outputFile}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Error: {ex.Message}");
|
||||
Environment.Exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
public static List<SettingEntry> ExtractSearchableElements(string xamlFile)
|
||||
{
|
||||
var elements = new List<SettingEntry>();
|
||||
string pageName = Path.GetFileNameWithoutExtension(xamlFile);
|
||||
|
||||
try
|
||||
{
|
||||
// Load XAML as XML
|
||||
var doc = XDocument.Load(xamlFile);
|
||||
|
||||
// Define namespaces
|
||||
XNamespace x = "http://schemas.microsoft.com/winfx/2006/xaml";
|
||||
XNamespace controls = "http://schemas.microsoft.com/winfx/2006/xaml/presentation";
|
||||
XNamespace labs = "using:CommunityToolkit.Labs.WinUI";
|
||||
XNamespace winui = "using:CommunityToolkit.WinUI.UI.Controls";
|
||||
|
||||
// Extract SettingsPageControl elements
|
||||
var settingsPageElements = doc.Descendants()
|
||||
.Where(e => e.Name.LocalName == "SettingsPageControl")
|
||||
.Where(e => e.Attribute(x + "Uid") != null);
|
||||
|
||||
// Extract SettingsCard elements
|
||||
var settingsElements = doc.Descendants()
|
||||
.Where(e => e.Name.LocalName == "SettingsCard")
|
||||
.Where(e => e.Attribute("Name") != null || e.Attribute(x + "Uid") != null);
|
||||
|
||||
// Extract SettingsExpander elements
|
||||
var settingsExpanderElements = doc.Descendants()
|
||||
.Where(e => e.Name.LocalName == "SettingsExpander")
|
||||
.Where(e => e.Attribute("Name") != null || e.Attribute(x + "Uid") != null);
|
||||
|
||||
// Process SettingsPageControl elements
|
||||
foreach (var element in settingsPageElements)
|
||||
{
|
||||
var elementUid = GetElementUid(element, x);
|
||||
|
||||
// Prefer the first SettingsCard.HeaderIcon as the module icon
|
||||
var moduleImageSource = ModuleIconResolver.ResolveIconFromFirstSettingsCard(xamlFile);
|
||||
|
||||
if (!string.IsNullOrEmpty(elementUid))
|
||||
{
|
||||
elements.Add(new SettingEntry
|
||||
{
|
||||
PageTypeName = pageName,
|
||||
Type = EntryType.SettingsPage,
|
||||
ParentElementName = string.Empty,
|
||||
ElementName = string.Empty,
|
||||
ElementUid = elementUid,
|
||||
Icon = moduleImageSource,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Process SettingsCard elements
|
||||
foreach (var element in settingsElements)
|
||||
{
|
||||
var elementName = GetElementName(element, x);
|
||||
var elementUid = GetElementUid(element, x);
|
||||
var headerIcon = ExtractIconValue(element);
|
||||
|
||||
if (!string.IsNullOrEmpty(elementName) || !string.IsNullOrEmpty(elementUid))
|
||||
{
|
||||
var parentElementName = GetParentElementName(element, x);
|
||||
|
||||
elements.Add(new SettingEntry
|
||||
{
|
||||
PageTypeName = pageName,
|
||||
Type = EntryType.SettingsCard,
|
||||
ParentElementName = parentElementName,
|
||||
ElementName = elementName,
|
||||
ElementUid = elementUid,
|
||||
Icon = headerIcon,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Process SettingsExpander elements
|
||||
foreach (var element in settingsExpanderElements)
|
||||
{
|
||||
var elementName = GetElementName(element, x);
|
||||
var elementUid = GetElementUid(element, x);
|
||||
var headerIcon = ExtractIconValue(element);
|
||||
|
||||
if (!string.IsNullOrEmpty(elementName) || !string.IsNullOrEmpty(elementUid))
|
||||
{
|
||||
var parentElementName = GetParentElementName(element, x);
|
||||
|
||||
elements.Add(new SettingEntry
|
||||
{
|
||||
PageTypeName = pageName,
|
||||
Type = EntryType.SettingsExpander,
|
||||
ParentElementName = parentElementName,
|
||||
ElementName = elementName,
|
||||
ElementUid = elementUid,
|
||||
Icon = headerIcon,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Error processing {xamlFile}: {ex.Message}");
|
||||
}
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
public static string GetElementName(XElement element, XNamespace x)
|
||||
{
|
||||
// Get Name attribute (we call it ElementName in our indexing system)
|
||||
var name = element.Attribute("Name")?.Value;
|
||||
return name;
|
||||
}
|
||||
|
||||
public static string GetElementUid(XElement element, XNamespace x)
|
||||
{
|
||||
// Try x:Uid
|
||||
var uid = element.Attribute(x + "Uid")?.Value;
|
||||
return uid;
|
||||
}
|
||||
|
||||
public static string GetParentElementName(XElement element, XNamespace x)
|
||||
{
|
||||
// Look for parent SettingsExpander
|
||||
var current = element.Parent;
|
||||
while (current != null)
|
||||
{
|
||||
// Check if we're inside a SettingsExpander.Items or just directly inside SettingsExpander
|
||||
if (current.Name.LocalName == "Items")
|
||||
{
|
||||
// Check if the parent of Items is SettingsExpander
|
||||
var expanderParent = current.Parent;
|
||||
if (expanderParent?.Name.LocalName == "SettingsExpander")
|
||||
{
|
||||
var expanderName = expanderParent.Attribute("Name")?.Value;
|
||||
if (!string.IsNullOrEmpty(expanderName))
|
||||
{
|
||||
return expanderName;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (current.Name.LocalName == "SettingsExpander")
|
||||
{
|
||||
// Direct child of SettingsExpander
|
||||
var expanderName = current.Attribute("Name")?.Value;
|
||||
if (!string.IsNullOrEmpty(expanderName))
|
||||
{
|
||||
return expanderName;
|
||||
}
|
||||
}
|
||||
|
||||
current = current.Parent;
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public static string ExtractIconValue(XElement element)
|
||||
{
|
||||
var headerIconAttribute = element.Attribute("HeaderIcon")?.Value;
|
||||
|
||||
if (string.IsNullOrEmpty(headerIconAttribute))
|
||||
{
|
||||
// Try nested property element: <SettingsCard.HeaderIcon> ... </SettingsCard.HeaderIcon>
|
||||
var headerIconProperty = element.Elements()
|
||||
.FirstOrDefault(e => e.Name.LocalName.EndsWith(".HeaderIcon", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (headerIconProperty != null)
|
||||
{
|
||||
// Prefer explicit icon elements within the HeaderIcon property
|
||||
var pathIcon = headerIconProperty.Descendants().FirstOrDefault(d => d.Name.LocalName == "PathIcon");
|
||||
if (pathIcon != null)
|
||||
{
|
||||
var dataAttr = pathIcon.Attribute("Data")?.Value;
|
||||
if (!string.IsNullOrWhiteSpace(dataAttr))
|
||||
{
|
||||
return dataAttr.Trim();
|
||||
}
|
||||
}
|
||||
|
||||
var fontIcon = headerIconProperty.Descendants().FirstOrDefault(d => d.Name.LocalName == "FontIcon");
|
||||
if (fontIcon != null)
|
||||
{
|
||||
var glyphAttr = fontIcon.Attribute("Glyph")?.Value;
|
||||
if (!string.IsNullOrWhiteSpace(glyphAttr))
|
||||
{
|
||||
return glyphAttr.Trim();
|
||||
}
|
||||
}
|
||||
|
||||
var bitmapIcon = headerIconProperty.Descendants().FirstOrDefault(d => d.Name.LocalName == "BitmapIcon");
|
||||
if (bitmapIcon != null)
|
||||
{
|
||||
var sourceAttr = bitmapIcon.Attribute("Source")?.Value;
|
||||
if (!string.IsNullOrWhiteSpace(sourceAttr))
|
||||
{
|
||||
return sourceAttr.Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Parse different icon markup extensions
|
||||
// Example: {ui:BitmapIcon Source=/Assets/Settings/Icons/AlwaysOnTop.png}
|
||||
if (headerIconAttribute.Contains("BitmapIcon") && headerIconAttribute.Contains("Source="))
|
||||
{
|
||||
var sourceStart = headerIconAttribute.IndexOf("Source=", StringComparison.OrdinalIgnoreCase) + "Source=".Length;
|
||||
var sourceEnd = headerIconAttribute.IndexOf('}', sourceStart);
|
||||
if (sourceEnd == -1)
|
||||
{
|
||||
sourceEnd = headerIconAttribute.Length;
|
||||
}
|
||||
|
||||
return headerIconAttribute.Substring(sourceStart, sourceEnd - sourceStart).Trim();
|
||||
}
|
||||
|
||||
// Example: {ui:FontIcon Glyph=}
|
||||
if (headerIconAttribute.Contains("FontIcon") && headerIconAttribute.Contains("Glyph="))
|
||||
{
|
||||
var glyphStart = headerIconAttribute.IndexOf("Glyph=", StringComparison.OrdinalIgnoreCase) + "Glyph=".Length;
|
||||
var glyphEnd = headerIconAttribute.IndexOf('}', glyphStart);
|
||||
if (glyphEnd == -1)
|
||||
{
|
||||
glyphEnd = headerIconAttribute.Length;
|
||||
}
|
||||
|
||||
return headerIconAttribute.Substring(glyphStart, glyphEnd - glyphStart).Trim();
|
||||
}
|
||||
|
||||
// If it doesn't match known patterns, return the original value
|
||||
return headerIconAttribute;
|
||||
}
|
||||
}
|
||||
}
|
||||
44
src/settings-ui/Settings.UI.XamlIndexBuilder/SettingEntry.cs
Normal file
44
src/settings-ui/Settings.UI.XamlIndexBuilder/SettingEntry.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.PowerToys.Tools.XamlIndexBuilder
|
||||
{
|
||||
public enum EntryType
|
||||
{
|
||||
SettingsPage,
|
||||
SettingsCard,
|
||||
SettingsExpander,
|
||||
}
|
||||
|
||||
public struct SettingEntry
|
||||
{
|
||||
public EntryType Type { get; set; }
|
||||
|
||||
public string Header { get; set; }
|
||||
|
||||
public string PageTypeName { get; set; }
|
||||
|
||||
public string ElementName { get; set; }
|
||||
|
||||
public string ElementUid { get; set; }
|
||||
|
||||
public string ParentElementName { get; set; }
|
||||
|
||||
public string Description { get; set; }
|
||||
|
||||
public string Icon { get; set; }
|
||||
|
||||
public SettingEntry(EntryType type, string header, string pageTypeName, string elementName, string elementUid, string parentElementName = null, string description = null, string icon = null)
|
||||
{
|
||||
Type = type;
|
||||
Header = header;
|
||||
PageTypeName = pageTypeName;
|
||||
ElementName = elementName;
|
||||
ElementUid = elementUid;
|
||||
ParentElementName = parentElementName;
|
||||
Description = description;
|
||||
Icon = icon;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<!-- Import common props to satisfy repo audit; override problematic bits below for this console tool. -->
|
||||
<Import Project="..\..\Common.Dotnet.CsWinRT.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>Microsoft.PowerToys.Tools.XamlIndexBuilder</RootNamespace>
|
||||
<AssemblyName>XamlIndexBuilder</AssemblyName>
|
||||
<!-- Platform-agnostic: framework-dependent DLL executed via dotnet -->
|
||||
<SelfContained>false</SelfContained>
|
||||
<UseAppHost>false</UseAppHost>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<RuntimeIdentifier></RuntimeIdentifier>
|
||||
<!-- Keep tool output out of product scan paths to avoid deps.json audit conflicts -->
|
||||
<OutputPath>$(MSBuildProjectDirectory)\obj\XamlIndexBuilder\$(Configuration)\</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Remove CsWinRT package introduced by common props; not needed for this tool and causes Windows metadata errors -->
|
||||
<ItemGroup>
|
||||
<PackageReference Remove="Microsoft.Windows.CsWinRT" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Text.Json" />
|
||||
<PackageReference Include="System.CommandLine" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Remove UI library reference to avoid pulling WindowsDesktop runtime (WindowsBase) -->
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- Fallback to dotnet if not provided by the environment -->
|
||||
<DotNetExe Condition="'$(DotNetExe)' == ''">dotnet</DotNetExe>
|
||||
<XamlViewsDir Condition="'$(XamlViewsDir)' == ''">$(MSBuildProjectDirectory)\..\Settings.UI\SettingsXAML\Views</XamlViewsDir>
|
||||
<GeneratedJsonFile Condition="'$(GeneratedJsonFile)' == ''">$(MSBuildProjectDirectory)\..\Settings.UI\Assets\Settings\search.index.json</GeneratedJsonFile>
|
||||
</PropertyGroup>
|
||||
<Target Name="GenerateSearchIndexSelf" AfterTargets="Build">
|
||||
<RemoveDir Directories="$(MSBuildProjectDirectory)\obj\ARM64;$(MSBuildProjectDirectory)\obj\x64;$(MSBuildProjectDirectory)\bin" />
|
||||
<MakeDir Directories="$([System.IO.Path]::GetDirectoryName('$(GeneratedJsonFile)'))" />
|
||||
<Message Importance="high" Text="[XamlIndexBuilder] Generating search index. Views='$(XamlViewsDir)'; Out='$(GeneratedJsonFile)'; Tool='$(TargetPath)'; DotNet='$(DotNetExe)'." />
|
||||
<!-- Execute via dotnet so host architecture doesn't need to match -->
|
||||
<Exec Command=""$(DotNetExe)" "$(TargetPath)" "$(XamlViewsDir)" "$(GeneratedJsonFile)"" />
|
||||
</Target>
|
||||
</Project>
|
||||
Reference in New Issue
Block a user