PSWinTerminal.psm1


if ($env:WT_SESSION -and ($IsWindows -or ($PSVersionTable.PSVersion.Major -le 5))) {
    #region init
    Write-Verbose "Identify WT version"
    if ($IsWindows) {
        Write-Verbose "PowerShell 6+"
        $Script:PSWinTerminalProcessPathFolder = (Get-Process -id $PID).Parent.Path | Split-Path
    }
    else {
        Write-Verbose "Windows PowerShell"
        $Script:PSWinTerminalProcessPathFolder = (Get-Process -id $((Get-CimInstance -Query "SELECT * FROM Win32_Process WHERE ProcessID = $pid").ParentProcessID)).Path | split-path
    }
    Write-Verbose "Setting path to json files."
    if ( $Script:PSWinTerminalProcessPathFolder.contains('WindowsTerminalPreview') ) {
        $Script:PSWinTerminalConfigPath = "$env:LocalAppData\Packages\Microsoft.WindowsTerminalPreview_8wekyb3d8bbwe\LocalState\settings.json"
        $Script:PSWinTerminalDefaultsPath = "$Script:PSWinTerminalProcessPathFolder\defaults.json"
    }
    else {
        $Script:PSWinTerminalConfigPath = "$env:LocalAppData\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json"
        $Script:PSWinTerminalDefaultsPath = "$Script:PSWinTerminalProcessPathFolder\defaults.json"
    }

    if ( ( Test-Path -LiteralPath $Script:PSWinTerminalDefaultsPath) ) {
        $Script:PSWinTerminalDefaults = (Get-Content -LiteralPath $Script:PSWinTerminalDefaultsPath | Where-Object { -not $_.Trim().StartsWith('//') } ) | ConvertFrom-Json
        $Script:PSWinTerminalDefaultThemes = $Script:PSWinTerminalDefaults.schemes.name
    }

    if ( ( Test-Path -LiteralPath $Script:PSWinTerminalConfigPath) ) {
        $Script:PSWinTerminalOriginalConfig = Get-Content $Script:PSWinTerminalConfigPath
    }
    #endregion init

    function Initialize-PSWinTerminalConfig {
        [CmdletBinding()]
        param (
        )
        begin {
        }
        process {
            if ( ( Test-Path -LiteralPath $Script:PSWinTerminalConfigPath) ) {
                $Script:PSWinTerminalConfig = (Get-Content -LiteralPath $Script:PSWinTerminalConfigPath | Where-Object { -not $_.Trim().StartsWith('//') } ) | ConvertFrom-Json
                $Script:PSWinTerminalThemes = $Script:PSWinTerminalConfig.schemes.name
                $Script:PSWinTerminalCurentProfile = $Script:PSWinTerminalConfig.profiles.list.GetEnumerator() | Where-Object { $_.guid -eq $env:WT_PROFILE_ID }

                if ($Script:PSWinTerminalCurentProfile.colorScheme) {
                    $Script:PSWinTerminalCurentProfileHasColorScheme = $true
                }
                else {
                    $Script:PSWinTerminalCurentProfileHasColorScheme = $false
                }
            }
        }
        end {
        }
    }

    function Restore-WTConfig {
        <#
            .SYNOPSIS
                Restore-WTConfig will restore your Windows Terminal configuration.
            .DESCRIPTION
                Restore-WTConfig will restore your Windows Terminal configuration to a backup which was created when the module was loaded into your current session.
 
                The backup is stored in memory, and will be deleted when you close the current PowerShell session.
            .INPUTS
                None.
            .OUTPUTS
                None.
            .EXAMPLE
                Restore-WTConfig
 
                Restore-WTConfig will restore your Windows Terminal configuration
            .LINK
                https://github.com/sassdawe/PSWinTerminal
        #>

        [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High")]
        param (
        )

        begin {
        }

        process {
            If ($PSCmdlet.ShouldProcess("Windows Terminal settings", "Restore")) {
                try {
                    $Script:PSWinTerminalOriginalConfig | Set-Content -LiteralPath $Script:PSWinTerminalConfigPath
                    Initialize-PSWinTerminalConfig
                    Write-Verbose "Windows Terminal settings have been restored"
                }
                catch {
                    Write-Warning "Oh something went sideways: $_"
                }
            }
        }

        end {
        }
    }

    function Get-WTTheme {
        <#
            .SYNOPSIS
                Get-WTTheme will get the current Windows Terminal theme
            .DESCRIPTION
                Get-WTTheme will get the current Windows Terminal theme, if these is no theme configured it'll return the default theme.
            .INPUTS
                These is no input for Get-WTTheme
            .OUTPUTS
                Name of the current theme
            .EXAMPLE
                Get-WTTheme
 
                Get-WTTheme will get the current Windows Terminal theme
            .LINK
                https://github.com/sassdawe/PSWinTerminal
        #>

        [CmdletBinding()]
        param (
        )
        begin {
        }

        process {
            if ($Script:PSWinTerminalCurentProfileHasColorScheme) {
                $Script:PSWinTerminalCurentProfile.colorScheme
            }
            else {
                Write-Output "Campbell"
            }
        }
        end {
        }
    }

    function Show-WTTheme {
        <#
            .SYNOPSIS
                Show-WTTheme will show all available Windows Terminal themes
            .DESCRIPTION
                Show-WTTheme will show all available Windows Terminal themes.
            .INPUTS
                These is no input for Show-WTTheme
            .OUTPUTS
                Array of available themes, the custom themes will have a (*) next to their names.
            .EXAMPLE
                Show-WTTheme
 
                Show-WTTheme will show all available Windows Terminal themes
            .LINK
                https://github.com/sassdawe/PSWinTerminal
        #>

        [CmdletBinding()]
        param (
        )

        begin {

        }

        process {
            $WTThemes = New-Object System.Collections.ArrayList
            $Script:PSWinTerminalDefaultThemes | ForEach-Object { $null = $WTThemes.Add("$_") }
            $Script:PSWinTerminalThemes | ForEach-Object { $null = $WTThemes.Add("$_ *") }
            $WTThemes.ToArray() | Sort-Object
        }

        end {

        }
    }
    function Initialize-WTThemeList {
        <#
            .SYNOPSIS
                Initialize-WTThemeList
            .DESCRIPTION
                Initialize-WTThemeList
            .INPUTS
                These is no input for Initialize-WTThemeList
            .OUTPUTS
                Array of available themes.
            .EXAMPLE
            .LINK
                https://github.com/sassdawe/PSWinTerminal
        #>

        [CmdletBinding()]
        param (
        )

        begin {

        }

        process {
            $WTThemes = New-Object System.Collections.ArrayList
            $Script:PSWinTerminalDefaultThemes | ForEach-Object { $null = $WTThemes.Add("$_") }
            $Script:PSWinTerminalThemes | ForEach-Object { $null = $WTThemes.Add("$_") }
            Write-Verbose "Count of themes: $($WTThemes.Count)"
            $WTThemes.ToArray() | Sort-Object
        }

        end {

        }
    }
    function Set-WTTheme {
        <#
            .SYNOPSIS
                Set-WTTheme will change current Windows Terminal theme
            .DESCRIPTION
                Set-WTTheme will change the current Windows Terminal theme.
            .INPUTS
                Name of the theme we want to use for the current Windows Terminal profile
            .OUTPUTS
                None.
            .EXAMPLE
                Set-WTTheme "Campbell Powershell"
 
                Set-WTTheme will set current Windows Terminal theme to 'Campbell Powershell'
            .LINK
                https://github.com/sassdawe/PSWinTerminal
        #>

        [CmdletBinding(SupportsShouldProcess = $true)]
        param (
            # Name of theme
            [Parameter(Mandatory = $true)]
            <#[ArgumentCompleter(
                {
                    param($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams)
 
                    if ($FakeBoundParams.Theme) {
                        ( Initialize-WTThemeList | where-object { $_ -like $FakeBoundParams.Theme } ) | foreach-object { "`'$_`'" }
                    } else {
                        Initialize-WTThemeList | foreach-object { "`'$_`'" }
                    }
                }
            )]#>

            [ValidateScript(
                {
                    $_ -in (Initialize-WTThemeList)
                }
            )]
            [System.String]
            $Theme
        )

        begin {
            Write-Verbose "Set-WTTheme - begin: $Theme"
        }

        process {
            If ($PSCmdlet.ShouldProcess("WT Theme to $Theme", "Set")) {
                if ( $Script:PSWinTerminalCurentProfileHasColorScheme -eq $false ) {
                    Write-Verbose "Set-WTTheme - PSWinTerminalCurentProfileHasColorScheme FALSE"
                    ( Get-Content -LiteralPath $Script:PSWinTerminalConfigPath | ForEach-Object { if ( $_.contains("`"guid`": `"$env:WT_PROFILE_ID`"") ) { "$_`n`t`t`t`t`"colorScheme`": `"$Theme`"," } else { $_ } } ) | Set-Content -LiteralPath $Script:PSWinTerminalConfigPath -PassThru:$false
                    Initialize-PSWinTerminalConfig
                    $Script:PSWinTerminalCurentProfileHasColorScheme = $true
                }
                else {
                    Write-Verbose "Set-WTTheme - PSWinTerminalCurentProfileHasColorScheme TRUE"
                    $currentProfileGuidLine = 0
                    $content = Get-Content -LiteralPath $Script:PSWinTerminalConfigPath
                    :guid Foreach ( $line in $content ) {
                        if ( $line.contains("`"guid`": `"$env:WT_PROFILE_ID`"") ) {
                            $currentProfileGuidLine += 1
                            break guid
                        }
                        else {
                            $currentProfileGuidLine += 1
                        }
                    }
                    Write-Verbose "Set-WTTheme - Guid is in line: $currentProfileGuidLine"
                    for ($i = $currentProfileGuidLine - 1; $i -gt 0; $i--) {
                        # "$i" + $content[$i]
                        if ( $content[$i].Trim() -eq '{' ) {
                            $currentProfileStartLine = $i + 1
                            break
                        }
                    }
                    Write-Verbose "Set-WTTheme - Start is in line: $currentProfileStartLine"
                    for ($i = $currentProfileStartLine - 1; $i -lt $content.Length; $i++ ) {
                        # "$i" + $content[$i]
                        if ( $content[$i].Trim() -eq '},' ) {
                            $currentProfileEndLine = $i + 1
                            break
                        }
                    }
                    Write-Verbose "Set-WTTheme - End is in line: $currentProfileEndLine"
                    $newConfig = for ($i = 0; $i -lt $content.Length; $i++ ) {
                        if ( ($i -ge $currentProfileStartLine - 1) -and ($i -lt $currentProfileEndLine) ) {
                            if ( $content[$i].Contains("colorScheme") ) {
                                Write-Verbose "Old $( Get-WTTheme )"
                                $content[$i].Replace("`"colorScheme`": `"$( Get-WTTheme )`"", "`"colorScheme`": `"$Theme`"")
                                Write-Verbose "New $Theme"
                            }
                            else {
                                $content[$i]
                            }
                        }
                        else {
                            $content[$i]
                        }
                    }
                    $newConfig | Set-Content -LiteralPath $Script:PSWinTerminalConfigPath -PassThru:$false


                    Initialize-PSWinTerminalConfig
                }
            }
        }
        end {
            Write-Verbose "Set-WTTheme - end"
        }
    }

    Register-ArgumentCompleter -CommandName 'Set-WTTheme' -ParameterName 'Theme' -ScriptBlock { param($commandName, $parameterName, $stringMatch) Initialize-WTThemeList | Where-Object { $_ -like "$stringMatch*" } | Where-Object { -not [System.String]::IsNullOrEmpty($_) } | ForEach-Object { "`'$_`'" } }

    function Import-WTTheme {
        <#
            .SYNOPSIS
                Import-WTTheme
            .DESCRIPTION
                Import-WTTheme will import a valid scheme in JSON format from your clipboard
            .INPUTS
                [System.String] The content of your clipboard
            .OUTPUTS
                [System.String] The name of the imported theme
            .NOTES
                You need to copy the scheme you want to import to your clipboard
            .LINK
                Sites to look for themes for Windows Terminal:
                https://terminalsplash.com/
                https://atomcorp.github.io/themes/
        #>

        [CmdletBinding(SupportsShouldProcess = $true)]
        param (

        )

        begin {
            Write-Warning "Note: if you accidently break your Terminal's config, you can restore it with 'Restore-WTConfig'"
            Write-Verbose "Note: if you accidently break your Terminal's config, you can restore it with 'Restore-WTConfig'"
        }

        process {
            $clipboard = Get-Clipboard
            Write-Verbose "$clipboard"
            try {
                $clipboardJSON = ConvertFrom-Json -InputObject "$clipboard"
                if ( $clipboardJSON.name -in (Initialize-WTThemeList -Verbose:$false) ) { Throw "Theme with name `'$($clipboardJSON.name)`' already exists, cannot import this theme!" }
                if ( $clipboardJSON.name -and (($clipboardJSON | Get-Member -MemberType NoteProperty).count -ge 19 )) {
                    $currentSchemesStartLine = 0
                    $hasCustomTheme = $false
                    $content = Get-Content -LiteralPath $Script:PSWinTerminalConfigPath
                    :schemes Foreach ( $line in $content ) {
                        if ( $line.contains("`"schemes`": [") ) {
                            Write-Verbose "Line: $line"
                            if ( -not $line.EndsWith('],') ) {
                                $hasCustomTheme = $true
                            }
                            $currentSchemesStartLine += 1
                            break schemes
                        }
                        else {
                            $currentSchemesStartLine += 1
                        }
                    }
                    Write-Verbose "Schema start line: $currentSchemesStartLine"
                    if ( $false -eq $hasCustomTheme ) {
                        $newConfig = for ($i = 0; $i -lt $content.Length; $i++ ) {
                            if ( ($i -eq ($currentSchemesStartLine -1) ) ) {
                                "`"schemes`": ["
                                "$($clipboardJSON | ConvertTo-Json | Out-String)"
                                "],"
                            }
                            else {
                                $content[$i]
                            }
                        }
                    }
                    else {
                        $newConfig = for ($i = 0; $i -lt $content.Length; $i++ ) {
                            if ( ($i -eq $currentSchemesStartLine ) ) {
                                "$($clipboardJSON | ConvertTo-Json | Out-String),"
                                $content[$i]
                            }
                            else {
                                $content[$i]
                            }
                        }
                    }
                    $newConfig | Set-Content -LiteralPath $Script:PSWinTerminalConfigPath -PassThru:$false
                    Initialize-PSWinTerminalConfig
                    $clipboardJSON.name
                }
                else {
                    Throw "The validation of the Theme `'$($clipboardJSON.name)`' failed, please check again"
                }
            }
            catch {
                Throw $_
            }
        }

        end {

        }
    }

    Initialize-PSWinTerminalConfig

    Export-ModuleMember -Function * -Variable 'PSWinTerminalCurentProfileHasColorScheme'
}
else {
    Throw "You need to use Windows Terminal to use PSWinTerminal, and Windows"
}