Skip to content

Instantly share code, notes, and snippets.

@MartinMiles
Last active June 15, 2025 08:41
Show Gist options
  • Save MartinMiles/d927b7a3556b9f645417cb8aa6720e7b to your computer and use it in GitHub Desktop.
Save MartinMiles/d927b7a3556b9f645417cb8aa6720e7b to your computer and use it in GitHub Desktop.
Processes all the rendering for a page and rebinds a top level component from one to another (say, from `page-layout` to `headless-main`) including those nested
# -----------------------------------------------------------
# Inline Script for Sitecore PowerShell ISE:
# Replace Multiple Placeholders for Renderings
# Target Item: Default is /sitecore/content/Zont/Habitat/Home/AAA
# DB: master
# - Processes Standard Values, Shared Layout, and Final Layout.
# - Replaces three placeholder pairs:
# $OldPlaceholder1 → $NewPlaceholder1
# $OldPlaceholder2 → $NewPlaceholder2
# $OldPlaceholder3 → $NewPlaceholder3
# - Logs every change to the console.
# -----------------------------------------------------------
param(
[string] $PageItemPath = "/sitecore/content/Zont/Habitat/Home/AAA",
[string] $OldPlaceholder1 = "page-layout",
[string] $NewPlaceholder1 = "headless-main",
[string] $OldPlaceholder2 = "header-top",
[string] $NewPlaceholder2 = "headless-header",
[string] $OldPlaceholder3 = "footer",
[string] $NewPlaceholder3 = "headless-footer"
)
# 1) Ensure master: PSDrive is mounted
if (-not (Get-PSDrive -Name master -ErrorAction SilentlyContinue)) {
New-PSDrive -Name master -PSProvider Sitecore -Root "/" -Database "master" -ErrorAction Stop | Out-Null
}
# 2) Get the master database and the page item
$db = [Sitecore.Configuration.Factory]::GetDatabase("master")
$item = $db.GetItem($PageItemPath)
if ($null -eq $item) {
Write-Error "Item not found at path: $PageItemPath"
return
}
# 3) Prepare an ArrayList for logging
$script:logEntries = New-Object System.Collections.ArrayList
# 4) Function to replace placeholders in a raw layout XML
function Replace-PlaceholdersInLayout {
param(
[string] $layoutXml,
[string] $contextItemID,
[string] $contextName,
[string] $languageName
)
$layoutDef = [Sitecore.Layouts.LayoutDefinition]::Parse($layoutXml)
$changed = $false
# Build mapping array from parameters
$mappings = @(
@{ Old = $OldPlaceholder1; New = $NewPlaceholder1 },
@{ Old = $OldPlaceholder2; New = $NewPlaceholder2 },
@{ Old = $OldPlaceholder3; New = $NewPlaceholder3 }
)
foreach ($devDef in $layoutDef.Devices) {
$deviceName = $devDef.Name
foreach ($rendDef in $devDef.Renderings) {
$oldPlaceholder = $rendDef.Placeholder
if ([string]::IsNullOrEmpty($oldPlaceholder)) { continue }
$newPlaceholder = $null
foreach ($mapping in $mappings) {
$old = $mapping.Old
$new = $mapping.New
if ($oldPlaceholder -eq $old) {
$newPlaceholder = $new
break
}
$prefix = "/$old/"
if ($oldPlaceholder.StartsWith($prefix)) {
$suffix = $oldPlaceholder.Substring($prefix.Length)
$newPlaceholder = "/$new/" + $suffix
break
}
}
if ($null -ne $newPlaceholder -and $newPlaceholder -ne $oldPlaceholder) {
$rendDef.Placeholder = $newPlaceholder
$changed = $true
$entry = [PSCustomObject]@{
ItemID = $contextItemID
Context = $contextName
Language = $languageName
Device = $deviceName
RenderingID = $rendDef.RenderingID
OldPlaceholder = $oldPlaceholder
NewPlaceholder = $newPlaceholder
}
$null = $script:logEntries.Add($entry)
}
}
}
if ($changed) {
return $layoutDef.ToXml()
} else {
return $null
}
}
# 5) Update Standard Values of the template
$templateItem = $db.GetItem($item.TemplateID.ToString())
if ($null -eq $templateItem) {
Write-Error "Unable to find template item for ID: $($item.TemplateID)"
return
}
$stdValuesItem = $templateItem.Children | Where-Object { $_.Name -eq "__Standard Values" }
if ($null -eq $stdValuesItem) {
Write-Error "No __Standard Values found under template: $($templateItem.Paths.FullPath)"
} else {
# Shared Layout (__Renderings)
$stdSharedField = $stdValuesItem.Fields["__Renderings"]
if ($stdSharedField -and -not [string]::IsNullOrWhiteSpace($stdSharedField.Value)) {
$newXml = Replace-PlaceholdersInLayout `
-layoutXml $stdSharedField.Value `
-contextItemID $stdValuesItem.ID.ToString() `
-contextName "Standard Values Shared Layout" `
-languageName ""
if ($newXml) {
$stdValuesItem.Editing.BeginEdit()
try {
$stdSharedField.Value = $newXml
Write-Host "✅ Standard Values (Shared Layout) updated on template: $($templateItem.Name)"
} finally {
$stdValuesItem.Editing.EndEdit()
}
} else {
Write-Host "ℹ️ No changes needed in Standard Values Shared Layout."
}
} else {
Write-Host "ℹ️ Standard Values Shared Layout is empty or missing. Skipping."
}
# Final Layout (__Final Renderings)
$stdFinalField = $stdValuesItem.Fields["__Final Renderings"]
if ($stdFinalField -and -not [string]::IsNullOrWhiteSpace($stdFinalField.Value)) {
$newXml = Replace-PlaceholdersInLayout `
-layoutXml $stdFinalField.Value `
-contextItemID $stdValuesItem.ID.ToString() `
-contextName "Standard Values Final Layout" `
-languageName ""
if ($newXml) {
$stdValuesItem.Editing.BeginEdit()
try {
$stdFinalField.Value = $newXml
Write-Host "✅ Standard Values (Final Layout) updated on template: $($templateItem.Name)"
} finally {
$stdValuesItem.Editing.EndEdit()
}
} else {
Write-Host "ℹ️ No changes needed in Standard Values Final Layout."
}
} else {
Write-Host "ℹ️ Standard Values Final Layout is empty or missing. Skipping."
}
}
# 6) Iterate through all languages of the page item
foreach ($lang in $item.Languages) {
$langName = $lang.Name
$versionedItem = $db.GetItem($PageItemPath, $lang)
if ($null -eq $versionedItem) {
Write-Host "⚠️ Item does not exist in language: $langName. Skipping."
continue
}
# Shared Layout (__Renderings)
$sharedField = $versionedItem.Fields["__Renderings"]
if ($sharedField -and -not [string]::IsNullOrWhiteSpace($sharedField.Value)) {
$newXml = Replace-PlaceholdersInLayout `
-layoutXml $sharedField.Value `
-contextItemID $versionedItem.ID.ToString() `
-contextName "Shared Layout" `
-languageName $langName
if ($newXml) {
$versionedItem.Editing.BeginEdit()
try {
$sharedField.Value = $newXml
Write-Host "✅ [$langName] Shared Layout updated on: $($versionedItem.Paths.FullPath)"
} finally {
$versionedItem.Editing.EndEdit()
}
} else {
Write-Host "ℹ️ [$langName] No changes needed in Shared Layout."
}
} else {
Write-Host "ℹ️ [$langName] Shared Layout is empty or missing. Skipping."
}
# Final Layout (__Final Renderings)
$finalField = $versionedItem.Fields["__Final Renderings"]
if ($finalField -and -not [string]::IsNullOrWhiteSpace($finalField.Value)) {
$newXml = Replace-PlaceholdersInLayout `
-layoutXml $finalField.Value `
-contextItemID $versionedItem.ID.ToString() `
-contextName "Final Layout" `
-languageName $langName
if ($newXml) {
$versionedItem.Editing.BeginEdit()
try {
$finalField.Value = $newXml
Write-Host "✅ [$langName] Final Layout updated on: $($versionedItem.Paths.FullPath)"
} finally {
$versionedItem.Editing.EndEdit()
}
} else {
Write-Host "ℹ️ [$langName] No changes needed in Final Layout."
}
} else {
Write-Host "ℹ️ [$langName] Final Layout is empty or missing. Skipping."
}
}
# 7) Output a brief table of every placeholder change
if ($logEntries.Count -gt 0) {
Write-Host "`n========== Placeholder Replacement Log ==========`n"
$logEntries |
Sort-Object Context, Language, Device |
Format-Table `
@{Label="Item ID"; Expression={$_.ItemID}}, `
@{Label="Context"; Expression={$_.Context}}, `
@{Label="Language"; Expression={$_.Language}}, `
@{Label="Device"; Expression={$_.Device}}, `
@{Label="Rendering ID"; Expression={$_.RenderingID}}, `
@{Label="Old Placeholder";Expression={$_.OldPlaceholder}}, `
@{Label="New Placeholder";Expression={$_.NewPlaceholder}} `
-AutoSize
Write-Host "`nTotal changes: $($logEntries.Count)`n"
} else {
Write-Host "`n✅ No placeholders matching the specified parameters were found. No changes made."
}
You are a world-renowned Sitecore PowerShell Extensions scripting expert. Write a single SPE script ready to paste directly into Sitecore PowerShell ISE (no saving to file) against the master database that does exactly the following:
1. **Parametrize everything at the top**:
- Accept these parameters (all strings):
- `$PageItemPath` (default: `"/sitecore/content/Zont/Habitat/Home/AAA"`)
- `$OldPlaceholder1` (default: `"page-layout"`)
- `$NewPlaceholder1` (default: `"headless-main"`)
- `$OldPlaceholder2` (default: `"header-top"`)
- `$NewPlaceholder2` (default: `"headless-header"`)
- `$OldPlaceholder3` (default: `"footer"`)
- `$NewPlaceholder3` (default: `"headless-footer"`)
2. **Mount the master drive if necessary**:
- If no `master:` PSDrive exists, create it with:
```
New-PSDrive -Name master -PSProvider Sitecore -Root "/" -Database "master" -ErrorAction Stop | Out-Null
```
3. **Get the master database and the target page item**:
- Use `[Sitecore.Configuration.Factory]::GetDatabase("master")` to get the master database.
- Load the item at `$PageItemPath`. If it doesn’t exist, write an error and stop.
4. **Prepare logging**:
- Create an `ArrayList` called `$logEntries` (as `$script:logEntries = New-Object System.Collections.ArrayList`) to store PSCustomObjects for every placeholder change. Do not use `+=` on a PSObject.
5. **Define a helper function `Replace-PlaceholdersInLayout`** that takes:
- `$layoutXml` (string)
- `$contextItemID` (string)
- `$contextName` (string)
- `$languageName` (string)
Inside the function:
- Parse `$layoutXml` using `[Sitecore.Layouts.LayoutDefinition]::Parse($layoutXml)` into a `$layoutDef`.
- Initialize `$changed = $false`.
- Build an array `$mappings = @(
@{ Old = $OldPlaceholder1; New = $NewPlaceholder1 },
@{ Old = $OldPlaceholder2; New = $NewPlaceholder2 },
@{ Old = $OldPlaceholder3; New = $NewPlaceholder3 }
)`.
- Loop through `$layoutDef.Devices` and then through `$devDef.Renderings`. For each rendering:
- Read `$oldPlaceholder = $rendDef.Placeholder`. If null or empty, `continue`.
- Initialize `$newPlaceholder = $null`.
- For each mapping in `$mappings`:
1. If `$oldPlaceholder -eq $mapping.Old`, set `$newPlaceholder = $mapping.New` and `break`.
2. Else if `$oldPlaceholder.StartsWith("/$($mapping.Old)/")`, set `$suffix = $oldPlaceholder.Substring(("/" + $mapping.Old + "/").Length)` and `$newPlaceholder = "/" + $mapping.New + "/" + $suffix`, then `break`.
- If `$newPlaceholder` is not null and not equal to `$oldPlaceholder`:
- Assign `$rendDef.Placeholder = $newPlaceholder`.
- Set `$changed = $true`.
- Create a PSCustomObject entry with properties:
- `ItemID = $contextItemID`
- `Context = $contextName`
- `Language = $languageName`
- `Device = $deviceName`
- `RenderingID = $rendDef.RenderingID`
- `OldPlaceholder = $oldPlaceholder`
- `NewPlaceholder = $newPlaceholder`
- Add it to `$script:logEntries` via `$null = $script:logEntries.Add($entry)`.
- After looping, if `$changed`, return `$layoutDef.ToXml()`, else return `$null`.
6. **Update the template’s __Standard Values item**:
- Get `$templateItem = $db.GetItem($item.TemplateID.ToString())`. If null, write an error and stop.
- Find its child named `__Standard Values`:
```
$stdValuesItem = $templateItem.Children | Where-Object { $_.Name -eq "__Standard Values" }
```
If null, write an error and stop.
- Process `$stdValuesItem.Fields["__Renderings"]` (Shared Layout) if it exists and is not empty:
- Call `Replace-PlaceholdersInLayout` with:
- `-layoutXml $stdSharedField.Value`
- `-contextItemID $stdValuesItem.ID.ToString()`
- `-contextName "Standard Values Shared Layout"`
- `-languageName ""`
- If the function returns non-null `$newXml`, do:
```
$stdValuesItem.Editing.BeginEdit()
try {
$stdSharedField.Value = $newXml
Write-Host "✅ Standard Values (Shared Layout) updated on template: $($templateItem.Name)"
} finally {
$stdValuesItem.Editing.EndEdit()
}
```
- Otherwise, `Write-Host "ℹ️ No changes needed in Standard Values Shared Layout."`
- Process `$stdValuesItem.Fields["__Final Renderings"]` (Final Layout) in the same manner, with `contextName "Standard Values Final Layout"`.
7. **Iterate through all languages of the page item**:
- For each `$lang` in `$item.Languages`:
- Set `$langName = $lang.Name`.
- Get `$versionedItem = $db.GetItem($PageItemPath, $lang)`. If null, `Write-Host "⚠️ Item does not exist in language: $langName. Skipping."` and `continue`.
- Process `$versionedItem.Fields["__Renderings"]` (Shared Layout):
- If present and non-empty, call `Replace-PlaceholdersInLayout` with:
- `-layoutXml $sharedField.Value`
- `-contextItemID $versionedItem.ID.ToString()`
- `-contextName "Shared Layout"`
- `-languageName $langName`
- If returns non-null, begin editing, set `Value = $newXml`, end editing, and `Write-Host "✅ [$langName] Shared Layout updated on: $($versionedItem.Paths.FullPath)"`. Else `Write-Host "ℹ️ [$langName] No changes needed in Shared Layout."`
- If missing or empty, `Write-Host "ℹ️ [$langName] Shared Layout is empty or missing. Skipping."`
- Process `$versionedItem.Fields["__Final Renderings"]` (Final Layout) identically with `contextName "Final Layout"`.
8. **After all language versions**, output a brief table of every placeholder change:
- If `$logEntries.Count -gt 0`:
```
Write-Host "`n========== Placeholder Replacement Log ==========`n"
$logEntries |
Sort-Object Context, Language, Device |
Format-Table `
@{Label="Item ID"; Expression={$_.ItemID}}, `
@{Label="Context"; Expression={$_.Context}}, `
@{Label="Language"; Expression={$_.Language}}, `
@{Label="Device"; Expression={$_.Device}}, `
@{Label="Rendering ID"; Expression={$_.RenderingID}}, `
@{Label="Old Placeholder";Expression={$_.OldPlaceholder}}, `
@{Label="New Placeholder";Expression={$_.NewPlaceholder}} `
-AutoSize
Write-Host "`nTotal changes: $($logEntries.Count)`n"
```
- Else, `Write-Host "`n✅ No placeholders matching the specified parameters were found. No changes made."`
9. **Everything must run inline in Sitecore PowerShell ISE**, so you can paste the entire block into ISE and click Run without saving to a separate file. Ensure that the three placeholder‐pair parameters (`OldPlaceholder1`, `NewPlaceholder1`, etc.) allow replacing:
- `"page-layout"` → `"headless-main"`
- `"header-top"` → `"headless-header"`
- `"footer"` → `"headless-footer"`
by default, but can be overridden when running inline.
Provide the complete script exactly as described, following Sitecore PowerShell best practices and using BeginEdit()/EndEdit() properly.
# -----------------------------------------------------------
# Inline Script for Sitecore PowerShell ISE:
# Replace Multiple Placeholders for Renderings
# Target Item: Default is /sitecore/content/Zont/Habitat/Home/AAA
# DB: master
# - Processes Standard Values, Shared Layout, and Final Layout.
# - Replaces three placeholder pairs:
# $OldPlaceholder1 → $NewPlaceholder1
# $OldPlaceholder2 → $NewPlaceholder2
# $OldPlaceholder3 → $NewPlaceholder3
# - Removes leading slash for any top-level placeholder (single segment).
# - Logs every change to the console.
# -----------------------------------------------------------
param(
[string] $PageItemPath = "/sitecore/content/Zont/Habitat/Home/target",
[string] $OldPlaceholder1 = "page-layout",
[string] $NewPlaceholder1 = "headless-main",
[string] $OldPlaceholder2 = "header-top",
[string] $NewPlaceholder2 = "headless-header",
[string] $OldPlaceholder3 = "footer",
[string] $NewPlaceholder3 = "headless-footer"
)
# 1) Ensure master: PSDrive is mounted
if (-not (Get-PSDrive -Name master -ErrorAction SilentlyContinue)) {
New-PSDrive -Name master -PSProvider Sitecore -Root "/" -Database "master" -ErrorAction Stop | Out-Null
}
# 2) Get the master database and the page item
$db = [Sitecore.Configuration.Factory]::GetDatabase("master")
$item = $db.GetItem($PageItemPath)
if ($null -eq $item) {
Write-Error "Item not found at path: $PageItemPath"
return
}
# 3) Prepare an ArrayList for logging
$script:logEntries = New-Object System.Collections.ArrayList
# 4) Function to replace placeholders in a raw layout XML
function Replace-PlaceholdersInLayout {
param(
[string] $layoutXml,
[string] $contextItemID,
[string] $contextName,
[string] $languageName
)
$layoutDef = [Sitecore.Layouts.LayoutDefinition]::Parse($layoutXml)
$changed = $false
# Build mapping array from parameters
$mappings = @(
@{ Old = $OldPlaceholder1; New = $NewPlaceholder1 },
@{ Old = $OldPlaceholder2; New = $NewPlaceholder2 },
@{ Old = $OldPlaceholder3; New = $NewPlaceholder3 }
)
foreach ($devDef in $layoutDef.Devices) {
$deviceName = $devDef.Name
foreach ($rendDef in $devDef.Renderings) {
$oldPlaceholder = $rendDef.Placeholder
if ([string]::IsNullOrEmpty($oldPlaceholder)) { continue }
$newPlaceholder = $null
# 4a) Explicit mappings
foreach ($mapping in $mappings) {
$old = $mapping.Old
$new = $mapping.New
# Top-level exact match (with or without leading slash)
if ($oldPlaceholder -eq $old -or $oldPlaceholder -eq "/$old") {
$newPlaceholder = $new
break
}
# Nested mapping: /old/suffix → /new/suffix
$prefix = "/$old/"
if ($oldPlaceholder.StartsWith($prefix)) {
$suffix = $oldPlaceholder.Substring($prefix.Length)
$newPlaceholder = "/$new/$suffix"
break
}
}
# 4b) Remove leading slash for other single-segment placeholders
if (-not $newPlaceholder) {
if ($oldPlaceholder -match '^/[^/]+$') {
$newPlaceholder = $oldPlaceholder.TrimStart('/')
}
}
# 4c) Apply change if needed
if ($newPlaceholder -and $newPlaceholder -ne $oldPlaceholder) {
$rendDef.Placeholder = $newPlaceholder
$changed = $true
$entry = [PSCustomObject]@{
ItemID = $contextItemID
Context = $contextName
Language = $languageName
Device = $deviceName
RenderingID = $rendDef.RenderingID
OldPlaceholder = $oldPlaceholder
NewPlaceholder = $newPlaceholder
}
$null = $script:logEntries.Add($entry)
}
}
}
if ($changed) { return $layoutDef.ToXml() }
return $null
}
# 5) Update Standard Values of the template
$templateItem = $db.GetItem($item.TemplateID.ToString())
if (-not $templateItem) {
Write-Error "Unable to find template item for ID: $($item.TemplateID)"
return
}
$stdValuesItem = $templateItem.Children | Where-Object { $_.Name -eq "__Standard Values" }
if (-not $stdValuesItem) {
Write-Error "No __Standard Values found under template: $($templateItem.Paths.FullPath)"
} else {
# Shared Layout
$stdSharedField = $stdValuesItem.Fields["__Renderings"]
if ($stdSharedField -and -not [string]::IsNullOrWhiteSpace($stdSharedField.Value)) {
$newXml = Replace-PlaceholdersInLayout `
-layoutXml $stdSharedField.Value `
-contextItemID $stdValuesItem.ID.ToString() `
-contextName "Standard Values Shared Layout" `
-languageName ""
if ($newXml) {
$stdValuesItem.Editing.BeginEdit()
try {
$stdSharedField.Value = $newXml
Write-Host "✅ Standard Values (Shared Layout) updated on template: $($templateItem.Name)"
} finally {
$stdValuesItem.Editing.EndEdit()
}
} else {
Write-Host "ℹ️ No changes needed in Standard Values Shared Layout."
}
}
# Final Layout
$stdFinalField = $stdValuesItem.Fields["__Final Renderings"]
if ($stdFinalField -and -not [string]::IsNullOrWhiteSpace($stdFinalField.Value)) {
$newXml = Replace-PlaceholdersInLayout `
-layoutXml $stdFinalField.Value `
-contextItemID $stdValuesItem.ID.ToString() `
-contextName "Standard Values Final Layout" `
-languageName ""
if ($newXml) {
$stdValuesItem.Editing.BeginEdit()
try {
$stdFinalField.Value = $newXml
Write-Host "✅ Standard Values (Final Layout) updated on template: $($templateItem.Name)"
} finally {
$stdValuesItem.Editing.EndEdit()
}
} else {
Write-Host "ℹ️ No changes needed in Standard Values Final Layout."
}
}
}
# 6) Iterate through all languages of the page item
foreach ($lang in $item.Languages) {
$langName = $lang.Name
$versionedItem = $db.GetItem($PageItemPath, $lang)
if (-not $versionedItem) {
Write-Host "⚠️ Item not in language: $langName. Skipping."
continue
}
# Shared Layout
$sharedField = $versionedItem.Fields["__Renderings"]
if ($sharedField -and -not [string]::IsNullOrWhiteSpace($sharedField.Value)) {
$newXml = Replace-PlaceholdersInLayout `
-layoutXml $sharedField.Value `
-contextItemID $versionedItem.ID.ToString() `
-contextName "Shared Layout" `
-languageName $langName
if ($newXml) {
$versionedItem.Editing.BeginEdit()
try {
$sharedField.Value = $newXml
Write-Host "✅ [$langName] Shared Layout updated on: $($versionedItem.Paths.FullPath)"
} finally {
$versionedItem.Editing.EndEdit()
}
} else {
Write-Host "ℹ️ [$langName] No changes in Shared Layout."
}
}
# Final Layout
$finalField = $versionedItem.Fields["__Final Renderings"]
if ($finalField -and -not [string]::IsNullOrWhiteSpace($finalField.Value)) {
$newXml = Replace-PlaceholdersInLayout `
-layoutXml $finalField.Value `
-contextItemID $versionedItem.ID.ToString() `
-contextName "Final Layout" `
-languageName $langName
if ($newXml) {
$versionedItem.Editing.BeginEdit()
try {
$finalField.Value = $newXml
Write-Host "✅ [$langName] Final Layout updated on: $($versionedItem.Paths.FullPath)"
} finally {
$versionedItem.Editing.EndEdit()
}
} else {
Write-Host "ℹ️ [$langName] No changes in Final Layout."
}
}
}
# 7) Output log
if ($script:logEntries.Count -gt 0) {
Write-Host "`n========== Placeholder Replacement Log ==========`n"
$script:logEntries |
Sort-Object Context, Language, Device |
Format-Table `
@{Label="Item ID"; Expression={$_.ItemID}},`
@{Label="Context"; Expression={$_.Context}},`
@{Label="Language"; Expression={$_.Language}},`
@{Label="Device"; Expression={$_.Device}},`
@{Label="Rendering ID"; Expression={$_.RenderingID}},`
@{Label="Old Placeholder";Expression={$_.OldPlaceholder}},`
@{Label="New Placeholder";Expression={$_.NewPlaceholder}}`
-AutoSize
Write-Host "`nTotal changes: $($script:logEntries.Count)`n"
} else {
Write-Host "`n✅ No matching placeholders found. No changes made.`n"
}
@MartinMiles
Copy link
Author

MartinMiles commented Jun 3, 2025

Successful run:
image

Nothing to change:
image

@MartinMiles
Copy link
Author

MartinMiles commented Jun 5, 2025

Updated script outputs:
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment