categories/ExplorerStart.ps1

# Explorer/Start menu tweaks: show file extensions, disable Start menu "Recommended" section,
# Explorer startup location, hide OneDrive/3D Objects/Music from "This PC", hide "Include in
# library"/"Give access to"/"Share" context menu entries. All per-user (HKCU/HKCR-user-scope)
# values, so this category does not require elevation.
#
# ponytail: "add custom folders to This PC" and drive-letter ordering were both in scope per the
# original ask, but dropped here - the former needs a user-chosen folder path (doesn't fit the
# fixed-target toggle shape every other Action uses), the latter has no confirmed registry
# mechanism (specs/006-win11debloat-parity/research.md). Add either only with a real, verified
# mechanism in hand, not a guess.
#
# "Hide from This PC" and "hide context menu entry" actions delete a whole registry key rather
# than set a value - the TargetIdentifier's value-name half is the literal sentinel "(delete)"
# so Set-OctaExplorerStartAction knows to Remove-Item the key instead of Set-ItemProperty. The
# existing Registry snapshot mechanism (export-before-change) already handles this correctly
# with no changes needed: it exports the whole key before deleting it, and re-importing that
# export on undo recreates it.

function Get-OctaExplorerStartCategory {
    [CmdletBinding()]
    param()
    return [pscustomobject]@{
        Id                  = 'explorer-start'
        DisplayName         = 'Explorer/Start'
        Description         = 'Show file extensions, disable Start menu recommendations'
        RequiresElevation   = $false
        ContainsIrreversibleActions = $false
        GetActionsFunction  = 'Get-OctaExplorerStartActions'
        ApplyActionFunction = 'Set-OctaExplorerStartAction'
    }
}

function Get-OctaExplorerStartActions {
    [CmdletBinding()]
    param()
    $actions = @()
    $regPath = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced'

    $current = (Get-ItemProperty -Path $regPath -Name 'HideFileExt' -ErrorAction SilentlyContinue).HideFileExt
    if ($null -eq $current -or $current -ne 0) {
        $displayCurrent = if ($null -eq $current) { '(not set)' } else { $current }
        $actions += New-OctaAction -TargetType Registry -TargetIdentifier "$regPath|HideFileExt" `
            -CurrentValue $displayCurrent -PlannedValue 0 -Reversible $true
    }

    $recommended = (Get-ItemProperty -Path $regPath -Name 'Start_IrisRecommendations' -ErrorAction SilentlyContinue).Start_IrisRecommendations
    if ($null -eq $recommended -or $recommended -ne 0) {
        $displayCurrent = if ($null -eq $recommended) { '(not set)' } else { $recommended }
        $actions += New-OctaAction -TargetType Registry -TargetIdentifier "$regPath|Start_IrisRecommendations" `
            -CurrentValue $displayCurrent -PlannedValue 0 -Reversible $true
    }

    $launchTo = (Get-ItemProperty -Path $regPath -Name 'LaunchTo' -ErrorAction SilentlyContinue).LaunchTo
    if ($null -eq $launchTo -or $launchTo -ne 1) {
        $displayCurrent = if ($null -eq $launchTo) { '(not set)' } else { $launchTo }
        $actions += New-OctaAction -TargetType Registry -TargetIdentifier "$regPath|LaunchTo" `
            -CurrentValue $displayCurrent -PlannedValue 1 -Reversible $true
    }

    # "Hide from This PC" via each folder's FolderDescriptions ThisPCPolicy value (String, not
    # DWord - the actual type this specific value uses).
    $thisPcFolders = @(
        @{ Guid = '{0DB7E03F-FC29-4DC6-9020-FF41B59E513A}'; Name = '3D Objects' },
        @{ Guid = '{1CF1260C-4DD0-4ebb-811F-33C572699FDE}'; Name = 'Music' }
    )
    foreach ($folder in $thisPcFolders) {
        $fdPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\FolderDescriptions\$($folder.Guid)"
        $policy = (Get-ItemProperty -Path $fdPath -Name 'ThisPCPolicy' -ErrorAction SilentlyContinue).ThisPCPolicy
        if ($policy -ne 'Hide') {
            $displayCurrent = if ($null -eq $policy) { '(not set)' } else { $policy }
            $actions += New-OctaAction -TargetType Registry -TargetIdentifier "$fdPath|ThisPCPolicy" `
                -CurrentValue $displayCurrent -PlannedValue 'Hide' -Reversible $true
        }
    }

    # Whole-key-delete-to-hide actions - see file header comment for the "(delete)" sentinel.
    $hideKeys = @(
        @{ Path = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace\{018D5C66-4533-4307-9B53-224DE2ED1FE6}'; Label = 'OneDrive from Explorer navigation pane' },
        @{ Path = 'HKCR:\Folder\ShellEx\ContextMenuHandlers\Library Location'; Label = '"Include in library" context menu entry' },
        @{ Path = 'HKCR:\*\shellex\ContextMenuHandlers\Sharing'; Label = '"Give access to" context menu entry' },
        @{ Path = 'HKCR:\*\shellex\ContextMenuHandlers\ModernSharing'; Label = '"Share" context menu entry' }
    )
    foreach ($hk in $hideKeys) {
        # -LiteralPath: "*" in HKCR:\*\... is a literal key name (HKEY_CLASSES_ROOT's real
        # "all file types" key), not a wildcard - without -LiteralPath, Test-Path enumerates
        # every one of HKCR's tens of thousands of subkeys trying to match it (found by hanging
        # for minutes during this feature's own implementation).
        if (Test-Path -LiteralPath $hk.Path) {
            $actions += New-OctaAction -TargetType Registry -TargetIdentifier "$($hk.Path)|(delete)" `
                -CurrentValue 'present' -PlannedValue 'removed' -Reversible $true
        }
    }

    return $actions
}

function Set-OctaExplorerStartAction {
    [CmdletBinding()]
    param([Parameter(Mandatory)]$Action)
    $keyPath, $valueName = $Action.TargetIdentifier -split '\|', 2
    if ($valueName -eq '(delete)') {
        Remove-Item -LiteralPath $keyPath -Recurse -Force -ErrorAction SilentlyContinue
        return
    }
    if (-not (Test-Path -LiteralPath $keyPath)) {
        New-Item -Path $keyPath -Force | Out-Null
    }
    $type = if ($Action.PlannedValue -is [string]) { 'String' } else { 'DWord' }
    Set-ItemProperty -Path $keyPath -Name $valueName -Value $Action.PlannedValue -Type $type
}