[console]::InputEncoding = [console]::OutputEncoding = [Text.UTF8Encoding]::new($false) # -- Set Console Encoding -- # [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 $OutputEncoding = [System.Text.Encoding]::UTF8 try {[Console]::InputEncoding = [System.Text.Encoding]::UTF8} catch {} function PS.UI ([string]$Title, [int]$WindowWidth, [int]$WindowHeight, [int]$BufferHeight, [string]$BackgroundColor, [string]$ForegroundColor) { # -- Registry Keys Path -- # $keys = @( "HKCU:\Console", "HKCU:\Console\%SystemRoot%_System32_WindowsPowerShell_v1.0_powershell.exe", "HKCU:\Console\%SystemRoot%_SysWOW64_WindowsPowerShell_v1.0_powershell.exe" ) # -- Settings For All Keys -- # $settings = @{ # General Colors "ColorTable00" = 0x00000000 "ColorTable01" = 0x00da3700 "ColorTable02" = 0x000ea113 "ColorTable03" = 0x00dd963a "ColorTable04" = 0x001f0fc5 "ColorTable05" = 0x00562401 "ColorTable06" = 0x00f0edee "ColorTable07" = 0x00cccccc "ColorTable08" = 0x00767676 "ColorTable09" = 0x00ff783b "ColorTable10" = 0x000cc616 "ColorTable11" = 0x00d6d661 "ColorTable12" = 0x005648e7 "ColorTable13" = 0x009e00b4 "ColorTable14" = 0x00a5f1f9 "ColorTable15" = 0x00ffffff # Console Properties "FaceName" = "Consolas" "FontFamily" = 0x00000036 "FontWeight" = 0x00000190 "PopupColors" = 0x0000000f "ScreenBufferSize" = 0x03e80064 "ScreenColors" = 0x0000000f "WindowSize" = 0x001e0064 "FontSize" = 0x00100000 "CursorType" = 0x00000001 "InterceptCopyPaste" = 0x00000001 "TerminalScrolling" = 0x00000000 "QuickEdit" = 0x00000001 "HistoryNoDup" = 0x00000001 "WindowAlpha" = 0x000000e6 } # -- Apply Settings To All Keys -- # foreach ($key in $keys) { if (!(Test-Path $key)) {ni -Path $key -Force | Out-Null} foreach ($name in $settings.Keys) { $value = $settings[$name] sp -Path $key -Name $name -Value $value -Type $(if ($value -is [int] -or $value -is [long]) {'DWord'} else {'String'}) } } # -- Native Methods Definition -- # Add-Type -TypeDefinition @" using System; using System.Runtime.InteropServices; using System.Text; [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct CONSOLE_FONT_INFO_EX { public uint cbSize; public uint nFont; public COORD dwFontSize; public uint FontFamily; public uint FontWeight; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string FaceName; } [StructLayout(LayoutKind.Sequential)] public struct COORD { public short X; public short Y; public COORD(short x, short y) { X = x; Y = y; } } public static class NativeMethods { [DllImport("kernel32.dll", SetLastError=true)] public static extern IntPtr GetStdHandle(int nStdHandle); [DllImport("kernel32.dll", SetLastError=true)] public static extern bool SetCurrentConsoleFontEx(IntPtr consoleOutput, bool maximumWindow, ref CONSOLE_FONT_INFO_EX info); [DllImport("kernel32.dll", SetLastError=true)] public static extern bool GetCurrentConsoleFontEx(IntPtr consoleOutput, bool maximumWindow, ref CONSOLE_FONT_INFO_EX info); } "@ cls # -- Set Console Font -- # $handle = [NativeMethods]::GetStdHandle(-11) $font = New-Object CONSOLE_FONT_INFO_EX $font.cbSize = [System.Runtime.InteropServices.Marshal]::SizeOf($font) [NativeMethods]::GetCurrentConsoleFontEx($handle, $false, [ref]$font) | Out-Null $font.FaceName = "Consolas" $font.dwFontSize = New-Object COORD(0, 16) $font.FontFamily = 0x36 $font.FontWeight = 400 [NativeMethods]::SetCurrentConsoleFontEx($handle, $false, [ref]$font) | Out-Null # -- Set Console Title -- # if ([string]::IsNullOrEmpty($Title)) {$Title = "Powershell"} [console]::Title = $Title # -- Set Window Size -- # if ($WindowWidth -eq 0) {$WindowWidth = 100} if ($WindowHeight -eq 0) {$WindowHeight = 25} [console]::SetWindowSize($WindowWidth, $WindowHeight) # -- Set Buffer Size -- # if ($BufferHeight -eq 0) {$BufferHeight = 1000} [console]::SetBufferSize($WindowWidth, $BufferHeight) # -- Set Console Colors -- # if (![string]::IsNullOrEmpty($BackgroundColor)) {[console]::BackgroundColor = $BackgroundColor} if (![string]::IsNullOrEmpty($ForegroundColor)) {[console]::ForegroundColor = $ForegroundColor} cls } PS.UI $global:ESC = [char]27 $global:RESET = "$ESC[0m" $global:BOLD = "$ESC[1m" $global:RED = "$ESC[1;31m" $global:GREEN = "$ESC[1;32m" $global:YELLOW = "$ESC[1;33m" $global:CYAN = "$ESC[1;36m" $global:MAGENTA = "$ESC[1;35m" $global:BLUE = "$ESC[1;34m" $global:GRAY = "$ESC[0;90m" $global:WHITE = "$ESC[1;97m" $global:lastSpinnerEsc = $false function Cursor.Set([int]$x, [int]$y) {try {[Console]::SetCursorPosition($x, $y)}catch {}} function Cursor.Hide {try {[Console]::CursorVisible = $false}catch {}} function Cursor.Show {try {[Console]::CursorVisible = $true}catch {}} function UI.Box([string]$text, [string]$color = "Purple", [int]$width = 38) { Write-Host "" UI.BoxGradient $text 0 $width $color } function Show-Box([string]$t, [string]$c = "Purple", [int]$w = 38) {UI.Box $t $c $w} function _PSUIGetColor([double]$p, [string]$theme) { $ESC = [char]27 $t = if ($p -lt 0.5) {$p * 2.0}else {($p - 0.5) * 2.0} if ($theme -eq "Red") {if ($p -lt 0.5) {$c1 = 239, 68, 68; $c2 = 244, 63, 94}else {$c1 = 244, 63, 94; $c2 = 239, 68, 68}} elseif ($theme -eq "Green") {if ($p -lt 0.5) {$c1 = 34, 197, 94; $c2 = 16, 185, 129}else {$c1 = 16, 185, 129; $c2 = 34, 197, 94}} elseif ($theme -eq "Yellow" -or $theme -eq "Orange") {if ($p -lt 0.5) {$c1 = 249, 115, 22; $c2 = 245, 158, 11}else {$c1 = 245, 158, 11; $c2 = 249, 115, 22}} elseif ($theme -eq "Purple") {if ($p -lt 0.5) {$c1 = 168, 85, 247; $c2 = 236, 72, 153}else {$c1 = 236, 72, 153; $c2 = 168, 85, 247}} else {if ($p -lt 0.5) {$c1 = 34, 211, 238; $c2 = 37, 99, 235}else {$c1 = 37, 99, 235; $c2 = 34, 211, 238}} $r = [int]($c1[0] + $t * ($c2[0] - $c1[0])); $g = [int]($c1[1] + $t * ($c2[1] - $c1[1])); $b = [int]($c1[2] + $t * ($c2[2] - $c1[2])) return "$ESC[38;2;$r;$g;${b}m" } function UI.BoxGradient([string]$text, [int]$offset, [int]$width = 38, [string]$theme = "Cyan") { $t = $text.Trim(); $width = [math]::Max($width, $t.Length + 4) $total = $width + 2 + 1 + $width + 2 + 1 $sbTop = [System.Text.StringBuilder]::new(" "); $sbBot = [System.Text.StringBuilder]::new(" ") $ESC = [char]27 for ($k = 0; $k -lt ($width + 2); $k++) { $pTop = (($k + $offset) % $total) / $total $cTop = if ($k -eq 0) {"╭"}elseif ($k -eq $width + 1) {"╮"}else {"─"} $null = $sbTop.Append("$(_PSUIGetColor $pTop $theme)$cTop") $pBot = ((($total - 2 - $k) + $offset) % $total) / $total $cBot = if ($k -eq 0) {"╰"}elseif ($k -eq $width + 1) {"╯"}else {"─"} $null = $sbBot.Append("$(_PSUIGetColor $pBot $theme)$cBot") } $pLeft = ((($total - 1) + $offset) % $total) / $total; $midLeft = "$(_PSUIGetColor $pLeft $theme)│$ESC[0m" $pRight = (($width + 2 + $offset) % $total) / $total; $midRight = "$(_PSUIGetColor $pRight $theme)│$ESC[0m" $null = $sbTop.Append("$ESC[0m"); $null = $sbBot.Append("$ESC[0m") $padded = (" $t").PadRight($width) $frame = $sbTop.ToString() + "`n ${midLeft}$ESC[1m${padded}$ESC[0m${midRight}`n" + $sbBot.ToString() + "`n" [Console]::Write($frame) } function UI.Status([string]$msg, [string]$type = "info") { $ESC = [char]27 switch ($type) { {$_ -in "success", "ok"} {Write-Host " $ESC[92m[✓] ${msg}$ESC[0m"} "warn" {Write-Host " $ESC[96m[!] ${msg}$ESC[0m"} {$_ -in "error", "err"} {Write-Host " $ESC[91m[✗] ${msg}$ESC[0m"} "info" {Write-Host " $ESC[96m[+] ${msg}$ESC[0m"} {$_ -in "loading", "proc"} {Write-Host " $ESC[94m[*] ${msg}$ESC[0m"} "off" {Write-Host " $ESC[90m[-] ${msg}$ESC[0m"} default {Write-Host " $msg"} } } function Write-Status([string]$msg, [string]$type = "info") {UI.Status $msg $type} function status_ok($m) {UI.Status $m "ok"} function status_err($m) {UI.Status $m "err"} function status_warn($m) {UI.Status $m "warn"} function status_info($m) {UI.Status $m "info"} function status_proc($m) {UI.Status $m "proc"} function UI.Pause { Write-Host "Press any key to continue . . . " -NoNewline -ForegroundColor DarkGray $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown"); Write-Host "" } function UI.Confirm([string]$prompt, [string]$color = "", [int]$indent = 3) { $ESC = [char]27 $frames = "⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏" $i = 0; Cursor.Hide [Console]::Write("`r$ESC[2K $ESC[96m $ESC[0m $prompt [Y/N]: ") while ($true) { if ([Console]::KeyAvailable) { $key = [Console]::ReadKey($true) switch ($key.Key) { {$_ -in "Enter", "Y"} { [Console]::Write("`r$ESC[2K $ESC[92m[✓]$ESC[0m $prompt [Y/N]: $ESC[92mYes$ESC[0m`n") Cursor.Show; return $true } "Escape" {[Console]::Write("`r$ESC[2K`n"); Cursor.Show; return $null} {$_ -in "Backspace", "N"} { [Console]::Write("`r$ESC[2K $ESC[91m[✗]$ESC[0m $prompt [Y/N]: $ESC[91mNo$ESC[0m`n") Cursor.Show; return $false } } } else { $f = $frames[$i % $frames.Length] Cursor.Set 3 ([Console]::CursorTop) [Console]::Write("$ESC[96m${f}$ESC[0m") sleep -Milliseconds 80; $i++ } } } function Ask-Confirm {UI.Confirm @args} function UI.Input { param([string]$Prompt, [string]$Default = "", [scriptblock]$Validate = $null, [switch]$NoNewline, [switch]$NoSuccess) $ESC = [char]27 $frames = "⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏" $frameIdx = 0; $chars = [System.Collections.Generic.List[char]]::new(); $cur = 0 [Console]::Write("`r$ESC[2K $ESC[96m $ESC[0m $Prompt") $tCol = 6 + $Prompt.Length; Cursor.Set $tCol ([Console]::CursorTop) $inlineError = "" while ($true) { if ([Console]::KeyAvailable) { $key = [Console]::ReadKey($true); $needsRedraw = $false if ($inlineError -and $key.Key -notin @("Enter", "Escape")) { $inlineError = "" [Console]::Write("$ESC[s`n$ESC[2K$ESC[u"); $needsRedraw = $true } switch ($key.Key) { "Enter" { $value = [string]::new($chars.ToArray()) if ([string]::IsNullOrWhiteSpace($value)) {$value = $Default} $ok = $true if ($Validate) { $res = & $Validate $value if ($res -is [bool] -and $res -eq $false) {$ok = $false} elseif ($res -is [string]) {$ok = $false; $inlineError = $res} } if ($ok) { if ($inlineError) {[Console]::Write("$ESC[s`n$ESC[2K$ESC[u")} if (!$NoSuccess) { Cursor.Set 0 ([Console]::CursorTop) [Console]::Write("`r$ESC[2K $ESC[92m[✓]$ESC[0m $ESC[92m$($Prompt.TrimEnd())$ESC[0m $ESC[92m${value}$ESC[0m`n") } elseif ($NoNewline) {Write-Host $value -n} return $value } else { if ($inlineError) { Cursor.Set 0 ([Console]::CursorTop) [Console]::Write("`r$ESC[2K $ESC[91m[✗]$ESC[0m ${Prompt}$ESC[91m${value}$ESC[0m") $len = 7 + $Prompt.Length + $value.Length [Console]::Write("`n$ESC[2K $ESC[91m${inlineError}$ESC[0m$ESC[1A$ESC[${len}G") } else {$chars.Clear(); $cur = 0; $needsRedraw = $true} } } "Escape" { if ($inlineError) {[Console]::Write("`n$ESC[2K$ESC[1A")} [Console]::Write("`r$ESC[2K`n"); return $null } "Backspace" {if ($cur -gt 0) {$chars.RemoveAt($cur - 1); $cur--; $needsRedraw = $true}} "Delete" {if ($cur -lt $chars.Count) {$chars.RemoveAt($cur); $needsRedraw = $true}} "LeftArrow" {if ($cur -gt 0) {$cur--; $needsRedraw = $true}} "RightArrow" {if ($cur -lt $chars.Count) {$cur++; $needsRedraw = $true}} "Home" {$cur = 0; $needsRedraw = $true} "End" {$cur = $chars.Count; $needsRedraw = $true} default { $ch = $key.KeyChar; $code = [int]$ch if ($code -ge 32 -and $code -le 126) {$chars.Insert($cur, $ch); $cur++; $needsRedraw = $true} } } if ($needsRedraw) { Cursor.Hide; $txt = [string]::new($chars.ToArray()) $f = $frames[$frameIdx % $frames.Length] [Console]::Write("`r$ESC[2K $ESC[96m${f}$ESC[0m $Prompt$txt") $tCol = 6 + $Prompt.Length + $cur; Cursor.Set $tCol ([Console]::CursorTop); Cursor.Show } } else { if (!$inlineError) { $f = $frames[$frameIdx % $frames.Length] [Console]::Write("$ESC[s$ESC[4G$ESC[96m${f}$ESC[0m$ESC[u") $frameIdx++; sleep -Milliseconds 80 } else {sleep -Milliseconds 50} } } } function Ask-Input {UI.Input @args} function UI.Menu { param([string[]]$Items, [string]$Title = "", [switch]$Multi, [string]$Color = "Cyan", [bool[]]$Status = $null, [switch]$NoHint) $ESC = [char]27 try {$fl = 0; while ([Console]::KeyAvailable -and $fl -lt 100) {$null = [Console]::ReadKey($true); $fl++}}catch {} $count = $Items.Count; $cur = 0; Cursor.Hide $indColor = switch ($Color) {"Purple" {"$ESC[95m"}; "Red" {"$ESC[91m"}; "Yellow" {"$ESC[93m"}; "Green" {"$ESC[92m"}; "Blue" {"$ESC[94m"}; default {"$ESC[96m"}} if (!$NoHint) { if ($Title) {Write-Host " $ESC[96m↑↓$ESC[0m $ESC[1m${Title}$ESC[0m"} Write-Host "" } foreach ($item in $Items) {Write-Host ""} $menuTop = [Console]::CursorTop - $count $checked = [bool[]]::new($count) $failedMap = if ($Status -and $Status.Count -eq $count) {$Status}else {[bool[]]::new($count)} $render = { for ($i = 0; $i -lt $count; $i++) { Cursor.Set 0 ($menuTop + $i); $num = $i + 1 if ($Multi) { $box = if ($checked[$i]) {"$ESC[91m[✓]$ESC[0m"}else {"$ESC[90m[ ]$ESC[0m"} if ($checked[$i] -or $failedMap[$i]) { if ($i -eq $cur) {Write-Host " $ESC[91m>$ESC[0m $box $ESC[91m$ESC[1m${num}. $($Items[$i])$ESC[0m "} else {Write-Host " $box $ESC[91m${num}. $($Items[$i])$ESC[0m "} } else { if ($i -eq $cur) {Write-Host " $ESC[91m>$ESC[0m $box $ESC[1m${num}. $($Items[$i])$ESC[0m "} else {Write-Host " $box $ESC[90m${num}. $($Items[$i])$ESC[0m "} } } else { if ($i -eq $cur) {Write-Host " ${indColor}●$ESC[0m ${indColor}$ESC[1m${num}. $($Items[$i])$ESC[0m "} else {Write-Host " $ESC[90m○$ESC[0m ${num}. $($Items[$i]) "} } } } $renderFinal = { for ($i = 0; $i -lt $count; $i++) { Cursor.Set 0 ($menuTop + $i); $num = $i + 1 if ($Multi) { $box = if ($checked[$i]) {"${indColor}[✓]$ESC[0m"}else {"$ESC[90m[ ]$ESC[0m"} if ($checked[$i]) {Write-Host " ${indColor}>$ESC[0m $box ${indColor}$ESC[1m${num}. $($Items[$i])$ESC[0m "} else {Write-Host " $box $ESC[90m${num}. $($Items[$i])$ESC[0m "} } else { if ($i -eq $cur) {Write-Host " ${indColor}●$ESC[0m ${indColor}$ESC[1m${num}. $($Items[$i])$ESC[0m "} else {Write-Host " $ESC[90m○$ESC[0m ${num}. $($Items[$i]) "} } } } & $render while ($true) { $key = [Console]::ReadKey($true) if ($key.Key -eq "UpArrow") {$cur = ($cur - 1 + $count) % $count} elseif ($key.Key -eq "DownArrow") {$cur = ($cur + 1) % $count} elseif ($Multi -and $key.Key -eq "Spacebar") {$checked[$cur] = !$checked[$cur]} elseif ($Multi -and ($key.KeyChar -eq "a" -or $key.KeyChar -eq "A")) { $any = $false; for ($i = 0; $i -lt $count; $i++) {if ($checked[$i]) {$any = $true; break}} for ($i = 0; $i -lt $count; $i++) {$checked[$i] = !$any} } elseif ($key.Key -eq "Enter") { & $renderFinal; Cursor.Set 0 ($menuTop + $count); Cursor.Show if ($Multi) { $r = [System.Collections.Generic.List[string]]::new() for ($i = 0; $i -lt $count; $i++) {if ($checked[$i]) {$r.Add($Items[$i])}} return , $r.ToArray() } else {return $cur} } elseif ($key.Key -in "Escape", "Backspace") { Cursor.Set 0 ($menuTop + $count); Cursor.Show; return $null } elseif ($key.KeyChar -ge "1" -and $key.KeyChar -le "9") { $val = [int]$key.KeyChar - [int][char]"1" if ($val -lt $count) { $cur = $val if ($Multi) {$checked[$cur] = !$checked[$cur]} else {& $renderFinal; Cursor.Set 0 ($menuTop + $count); Cursor.Show; return $cur} } } & $render } } function Ask-Menu {UI.Menu @args} function UI.BoxSpinner([scriptblock]$Action, [string]$Message, [string]$Color = "Cyan", [object[]]$ArgumentList = @()) { $rs = [runspacefactory]::CreateRunspace(); $rs.Open() $ps = [powershell]::Create(); $ps.Runspace = $rs $null = $ps.AddScript($Action) foreach ($arg in $ArgumentList) {$null = $ps.AddArgument($arg)} $handle = $ps.BeginInvoke(); $i = 0; Cursor.Hide Write-Host ""; Write-Host ""; Write-Host ""; Write-Host "" $global:lastSpinnerEsc = $false while (!$handle.IsCompleted) { if ([Console]::KeyAvailable) { if ([Console]::ReadKey($true).Key -eq "Escape") { $ps.Stop(); [Console]::Write("$([char]27)[4F$([char]27)[0J"); Cursor.Show $ps.Dispose(); $rs.Dispose(); $global:lastSpinnerEsc = $true; return $null } } [Console]::Write("$([char]27)[3F") UI.BoxGradient $Message ($i * 2) 38 $Color $i++; sleep -Milliseconds 80 } [Console]::Write("$([char]27)[4F$([char]27)[0J"); Cursor.Show try {$result = $ps.EndInvoke($handle)}catch {$result = $null} $ps.Dispose(); $rs.Dispose(); return $result } function UI.Spinner([scriptblock]$Action, [string]$Message = "Processing...", [string]$Color = "Cyan", [object[]]$ArgumentList = @()) { $successMsg = $Message ` -replace "Installing", "Installed" -replace "Removing", "Removed" ` -replace "Patching", "Patched" -replace "Fetching", "Fetched" ` -replace "Deleting", "Deleted" -replace "Updating", "Updated" ` -replace "Deploying", "Deployed" -replace "Creating", "Created" ` -replace "\.\.\.", "" $result = UI.BoxSpinner $Action $Message $Color $ArgumentList if ($global:lastSpinnerEsc) {return $null} $ESC = [char]27 if ($null -ne $result -and $result -ne $false) { [Console]::Write("`r$ESC[2K $ESC[92m[✓]$ESC[0m $successMsg`n") } else { [Console]::Write("`r$ESC[2K $ESC[91m[✗]$ESC[0m $successMsg`n") } return $result } function UI.Start-GradientBox([string]$text, [string]$theme = "Cyan") { $sb = { param($text, $theme) function GC2([double]$p, [string]$theme) { $ESC = [char]27; $t = if ($p -lt 0.5) {$p * 2.0}else {($p - 0.5) * 2.0} if ($theme -eq "Red") {if ($p -lt 0.5) {$c1 = 255, 77, 77; $c2 = 153, 27, 27}else {$c1 = 153, 27, 27; $c2 = 255, 77, 77}} elseif ($theme -eq "Green") {if ($p -lt 0.5) {$c1 = 34, 197, 94; $c2 = 20, 184, 166}else {$c1 = 20, 184, 166; $c2 = 34, 197, 94}} elseif ($theme -eq "Yellow") {if ($p -lt 0.5) {$c1 = 250, 204, 21; $c2 = 249, 115, 22}else {$c1 = 249, 115, 22; $c2 = 250, 204, 21}} else {if ($p -lt 0.5) {$c1 = 34, 211, 238; $c2 = 37, 99, 235}else {$c1 = 37, 99, 235; $c2 = 34, 211, 238}} $r = [int]($c1[0] + $t * ($c2[0] - $c1[0])); $g = [int]($c1[1] + $t * ($c2[1] - $c1[1])); $b = [int]($c1[2] + $t * ($c2[2] - $c1[2])) return "$ESC[38;2;$r;$g;${b}m" } $i = 0 while ($true) { $ESC = [char]27; $lbl = $text; $width = [math]::Max(38, $lbl.Length + 4); $total = $width + 2 + 1 + $width + 2 + 1; $offset = $i * 2 $sbTop = [System.Text.StringBuilder]::new(" "); $sbBot = [System.Text.StringBuilder]::new(" ") for ($k = 0; $k -lt ($width + 2); $k++) { $pTop = (($k + $offset) % $total) / $total $cTop = if ($k -eq 0) {"╭"}elseif ($k -eq $width + 1) {"╮"}else {"─"} $null = $sbTop.Append("$(GC2 $pTop $theme)$cTop") $pBot = ((($total - 2 - $k) + $offset) % $total) / $total $cBot = if ($k -eq 0) {"╰"}elseif ($k -eq $width + 1) {"╯"}else {"─"} $null = $sbBot.Append("$(GC2 $pBot $theme)$cBot") } $pLeft = ((($total - 1) + $offset) % $total) / $total; $midLeft = "$(GC2 $pLeft $theme)│$ESC[0m" $pRight = (($width + 2 + $offset) % $total) / $total; $midRight = "$(GC2 $pRight $theme)│$ESC[0m" $null = $sbTop.Append("$ESC[0m"); $null = $sbBot.Append("$ESC[0m") $spaces = $width - $lbl.Length; $padded = " $lbl" + (" " * ($spaces - 1)) $box = $sbTop.ToString() + "`n ${midLeft}$ESC[1m${padded}$ESC[0m${midRight}`n" + $sbBot.ToString() + "`n" [Console]::Write("$ESC[3F"); [Console]::Write($box); $i++; sleep -Milliseconds 80 } } Write-Host ""; Write-Host ""; Write-Host ""; Write-Host "" $rs = [runspacefactory]::CreateRunspace(); $rs.Open() $p = [powershell]::Create(); $p.Runspace = $rs $null = $p.AddScript($sb).AddArgument($text).AddArgument($theme) $handle = $p.BeginInvoke() return @{ps = $p; rs = $rs; handle = $handle} } function UI.Stop-GradientBox($job) { $job.ps.Stop(); $job.ps.Dispose(); $job.rs.Dispose() [Console]::Write("$([char]27)[4F$([char]27)[0J") }