2026-02-20 15:04:45 -08:00
<#
Copyright ( c ) Microsoft Corporation .
Licensed under the MIT license .
. SYNOPSIS
2026-05-20 17:48:41 -07:00
Scans XAML files for local : SettingsCard and local : SettingsExpander entries and generates GeneratedSettingsIndex . g . h / . g . cpp .
2026-02-20 15:04:45 -08:00
. PARAMETER SourceDir
Directory to scan recursively for . xaml files .
. PARAMETER OutputDir
Directory to place generated C + + files .
#>
[ CmdletBinding ( ) ]
param (
2026-02-26 14:54:07 -08:00
[ Parameter ( Mandatory = $false ) ] [ string ] $SourceDir = " $PSScriptRoot \..\src\cascadia\TerminalSettingsEditor\ " ,
[ Parameter ( Mandatory = $false ) ] [ string ] $OutputDir = " $PSScriptRoot \..\src\cascadia\TerminalSettingsEditor\Generated Files\ "
2026-02-20 15:04:45 -08:00
)
# Prohibited UIDs (exact match, case-insensitive by default)
$ProhibitedUids = @ (
2026-02-26 14:54:07 -08:00
" Extensions_Scope " ,
" Profile_MissingFontFaces " ,
" Profile_ProportionalFontFaces " ,
" ColorScheme_InboxSchemeDuplicate " ,
" ColorScheme_ColorsHeader " ,
2026-05-20 17:48:41 -07:00
" ColorScheme_Rename " ,
" Profile_ResetProfile " ,
" Profile_DeleteProfile "
2026-02-20 15:04:45 -08:00
)
2026-02-26 14:54:07 -08:00
# Prohibited XAML files (already limited to Page root elements)
2026-02-20 15:04:45 -08:00
$ProhibitedXamlFiles = @ (
2026-02-26 14:54:07 -08:00
" AISettings.xaml " ,
" Profiles_Base_Orphaned.xaml " ,
" EditAction.xaml " ,
" MainPage.xaml "
2026-02-20 15:04:45 -08:00
)
2026-02-26 14:54:07 -08:00
# Grouped metadata for each page class
$ClassMap = @ {
" Microsoft::Terminal::Settings::Editor::Launch " = @ {
ResourceName = " Nav_Launch/Content "
NavigationParam = " Launch_Nav "
SubPage = " BreadcrumbSubPage::None "
}
" Microsoft::Terminal::Settings::Editor::Interaction " = @ {
ResourceName = " Nav_Interaction/Content "
NavigationParam = " Interaction_Nav "
SubPage = " BreadcrumbSubPage::None "
}
" Microsoft::Terminal::Settings::Editor::GlobalAppearance " = @ {
ResourceName = " Nav_Appearance/Content "
NavigationParam = " GlobalAppearance_Nav "
SubPage = " BreadcrumbSubPage::None "
}
" Microsoft::Terminal::Settings::Editor::ColorSchemes " = @ {
ResourceName = " Nav_ColorSchemes/Content "
NavigationParam = " ColorSchemes_Nav "
SubPage = " BreadcrumbSubPage::None "
}
" Microsoft::Terminal::Settings::Editor::Rendering " = @ {
ResourceName = " Nav_Rendering/Content "
NavigationParam = " Rendering_Nav "
SubPage = " BreadcrumbSubPage::None "
}
" Microsoft::Terminal::Settings::Editor::Compatibility " = @ {
ResourceName = " Nav_Compatibility/Content "
NavigationParam = " Compatibility_Nav "
SubPage = " BreadcrumbSubPage::None "
}
" Microsoft::Terminal::Settings::Editor::Actions " = @ {
ResourceName = " Nav_Actions/Content "
NavigationParam = " Actions_Nav "
SubPage = " BreadcrumbSubPage::None "
}
" Microsoft::Terminal::Settings::Editor::NewTabMenu " = @ {
ResourceName = " Nav_NewTabMenu/Content " # [Folders] Replaced with folder name
NavigationParam = " NewTabMenu_Nav " # [Folders] Replaced with folder VM
SubPage = " BreadcrumbSubPage::None " # [Folders] Replaced with BreadcrumbSubPage::NewTabMenu_Folder
}
" Microsoft::Terminal::Settings::Editor::Extensions " = @ {
ResourceName = " Nav_Extensions/Content " # [Extension] Replaced with extension name
NavigationParam = " Extensions_Nav " # [Extension] Replaced with extension VM
SubPage = " BreadcrumbSubPage::None " # [Extension] Replaced with BreadcrumbSubPage::Extensions_Extension
}
" Microsoft::Terminal::Settings::Editor::Profiles_Base " = @ {
ResourceName = " Nav_ProfileDefaults/Content "
NavigationParam = " GlobalProfile_Nav "
SubPage = " BreadcrumbSubPage::None "
}
" Microsoft::Terminal::Settings::Editor::Profiles_Appearance " = @ {
ResourceName = " Nav_ProfileDefaults/Content "
NavigationParam = " GlobalProfile_Nav "
SubPage = " BreadcrumbSubPage::Profile_Appearance "
}
" Microsoft::Terminal::Settings::Editor::Profiles_Terminal " = @ {
ResourceName = " Nav_ProfileDefaults/Content "
NavigationParam = " GlobalProfile_Nav "
SubPage = " BreadcrumbSubPage::Profile_Terminal "
}
" Microsoft::Terminal::Settings::Editor::Profiles_Advanced " = @ {
ResourceName = " Nav_ProfileDefaults/Content "
NavigationParam = " GlobalProfile_Nav "
SubPage = " BreadcrumbSubPage::Profile_Advanced "
}
2026-06-11 18:25:31 -07:00
" Microsoft::Terminal::Settings::Editor::Profiles " = @ {
ResourceName = " Nav_Profiles/Content "
NavigationParam = " Profiles_Nav "
2026-02-26 14:54:07 -08:00
SubPage = " BreadcrumbSubPage::None "
}
}
function IsProfileSubPage($pageClass )
{
return $pageClass -match " Editor::Profiles_Appearance " -or
$pageClass -match " Editor::Profiles_Terminal " -or
$pageClass -match " Editor::Profiles_Advanced "
}
2026-02-20 15:04:45 -08:00
if ( -not ( Test-Path $SourceDir ) ) { throw " SourceDir not found: $SourceDir " }
if ( -not ( Test-Path $OutputDir ) ) { New-Item -ItemType Directory -Path $OutputDir | Out-Null }
$entries = @ ( )
2026-02-26 14:54:07 -08:00
foreach ( $xamlFile in Get-ChildItem -Path $SourceDir -Filter * . xaml )
{
2026-02-20 15:04:45 -08:00
# Skip whole file if prohibited
2026-02-26 14:54:07 -08:00
$filename = $xamlFile . Name
2026-02-20 15:04:45 -08:00
if ( $ProhibitedXamlFiles -contains $filename )
{
2026-02-26 14:54:07 -08:00
continue
2026-02-20 15:04:45 -08:00
}
2026-02-26 14:54:07 -08:00
# Load XAML and namespace manager
[ xml ] $xml = Get-Content -LiteralPath $xamlFile . FullName
$xm = New-Object System . Xml . XmlNamespaceManager ( $xml . NameTable )
$xm . AddNamespace ( " local " , " using:Microsoft.Terminal.Settings.Editor " )
$xm . AddNamespace ( " x " , " http://schemas.microsoft.com/winfx/2006/xaml " )
2026-02-20 15:04:45 -08:00
2026-02-26 14:54:07 -08:00
if ( $xml . DocumentElement . LocalName -ne " Page " -and $filename -ne " Appearances.xaml " )
2026-02-20 15:04:45 -08:00
{
2026-02-26 14:54:07 -08:00
# Only allow xaml files for Page elements (or Appearances.xaml)
continue
2026-02-20 15:04:45 -08:00
}
2026-02-26 14:54:07 -08:00
# Extract Page x:Class
# Appearances.xaml: UserControl hosted in Profiles_Appearance.xaml
$pageClass = $filename -eq " Appearances.xaml " ?
" Microsoft::Terminal::Settings::Editor::Profiles_Appearance " :
$xml . DocumentElement . SelectSingleNode ( " @x:Class " , $xm ) . Value
2026-02-20 15:04:45 -08:00
2026-02-26 14:54:07 -08:00
# Convert XAML namespace dots to C++ scope operators
$pageClass = ( $pageClass -replace " \. " , " :: " )
if ( $ClassMap . ContainsKey ( ( $pageClass ) ) -and -not ( IsProfileSubPage $pageClass ) )
2026-02-20 15:04:45 -08:00
{
$entries + = [ pscustomobject ] @ {
2026-02-26 14:54:07 -08:00
ResourceName = $ClassMap [ $pageClass ] . ResourceName
ParentPage = $pageClass
NavigationParam = $ClassMap [ $pageClass ] . NavigationParam
SubPage = $ClassMap [ $pageClass ] . SubPage
2026-02-26 17:35:04 -08:00
ElementName = $null # No specific element to navigate to, for the page itself
2026-02-26 14:54:07 -08:00
File = $filename
2026-02-20 15:04:45 -08:00
}
}
2026-02-26 14:54:07 -08:00
elseif ( $pageClass -notmatch " Editor::EditColorScheme " -and -not ( IsProfileSubPage $pageClass ) )
2026-02-20 15:04:45 -08:00
{
2026-02-26 14:54:07 -08:00
# Special case: EditColorScheme is only valid if a color scheme is associated,
# so don't register it in ClassMap and don't skip over it here.
Write-Warning " No class map entry for page class $pageClass (file: $filename ). Skipping automatic index generation for this page. "
continue
2026-02-20 15:04:45 -08:00
}
2026-02-26 14:54:07 -08:00
# Manually register special entries
if ( $filename -eq " ColorSchemes.xaml " )
2026-02-20 15:04:45 -08:00
{
2026-02-26 14:54:07 -08:00
# "add new" button
2026-02-20 15:04:45 -08:00
$entries + = [ pscustomobject ] @ {
2026-02-26 14:54:07 -08:00
ResourceName = " ColorScheme_AddNewButton/Text "
ParentPage = $pageClass
NavigationParam = $ClassMap [ $pageClass ] . NavigationParam
SubPage = $ClassMap [ $pageClass ] . SubPage
ElementName = " AddNewButton "
File = $filename
2026-02-20 15:04:45 -08:00
}
}
2026-05-20 17:48:41 -07:00
# Iterate over all local:SettingsCard and local:SettingsExpander nodes
foreach ( $settingContainer in ( $xml . SelectNodes ( " //local:SettingsCard " , $xm ) + $xml . SelectNodes ( " //local:SettingsExpander " , $xm ) ) )
2026-02-20 15:04:45 -08:00
{
# Extract Uid
2026-02-26 14:54:07 -08:00
if ( $null -eq $settingContainer . Uid )
2026-02-20 15:04:45 -08:00
{
2026-05-20 17:48:41 -07:00
# SettingsCard/SettingsExpander without x:Uid are not indexable — skip silently
2026-02-20 15:04:45 -08:00
continue
}
2026-02-26 14:54:07 -08:00
elseif ( $ProhibitedUids -contains $settingContainer . Uid )
2026-02-20 15:04:45 -08:00
{
2026-02-26 14:54:07 -08:00
continue
2026-02-20 15:04:45 -08:00
}
2026-03-10 22:33:15 +05:30
# Extract Name via GetAttribute to avoid PowerShell's XML integration
2026-06-09 17:14:56 -07:00
# returning the element name (e.g. "local:SettingsCard") when x:Name is absent.
2026-03-10 22:33:15 +05:30
$name = $settingContainer . GetAttribute ( " x:Name " )
if ( [ string ] :: IsNullOrEmpty ( $name ) )
{
$name = " "
}
2026-02-26 14:54:07 -08:00
if ( $filename -eq " Appearances.xaml " )
2026-02-20 15:04:45 -08:00
{
2026-02-26 14:54:07 -08:00
# Profile.Appearance settings need a special prefix for the ElementName.
# This allows us to bring the element into view at runtime.
$name = " App. " + $name
2026-02-20 15:04:45 -08:00
}
2026-02-26 14:54:07 -08:00
# Deduce NavigationParam and SubPage
2026-02-20 15:04:45 -08:00
# includeInBuildIndex: include the entry in the build-time index (no special param at runtime)
# includeInPartialIndex: include the entry in the partial index, where the NavigationParam is the view model at runtime (i.e. profile vs profile defaults)
$includeInBuildIndex = $true
$includeInPartialIndex = $false
2026-02-26 14:54:07 -08:00
$navigationParam = $ClassMap [ $pageClass ] . NavigationParam
$subPage = $ClassMap [ $pageClass ] . SubPage ? ? " BreadcrumbSubPage::None "
if ( $pageClass -match " Editor::NewTabMenu " )
2026-02-20 15:04:45 -08:00
{
2026-02-26 14:54:07 -08:00
if ( $settingContainer . Uid -match " NewTabMenu_CurrentFolder " )
2026-02-20 15:04:45 -08:00
{
2026-02-26 14:54:07 -08:00
$navigationParam = $null # VM param at runtime
$subPage = " BreadcrumbSubPage::NewTabMenu_Folder "
$includeInBuildIndex = $false
$includeInPartialIndex = $true
2026-02-20 15:04:45 -08:00
}
else
{
$includeInPartialIndex = $true
}
}
2026-02-26 14:54:07 -08:00
elseif ( $pageClass -match " Editor::Profiles_Base " -or ( IsProfileSubPage $pageClass ) )
2026-02-20 15:04:45 -08:00
{
$includeInBuildIndex = ! ( $name -eq " Name " -or $name -eq " Commandline " )
$includeInPartialIndex = $true
}
2026-02-26 14:54:07 -08:00
elseif ( $pageClass -match " Editor::EditColorScheme " )
2026-02-20 15:04:45 -08:00
{
2026-02-26 14:54:07 -08:00
$subPage = " BreadcrumbSubPage::ColorSchemes_Edit "
$includeInBuildIndex = $false
$includeInPartialIndex = $true
2026-02-20 15:04:45 -08:00
}
if ( $includeInBuildIndex )
{
$entries + = [ pscustomobject ] @ {
2026-02-26 14:54:07 -08:00
ResourceName = " $( $settingContainer . Uid ) /Header "
ParentPage = $pageClass
NavigationParam = $navigationParam
SubPage = $subPage
ElementName = $name
File = $filename
2026-02-20 15:04:45 -08:00
}
}
if ( $includeInPartialIndex )
{
$entries + = [ pscustomobject ] @ {
2026-02-26 14:54:07 -08:00
ResourceName = " $( $settingContainer . Uid ) /Header "
ParentPage = $pageClass
NavigationParam = $null # VM param at runtime
SubPage = $pageClass -match " Editor::NewTabMenu " ? " BreadcrumbSubPage::NewTabMenu_Folder " : $subPage
ElementName = $name
File = $filename
2026-02-20 15:04:45 -08:00
}
}
}
}
2026-02-26 14:54:07 -08:00
function FormatEntry($e )
{
$formattedResourceName = 'USES_RESOURCE(L"{0}")' -f $e . ResourceName
$formattedNavigationParam = 'L"{0}"' -f $e . NavigationParam # null Navigation param resolves to empty string
$formattedElementName = 'L"{0}"' -f $e . ElementName
return " IndexEntry{{ {0}, {1}, {2}, {3} }}, // {4} " -f ( $formattedResourceName , $formattedNavigationParam , $e . SubPage , $formattedElementName , $e . File )
}
function FormatEntries($es ) {
return ( $es | ForEach-Object { FormatEntry $_ } ) -join " `r `n "
}
# Sort and remove duplicates
$entries = $entries | Sort-Object ResourceName , ParentPage , NavigationParam , SubPage , ElementName , File -Unique
2026-02-20 15:04:45 -08:00
$buildTimeEntries = @ ( )
$profileEntries = @ ( )
$schemeEntries = @ ( )
$ntmEntries = @ ( )
foreach ( $e in $entries )
{
2026-02-26 14:54:07 -08:00
if ( $null -eq $e . NavigationParam -and $e . ParentPage -match " Profiles_ " )
2026-02-20 15:04:45 -08:00
{
2026-02-26 14:54:07 -08:00
$profileEntries + = $e
2026-02-20 15:04:45 -08:00
}
2026-02-26 14:54:07 -08:00
elseif ( $e . SubPage -eq " BreadcrumbSubPage::ColorSchemes_Edit " )
2026-02-20 15:04:45 -08:00
{
2026-02-26 14:54:07 -08:00
$schemeEntries + = $e
2026-02-20 15:04:45 -08:00
}
2026-02-26 14:54:07 -08:00
elseif ( $e . SubPage -eq " BreadcrumbSubPage::NewTabMenu_Folder " )
2026-02-20 15:04:45 -08:00
{
2026-02-26 14:54:07 -08:00
$ntmEntries + = $e
2026-02-20 15:04:45 -08:00
}
else
{
2026-02-26 14:54:07 -08:00
$buildTimeEntries + = $e
2026-02-20 15:04:45 -08:00
}
}
2026-02-26 14:54:07 -08:00
$headerPath = Join-Path $OutputDir " GeneratedSettingsIndex.g.h "
$cppPath = Join-Path $OutputDir " GeneratedSettingsIndex.g.cpp "
2026-02-20 15:04:45 -08:00
$header = @"
/ * + +
Copyright ( c ) Microsoft Corporation
Licensed under the MIT license .
- - * /
2026-02-26 14:54:07 -08:00
/ / This file is automatically generated by tools \ GenerateSettingsIndex . ps1 . Changes to this file may be overwritten .
2026-02-20 15:04:45 -08:00
#pragma once
#include <winrt/Windows.UI.Xaml.Interop.h>
namespace winrt :: Microsoft :: Terminal :: Settings :: Editor :: implementation
{
struct IndexEntry
{
2026-02-26 14:54:07 -08:00
/ / Resource name of the SettingContainer ' s Header ( i . e . " Globals_DefaultProfile/Header " )
/ / NOTE : wrapped in USES_RESOURCE ( ) to take advantage of compile-time resource name validation in ResourceLoader
wil :: zwstring_view ResourceName ;
/ / Navigation argument
/ / - the tag used to identify the page to navigate to ( i . e . " Launch_Nav " )
/ / - empty if the NavigationArg is meant to be a view model object at runtime ( i . e . profile , ntm folder , etc . )
wil :: zwstring_view NavigationArgTag ;
2026-02-20 15:04:45 -08:00
2026-02-26 17:35:04 -08:00
/ / SubPage to navigate to , for pages with multiple subpages ( i . e . Profiles , New Tab Menu )
2026-02-20 15:04:45 -08:00
BreadcrumbSubPage SubPage ;
/ / x: Name of the SettingContainer to navigate to on the page ( i . e . " DefaultProfile " )
2026-02-26 14:54:07 -08:00
wil :: zwstring_view ElementName ;
2026-02-20 15:04:45 -08:00
} ;
const std :: array < IndexEntry , $ ( $buildTimeEntries . Count ) > & LoadBuildTimeIndex ( ) ;
const std :: array < IndexEntry , $ ( $profileEntries . Count ) > & LoadProfileIndex ( ) ;
const std :: array < IndexEntry , $ ( $ntmEntries . Count ) > & LoadNTMFolderIndex ( ) ;
const std :: array < IndexEntry , $ ( $schemeEntries . Count ) > & LoadColorSchemeIndex ( ) ;
const IndexEntry & PartialProfileIndexEntry ( ) ;
const IndexEntry & PartialNTMFolderIndexEntry ( ) ;
const IndexEntry & PartialColorSchemeIndexEntry ( ) ;
const IndexEntry & PartialExtensionIndexEntry ( ) ;
const IndexEntry & PartialActionIndexEntry ( ) ;
}
" @
$cpp = @"
/ / Copyright ( c ) Microsoft Corporation .
/ / Licensed under the MIT license .
2026-02-26 14:54:07 -08:00
/ / This file is automatically generated by tools \ GenerateSettingsIndex . ps1 . Changes to this file may be overwritten .
2026-02-20 15:04:45 -08:00
#include "pch.h"
#include <winrt/Microsoft.Terminal.Settings.Editor.h>
#include "GeneratedSettingsIndex.g.h"
#include <LibraryResources.h>
2026-02-26 14:54:07 -08:00
/ / In Debug builds , USES_RESOURCE ( ) expands to a lambda ( for resource validation ) ,
/ / which prevents constexpr evaluation . In Release it ' s a no-op identity macro .
#ifdef _DEBUG
#define STATIC_INDEX_QUALIFIER static const
#else
#define STATIC_INDEX_QUALIFIER static constexpr
#endif
2026-02-20 15:04:45 -08:00
namespace winrt :: Microsoft :: Terminal :: Settings :: Editor :: implementation
{
const std :: array < IndexEntry , $ ( $buildTimeEntries . Count ) > & LoadBuildTimeIndex ( )
{
2026-02-26 14:54:07 -08:00
STATIC_INDEX_QUALIFIER std :: array entries =
2026-02-20 15:04:45 -08:00
{
2026-02-26 14:54:07 -08:00
$ ( FormatEntries $buildTimeEntries )
2026-02-20 15:04:45 -08:00
} ;
return entries ;
}
const std :: array < IndexEntry , $ ( $profileEntries . Count ) > & LoadProfileIndex ( )
{
2026-02-26 14:54:07 -08:00
STATIC_INDEX_QUALIFIER std :: array entries =
2026-02-20 15:04:45 -08:00
{
2026-02-26 14:54:07 -08:00
$ ( FormatEntries $profileEntries )
2026-02-20 15:04:45 -08:00
} ;
return entries ;
}
const std :: array < IndexEntry , $ ( $ntmEntries . Count ) > & LoadNTMFolderIndex ( )
{
2026-02-26 14:54:07 -08:00
STATIC_INDEX_QUALIFIER std :: array entries =
2026-02-20 15:04:45 -08:00
{
2026-02-26 14:54:07 -08:00
$ ( FormatEntries $ntmEntries )
2026-02-20 15:04:45 -08:00
} ;
return entries ;
}
const std :: array < IndexEntry , $ ( $schemeEntries . Count ) > & LoadColorSchemeIndex ( )
{
2026-02-26 14:54:07 -08:00
STATIC_INDEX_QUALIFIER std :: array entries =
2026-02-20 15:04:45 -08:00
{
2026-02-26 14:54:07 -08:00
$ ( FormatEntries $schemeEntries )
2026-02-20 15:04:45 -08:00
} ;
return entries ;
}
const IndexEntry & PartialProfileIndexEntry ( )
{
2026-02-26 14:54:07 -08:00
static constexpr IndexEntry entry { . SubPage = BreadcrumbSubPage :: None } ;
2026-02-20 15:04:45 -08:00
return entry ;
}
const IndexEntry & PartialNTMFolderIndexEntry ( )
{
2026-02-26 14:54:07 -08:00
static constexpr IndexEntry entry { . SubPage = BreadcrumbSubPage :: NewTabMenu_Folder } ;
2026-02-20 15:04:45 -08:00
return entry ;
}
const IndexEntry & PartialColorSchemeIndexEntry ( )
{
2026-02-26 14:54:07 -08:00
static constexpr IndexEntry entry { . SubPage = BreadcrumbSubPage :: ColorSchemes_Edit } ;
2026-02-20 15:04:45 -08:00
return entry ;
}
const IndexEntry & PartialExtensionIndexEntry ( )
{
2026-02-26 14:54:07 -08:00
static constexpr IndexEntry entry { . SubPage = BreadcrumbSubPage :: Extensions_Extension } ;
2026-02-20 15:04:45 -08:00
return entry ;
}
const IndexEntry & PartialActionIndexEntry ( )
{
2026-02-26 14:54:07 -08:00
static constexpr IndexEntry entry { . SubPage = BreadcrumbSubPage :: Actions_Edit } ;
2026-02-20 15:04:45 -08:00
return entry ;
}
}
" @
Set-Content -LiteralPath $headerPath -Value $header -NoNewline
Set-Content -LiteralPath $cppPath -Value $cpp -NoNewline
Write-Host " Generated: "
Write-Host " $headerPath "
Write-Host " $cppPath "