diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..03a5d53f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Chris Titus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Microsoft.PowerShell_profile.ps1 b/Microsoft.PowerShell_profile.ps1 index 91992e0e..da4512b8 100644 --- a/Microsoft.PowerShell_profile.ps1 +++ b/Microsoft.PowerShell_profile.ps1 @@ -1,5 +1,7 @@ ### PowerShell Profile Refactor -### Version 1.03 - Refactored +### Version 1.04 - Refactored + +$debug = $false ################################################################################################################################# ############ ############ @@ -11,13 +13,118 @@ ############ ############ #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!# ############ ############ -############ IF YOU WANT TO MAKE CHANGES, USE THE Edit-Profile FUNCTION ############ -############ AND SAVE YOUR CHANGES IN THE FILE CREATED. ############ +############ TO ADD YOUR OWN CODE OR IF YOU WANT TO OVERRIDE ANY OF THESE VARIABLES ############ +############ OR FUNCTIONS. USE THE Edit-Profile FUNCTION TO CREATE YOUR OWN profile.ps1 FILE. ############ +############ TO OVERRIDE IN YOUR NEW profile.ps1 FILE, REWRITE THE VARIABLE ############ +############ OR FUNCTION, ADDING "_Override" TO THE NAME. ############ +############ ############ +############ THE FOLLOWING VARIABLES RESPECT _Override: ############ +############ $EDITOR_Override ############ +############ $debug_Override ############ +############ $repo_root_Override [To point to a fork, for example] ############ +############ $timeFilePath_Override ############ +############ $updateInterval_Override ############ ############ ############ +############ THE FOLLOWING FUNCTIONS RESPECT _Override: ############ +############ Debug-Message_Override ############ +############ Update-Profile_Override ############ +############ Update-PowerShell_Override ############ +############ Clear-Cache_Override ############ +############ Get-Theme_Override ############ +############ WinUtilDev_Override [To call a fork, for example] ############ +############ Set-PredictionSource ############ ################################################################################################################################# -# Initial GitHub.com connectivity check with 1 second timeout -$canConnectToGitHub = Test-Connection github.com -Count 1 -Quiet -TimeoutSeconds 1 +if ($debug_Override){ + # If variable debug_Override is defined in profile.ps1 file + # then use it instead + $debug = $debug_Override +} else { + $debug = $false +} + +# Define the path to the file that stores the last execution time +if ($repo_root_Override){ + # If variable $repo_root_Override is defined in profile.ps1 file + # then use it instead + $repo_root = $repo_root_Override +} else { + $repo_root = "https://raw.githubusercontent.com/ChrisTitusTech" +} + +# Helper function for cross-edition compatibility +function Get-ProfileDir { + if ($PSVersionTable.PSEdition -eq "Core") { + return [Environment]::GetFolderPath("MyDocuments") + "\PowerShell" + } elseif ($PSVersionTable.PSEdition -eq "Desktop") { + return [Environment]::GetFolderPath("MyDocuments") + "\WindowsPowerShell" + } else { + Write-Error "Unsupported PowerShell edition: $($PSVersionTable.PSEdition)" + return $null + } +} + +# Define the path to the file that stores the last execution time +if ($timeFilePath_Override){ + # If variable $timeFilePath_Override is defined in profile.ps1 file + # then use it instead + $timeFilePath = $timeFilePath_Override +} else { + $profileDir = Get-ProfileDir + $timeFilePath = "$profileDir\LastExecutionTime.txt" +} + +# Define the update interval in days, set to -1 to always check +if ($updateInterval_Override){ + # If variable $updateInterval_Override is defined in profile.ps1 file + # then use it instead + $updateInterval = $updateInterval_Override +} else { + $updateInterval = 7 +} + +function Debug-Message{ + # If function "Debug-Message_Override" is defined in profile.ps1 file + # then call it instead. + if (Get-Command -Name "Debug-Message_Override" -ErrorAction SilentlyContinue) { + Debug-Message_Override + } else { + Write-Host "#######################################" -ForegroundColor Red + Write-Host "# Debug mode enabled #" -ForegroundColor Red + Write-Host "# ONLY FOR DEVELOPMENT #" -ForegroundColor Red + Write-Host "# #" -ForegroundColor Red + Write-Host "# IF YOU ARE NOT DEVELOPING #" -ForegroundColor Red + Write-Host "# JUST RUN \`Update-Profile\` #" -ForegroundColor Red + Write-Host "# to discard all changes #" -ForegroundColor Red + Write-Host "# and update to the latest profile #" -ForegroundColor Red + Write-Host "# version #" -ForegroundColor Red + Write-Host "#######################################" -ForegroundColor Red + } +} + +if ($debug) { + Debug-Message +} + + +# Opt-out of telemetry before doing anything, only if PowerShell is run as admin +if ([bool]([System.Security.Principal.WindowsIdentity]::GetCurrent()).IsSystem) { + [System.Environment]::SetEnvironmentVariable('POWERSHELL_TELEMETRY_OPTOUT', 'true', [System.EnvironmentVariableTarget]::Machine) +} + +# Initial GitHub.com connectivity check +function Test-GitHubConnection { + if ($PSVersionTable.PSEdition -eq "Core") { + # If PowerShell Core, use a 1 second timeout + return Test-Connection github.com -Count 1 -Quiet -TimeoutSeconds 1 + } else { + # For PowerShell Desktop, use .NET Ping class with timeout + $ping = New-Object System.Net.NetworkInformation.Ping + $result = $ping.Send("github.com", 1000) # 1 second timeout + return ($result.Status -eq "Success") + } +} +$global:canConnectToGitHub = Test-GitHubConnection # Import Modules and External Profiles # Ensure Terminal-Icons module is installed before importing @@ -30,60 +137,134 @@ if (Test-Path($ChocolateyProfile)) { Import-Module "$ChocolateyProfile" } -# Check for Profile Updates -function Update-Profile { - if (-not $global:canConnectToGitHub) { - Write-Host "Skipping profile update check due to GitHub.com not responding within 1 second." -ForegroundColor Yellow - return +# Safely read and parse the last execution date once to avoid exceptions when the file is missing or empty +$lastExecRaw = if (Test-Path $timeFilePath) { (Get-Content -Path $timeFilePath -Raw).Trim() } else { $null } +[Nullable[datetime]]$lastExec = $null +if (-not [string]::IsNullOrWhiteSpace($lastExecRaw)) { + [datetime]$parsed = [datetime]::MinValue + if ([datetime]::TryParseExact($lastExecRaw, 'yyyy-MM-dd', $null, [System.Globalization.DateTimeStyles]::None, [ref]$parsed)) { + $lastExec = $parsed } +} - try { - $url = "https://raw.githubusercontent.com/ChrisTitusTech/powershell-profile/main/Microsoft.PowerShell_profile.ps1" - $oldhash = Get-FileHash $PROFILE - Invoke-RestMethod $url -OutFile "$env:temp/Microsoft.PowerShell_profile.ps1" - $newhash = Get-FileHash "$env:temp/Microsoft.PowerShell_profile.ps1" - if ($newhash.Hash -ne $oldhash.Hash) { - Copy-Item -Path "$env:temp/Microsoft.PowerShell_profile.ps1" -Destination $PROFILE -Force - Write-Host "Profile has been updated. Please restart your shell to reflect changes" -ForegroundColor Magenta +# Check for Profile Updates +function Update-Profile { + # If function "Update-Profile_Override" is defined in profile.ps1 file + # then call it instead. + if (Get-Command -Name "Update-Profile_Override" -ErrorAction SilentlyContinue) { + Update-Profile_Override + } else { + try { + $url = "$repo_root/powershell-profile/main/Microsoft.PowerShell_profile.ps1" + $oldhash = Get-FileHash $PROFILE + Invoke-RestMethod $url -OutFile "$env:temp/Microsoft.PowerShell_profile.ps1" + $newhash = Get-FileHash "$env:temp/Microsoft.PowerShell_profile.ps1" + if ($newhash.Hash -ne $oldhash.Hash) { + Copy-Item -Path "$env:temp/Microsoft.PowerShell_profile.ps1" -Destination $PROFILE -Force + Write-Host "Profile has been updated. Please restart your shell to reflect changes" -ForegroundColor Magenta + } else { + Write-Host "Profile is up to date." -ForegroundColor Green + } + } catch { + Write-Error "Unable to check for `$profile updates: $_" + } finally { + Remove-Item "$env:temp/Microsoft.PowerShell_profile.ps1" -ErrorAction SilentlyContinue } - } catch { - Write-Error "Unable to check for `$profile updates" - } finally { - Remove-Item "$env:temp/Microsoft.PowerShell_profile.ps1" -ErrorAction SilentlyContinue } } -Update-Profile -function Update-PowerShell { - if (-not $global:canConnectToGitHub) { - Write-Host "Skipping PowerShell update check due to GitHub.com not responding within 1 second." -ForegroundColor Yellow - return - } +# Check if not in debug mode AND (updateInterval is -1 OR file doesn't exist OR time difference is greater than the update interval) +if (-not $debug -and ` + ($updateInterval -eq -1 -or ` + -not (Test-Path $timeFilePath) -or ` + $null -eq $lastExec -or ` + ((Get-Date) - $lastExec).TotalDays -gt $updateInterval)) { - try { - Write-Host "Checking for PowerShell updates..." -ForegroundColor Cyan - $updateNeeded = $false - $currentVersion = $PSVersionTable.PSVersion.ToString() - $gitHubApiUrl = "https://api.github.com/repos/PowerShell/PowerShell/releases/latest" - $latestReleaseInfo = Invoke-RestMethod -Uri $gitHubApiUrl - $latestVersion = $latestReleaseInfo.tag_name.Trim('v') - if ($currentVersion -lt $latestVersion) { - $updateNeeded = $true - } + Update-Profile + $currentTime = Get-Date -Format 'yyyy-MM-dd' + $currentTime | Out-File -FilePath $timeFilePath - if ($updateNeeded) { - Write-Host "Updating PowerShell..." -ForegroundColor Yellow - winget upgrade "Microsoft.PowerShell" --accept-source-agreements --accept-package-agreements - Write-Host "PowerShell has been updated. Please restart your shell to reflect changes" -ForegroundColor Magenta - } else { - Write-Host "Your PowerShell is up to date." -ForegroundColor Green +} elseif ($debug) { + Write-Warning "Skipping profile update check in debug mode" +} + +function Update-PowerShell { + # If function "Update-PowerShell_Override" is defined in profile.ps1 file + # then call it instead. + if (Get-Command -Name "Update-PowerShell_Override" -ErrorAction SilentlyContinue) { + Update-PowerShell_Override + } else { + try { + Write-Host "Checking for PowerShell updates..." -ForegroundColor Cyan + $updateNeeded = $false + $currentVersion = $PSVersionTable.PSVersion.ToString() + $gitHubApiUrl = "https://api.github.com/repos/PowerShell/PowerShell/releases/latest" + $latestReleaseInfo = Invoke-RestMethod -Uri $gitHubApiUrl + $latestVersion = $latestReleaseInfo.tag_name.Trim('v') + if ($currentVersion -lt $latestVersion) { + $updateNeeded = $true + } + + if ($updateNeeded) { + Write-Host "Updating PowerShell..." -ForegroundColor Yellow + Start-Process powershell.exe -ArgumentList "-NoProfile -Command winget upgrade Microsoft.PowerShell --accept-source-agreements --accept-package-agreements" -Wait -NoNewWindow + Write-Host "PowerShell has been updated. Please restart your shell to reflect changes" -ForegroundColor Magenta + } else { + Write-Host "Your PowerShell is up to date." -ForegroundColor Green + } + } catch { + Write-Error "Failed to update PowerShell. Error: $_" } - } catch { - Write-Error "Failed to update PowerShell. Error: $_" } } -Update-PowerShell +# skip in debug mode +# Check if not in debug mode AND (updateInterval is -1 OR file doesn't exist OR time difference is greater than the update interval) +if (-not $debug -and ` + ($updateInterval -eq -1 -or ` + -not (Test-Path $timeFilePath) -or ` + $null -eq $lastExec -or ` + ((Get-Date).Date - $lastExec.Date).TotalDays -gt $updateInterval)) { + + Update-PowerShell + $currentTime = Get-Date -Format 'yyyy-MM-dd' + $currentTime | Out-File -FilePath $timeFilePath +} elseif ($debug) { + Write-Warning "Skipping PowerShell update in debug mode" +} + +function Clear-Cache { + # If function "Clear-Cache_Override" is defined in profile.ps1 file + # then call it instead. + # ----------------------------------------------------------------- + # If you do override this function, you should should probably duplicate + # the following calls in your override function, just don't call this + # function from your override function, otherwise you'll be in an infinate loop. + if (Get-Command -Name "Clear-Cache_Override" -ErrorAction SilentlyContinue) { + Clear-Cache_Override + } else { + # add clear cache logic here + Write-Host "Clearing cache..." -ForegroundColor Cyan + + # Clear Windows Prefetch + Write-Host "Clearing Windows Prefetch..." -ForegroundColor Yellow + Remove-Item -Path "$env:SystemRoot\Prefetch\*" -Force -ErrorAction SilentlyContinue + + # Clear Windows Temp + Write-Host "Clearing Windows Temp..." -ForegroundColor Yellow + Remove-Item -Path "$env:SystemRoot\Temp\*" -Recurse -Force -ErrorAction SilentlyContinue + + # Clear User Temp + Write-Host "Clearing User Temp..." -ForegroundColor Yellow + Remove-Item -Path "$env:TEMP\*" -Recurse -Force -ErrorAction SilentlyContinue + + # Clear Internet Explorer Cache + Write-Host "Clearing Internet Explorer Cache..." -ForegroundColor Yellow + Remove-Item -Path "$env:LOCALAPPDATA\Microsoft\Windows\INetCache\*" -Recurse -Force -ErrorAction SilentlyContinue + + Write-Host "Cache clearing completed." -ForegroundColor Green + } +} # Admin Check and Prompt Customization $isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) @@ -101,19 +282,33 @@ function Test-CommandExists { } # Editor Configuration -$EDITOR = if (Test-CommandExists nvim) { 'nvim' } - elseif (Test-CommandExists pvim) { 'pvim' } - elseif (Test-CommandExists vim) { 'vim' } - elseif (Test-CommandExists vi) { 'vi' } - elseif (Test-CommandExists code) { 'code' } - elseif (Test-CommandExists notepad++) { 'notepad++' } - elseif (Test-CommandExists sublime_text) { 'sublime_text' } - else { 'notepad' } -Set-Alias -Name vim -Value $EDITOR - +if ($EDITOR_Override){ + $EDITOR = $EDITOR_Override +} else { + $EDITOR = if (Test-CommandExists nvim) { 'nvim' } + elseif (Test-CommandExists pvim) { 'pvim' } + elseif (Test-CommandExists vim) { 'vim' } + elseif (Test-CommandExists vi) { 'vi' } + elseif (Test-CommandExists code) { 'code' } + elseif (Test-CommandExists codium) { 'codium' } + elseif (Test-CommandExists notepad++) { 'notepad++' } + elseif (Test-CommandExists sublime_text) { 'sublime_text' } + else { 'notepad' } + Set-Alias -Name vim -Value $EDITOR +} +# Quick Access to Editing the Profile function Edit-Profile { vim $PROFILE.CurrentUserAllHosts } +Set-Alias -Name ep -Value Edit-Profile + +function Invoke-Profile { + if ($PSVersionTable.PSEdition -eq "Desktop") { + Write-Host "Note: Some Oh My Posh/PSReadLine errors are expected in PowerShell 5. The profile still works fine." -ForegroundColor Yellow + } + & $PROFILE +} + function touch($file) { "" | Out-File $file -Encoding ASCII } function ff($name) { Get-ChildItem -recurse -filter "*${name}*" -ErrorAction SilentlyContinue | ForEach-Object { @@ -122,17 +317,28 @@ function ff($name) { } # Network Utilities -function Get-PubIP { (Invoke-WebRequest http://ifconfig.me/ip).Content } +function pubip { (Invoke-WebRequest http://ifconfig.me/ip).Content } -# Open WinUtil +# Open WinUtil full-release function winutil { - iwr -useb https://christitus.com/win | iex + Invoke-Expression (Invoke-RestMethod https://christitus.com/win) +} + +# Open WinUtil dev-release +function winutildev { + # If function "WinUtilDev_Override" is defined in profile.ps1 file + # then call it instead. + if (Get-Command -Name "WinUtilDev_Override" -ErrorAction SilentlyContinue) { + WinUtilDev_Override + } else { + Invoke-Expression (Invoke-RestMethod https://christitus.com/windev) + } } # System Utilities function admin { if ($args.Count -gt 0) { - $argList = "& '$args'" + $argList = $args -join ' ' Start-Process wt -Verb runAs -ArgumentList "pwsh.exe -NoExit -Command $argList" } else { Start-Process wt -Verb runAs @@ -143,15 +349,43 @@ function admin { Set-Alias -Name su -Value admin function uptime { - if ($PSVersionTable.PSVersion.Major -eq 5) { - Get-WmiObject win32_operatingsystem | Select-Object @{Name='LastBootUpTime'; Expression={$_.ConverttoDateTime($_.lastbootuptime)}} | Format-Table -HideTableHeaders - } else { - net statistics workstation | Select-String "since" | ForEach-Object { $_.ToString().Replace('Statistics since ', '') } - } -} + try { + # find date/time format + $dateFormat = [System.Globalization.CultureInfo]::CurrentCulture.DateTimeFormat.ShortDatePattern + $timeFormat = [System.Globalization.CultureInfo]::CurrentCulture.DateTimeFormat.LongTimePattern -function reload-profile { - & $profile + # check powershell version + if ($PSVersionTable.PSVersion.Major -eq 5) { + $lastBoot = (Get-WmiObject win32_operatingsystem).LastBootUpTime + $bootTime = [System.Management.ManagementDateTimeConverter]::ToDateTime($lastBoot) + + # reformat lastBoot + $lastBoot = $bootTime.ToString("$dateFormat $timeFormat") + } else { + # the Get-Uptime cmdlet was introduced in PowerShell 6.0 + $lastBoot = (Get-Uptime -Since).ToString("$dateFormat $timeFormat") + $bootTime = [System.DateTime]::ParseExact($lastBoot, "$dateFormat $timeFormat", [System.Globalization.CultureInfo]::InvariantCulture) + } + + # Format the start time + $formattedBootTime = $bootTime.ToString("dddd, MMMM dd, yyyy HH:mm:ss", [System.Globalization.CultureInfo]::InvariantCulture) + " [$lastBoot]" + Write-Host "System started on: $formattedBootTime" -ForegroundColor DarkGray + + # calculate uptime + $uptime = (Get-Date) - $bootTime + + # Uptime in days, hours, minutes, and seconds + $days = $uptime.Days + $hours = $uptime.Hours + $minutes = $uptime.Minutes + $seconds = $uptime.Seconds + + # Uptime output + Write-Host ("Uptime: {0} days, {1} hours, {2} minutes, {3} seconds" -f $days, $hours, $minutes, $seconds) -ForegroundColor Blue + + } catch { + Write-Error "An error occurred while retrieving system uptime." + } } function unzip ($file) { @@ -164,22 +398,23 @@ function hb { Write-Error "No file path specified." return } - + $FilePath = $args[0] - + if (Test-Path $FilePath) { $Content = Get-Content $FilePath -Raw } else { Write-Error "File path does not exist." return } - + $uri = "http://bin.christitus.com/documents" try { $response = Invoke-RestMethod -Uri $uri -Method Post -Body $Content -ErrorAction Stop $hasteKey = $response.key $url = "http://bin.christitus.com/$hasteKey" - Write-Output $url + Set-Clipboard $url + Write-Output "$url copied to clipboard." } catch { Write-Error "Failed to upload the document. Error: $_" } @@ -217,13 +452,13 @@ function pgrep($name) { } function head { - param($Path, $n = 10) - Get-Content $Path -Head $n + param($Path, $n = 10) + Get-Content $Path -Head $n } function tail { - param($Path, $n = 10, [switch]$f = $false) - Get-Content $Path -Tail $n -Wait:$f + param($Path, $n = 10, [switch]$f = $false) + Get-Content $Path -Tail $n -Wait:$f } # Quick File Creation @@ -232,22 +467,53 @@ function nf { param($name) New-Item -ItemType "file" -Path . -Name $name } # Directory Management function mkcd { param($dir) mkdir $dir -Force; Set-Location $dir } +function trash($path) { + $fullPath = (Resolve-Path -Path $path).Path + + if (Test-Path $fullPath) { + $item = Get-Item $fullPath + + if ($item.PSIsContainer) { + # Handle directory + $parentPath = $item.Parent.FullName + } else { + # Handle file + $parentPath = $item.DirectoryName + } + + $shell = New-Object -ComObject 'Shell.Application' + $shellItem = $shell.NameSpace($parentPath).ParseName($item.Name) + + if ($item) { + $shellItem.InvokeVerb('delete') + Write-Host "Item '$fullPath' has been moved to the Recycle Bin." + } else { + Write-Host "Error: Could not find the item '$fullPath' to trash." + } + } else { + Write-Host "Error: Item '$fullPath' does not exist." + } +} + ### Quality of Life Aliases # Navigation Shortcuts -function docs { Set-Location -Path $HOME\Documents } - -function dtop { Set-Location -Path $HOME\Desktop } +function docs { + $docs = if(([Environment]::GetFolderPath("MyDocuments"))) {([Environment]::GetFolderPath("MyDocuments"))} else {$HOME + "\Documents"} + Set-Location -Path $docs +} -# Quick Access to Editing the Profile -function ep { vim $PROFILE } +function dtop { + $dtop = if ([Environment]::GetFolderPath("Desktop")) {[Environment]::GetFolderPath("Desktop")} else {$HOME + "\Documents"} + Set-Location -Path $dtop +} # Simplified Process Management function k9 { Stop-Process -Name $args[0] } # Enhanced Listing -function la { Get-ChildItem -Path . -Force | Format-Table -AutoSize } -function ll { Get-ChildItem -Path . -Force -Hidden | Format-Table -AutoSize } +function la { Get-ChildItem | Format-Table -AutoSize } +function ll { Get-ChildItem -Force | Format-Table -AutoSize } # Git Shortcuts function gs { git status } @@ -256,9 +522,11 @@ function ga { git add . } function gc { param($m) git commit -m "$m" } -function gp { git push } +function gpush { git push } + +function gpull { git pull } -function g { z Github } +function g { __zoxide_z github } function gcl { git clone "$args" } @@ -277,8 +545,8 @@ function sysinfo { Get-ComputerInfo } # Networking Utilities function flushdns { - Clear-DnsClientCache - Write-Host "DNS has been flushed" + Clear-DnsClientCache + Write-Host "DNS has been flushed" } # Clipboard Utilities @@ -286,36 +554,141 @@ function cpy { Set-Clipboard $args[0] } function pst { Get-Clipboard } +# Set-PSReadLineOption Compatibility for PowerShell Desktop +function Set-PSReadLineOptionsCompat { + param([hashtable]$Options) + if ($PSVersionTable.PSEdition -eq "Core") { + Set-PSReadLineOption @Options + } else { + # Remove unsupported keys for Desktop and silence errors + $SafeOptions = $Options.Clone() + $SafeOptions.Remove('PredictionSource') + $SafeOptions.Remove('PredictionViewStyle') + Set-PSReadLineOption @SafeOptions + } +} + # Enhanced PowerShell Experience -Set-PSReadLineOption -Colors @{ - Command = 'Yellow' - Parameter = 'Green' - String = 'DarkCyan' -} - -# Get theme from profile.ps1 or use a default theme -function Get-Theme { - if (Test-Path -Path $PROFILE.CurrentUserAllHosts -PathType leaf) { - $existingTheme = Select-String -Raw -Path $PROFILE.CurrentUserAllHosts -Pattern "oh-my-posh init pwsh --config" - if ($null -ne $existingTheme) { - Invoke-Expression $existingTheme - return +# Enhanced PSReadLine Configuration +$PSReadLineOptions = @{ + EditMode = 'Windows' + HistoryNoDuplicates = $true + HistorySearchCursorMovesToEnd = $true + Colors = @{ + Command = '#87CEEB' # SkyBlue (pastel) + Parameter = '#98FB98' # PaleGreen (pastel) + Operator = '#FFB6C1' # LightPink (pastel) + Variable = '#DDA0DD' # Plum (pastel) + String = '#FFDAB9' # PeachPuff (pastel) + Number = '#B0E0E6' # PowderBlue (pastel) + Type = '#F0E68C' # Khaki (pastel) + Comment = '#D3D3D3' # LightGray (pastel) + Keyword = '#8367c7' # Violet (pastel) + Error = '#FF6347' # Tomato (keeping it close to red for visibility) + } + PredictionSource = 'History' + PredictionViewStyle = 'ListView' + BellStyle = 'None' +} +Set-PSReadLineOptionsCompat -Options $PSReadLineOptions + +# Custom key handlers +Set-PSReadLineKeyHandler -Key UpArrow -Function HistorySearchBackward +Set-PSReadLineKeyHandler -Key DownArrow -Function HistorySearchForward +Set-PSReadLineKeyHandler -Key Tab -Function MenuComplete +Set-PSReadLineKeyHandler -Chord 'Ctrl+d' -Function DeleteChar +Set-PSReadLineKeyHandler -Chord 'Ctrl+w' -Function BackwardDeleteWord +Set-PSReadLineKeyHandler -Chord 'Alt+d' -Function DeleteWord +Set-PSReadLineKeyHandler -Chord 'Ctrl+LeftArrow' -Function BackwardWord +Set-PSReadLineKeyHandler -Chord 'Ctrl+RightArrow' -Function ForwardWord +Set-PSReadLineKeyHandler -Chord 'Ctrl+z' -Function Undo +Set-PSReadLineKeyHandler -Chord 'Ctrl+y' -Function Redo + +# Custom functions for PSReadLine +Set-PSReadLineOption -AddToHistoryHandler { + param($line) + $sensitive = @('password', 'secret', 'token', 'apikey', 'connectionstring') + $hasSensitive = $sensitive | Where-Object { $line -match $_ } + return ($null -eq $hasSensitive) +} + +# Fix Set-PredictionSource for Desktop +function Set-PredictionSource { + # If "Set-PredictionSource_Override" is defined in profile.ps1 file + # then call it instead. + if (Get-Command -Name "Set-PredictionSource_Override" -ErrorAction SilentlyContinue) { + Set-PredictionSource_Override + } elseif ($PSVersionTable.PSEdition -eq "Core") { + # Improved prediction settings + Set-PSReadLineOption -PredictionSource HistoryAndPlugin + Set-PSReadLineOption -MaximumHistoryCount 10000 + } else { + # Desktop version - use History only + Set-PSReadLineOption -MaximumHistoryCount 10000 + } +} +Set-PredictionSource + +# Custom completion for common commands +$scriptblock = { + param($wordToComplete, $commandAst, $cursorPosition) + $customCompletions = @{ + 'git' = @('status', 'add', 'commit', 'push', 'pull', 'clone', 'checkout') + 'npm' = @('install', 'start', 'run', 'test', 'build') + 'deno' = @('run', 'compile', 'bundle', 'test', 'lint', 'fmt', 'cache', 'info', 'doc', 'upgrade') + } + + $command = $commandAst.CommandElements[0].Value + if ($customCompletions.ContainsKey($command)) { + $customCompletions[$command] | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { + [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } + } +} +Register-ArgumentCompleter -Native -CommandName git, npm, deno -ScriptBlock $scriptblock + +$scriptblock = { + param($wordToComplete, $commandAst, $cursorPosition) + dotnet complete --position $cursorPosition $commandAst.ToString() | + ForEach-Object { + [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) + } +} +Register-ArgumentCompleter -Native -CommandName dotnet -ScriptBlock $scriptblock + +# If function "Get-Theme_Override" is defined in profile.ps1 file +# then call it instead. +if (Get-Command -Name "Get-Theme_Override" -ErrorAction SilentlyContinue) { + Get-Theme_Override +} else { + # Oh My Posh initialization with local theme fallback and auto-download + $localThemePath = Join-Path (Get-ProfileDir) "cobalt2.omp.json" + if (-not (Test-Path $localThemePath)) { + # Try to download the theme file to the detected local path + $themeUrl = "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/cobalt2.omp.json" + try { + Invoke-RestMethod -Uri $themeUrl -OutFile $localThemePath + Write-Host "Downloaded missing Oh My Posh theme to $localThemePath" + } catch { + Write-Warning "Failed to download theme file. Falling back to remote theme. Error: $_" + } + } + if (Test-Path $localThemePath) { + oh-my-posh init pwsh --config $localThemePath | Invoke-Expression } else { + # Fallback to remote theme if local file doesn't exist oh-my-posh init pwsh --config https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/cobalt2.omp.json | Invoke-Expression } } -## Final Line to set prompt -Get-Theme if (Get-Command zoxide -ErrorAction SilentlyContinue) { - Invoke-Expression (& { (zoxide init --cmd cd powershell | Out-String) }) + Invoke-Expression (& { (zoxide init --cmd z powershell | Out-String) }) } else { Write-Host "zoxide command not found. Attempting to install via winget..." try { winget install -e --id ajeetdsouza.zoxide Write-Host "zoxide installed successfully. Initializing..." - Invoke-Expression (& { (zoxide init powershell | Out-String) }) + Invoke-Expression (& { (zoxide init --cmd z powershell | Out-String) }) } catch { Write-Error "Failed to install zoxide. Error: $_" } @@ -323,89 +696,66 @@ if (Get-Command zoxide -ErrorAction SilentlyContinue) { # Help Function function Show-Help { - @" -PowerShell Profile Help -======================= - -Update-Profile - Checks for profile updates from a remote repository and updates if necessary. - -Update-PowerShell - Checks for the latest PowerShell release and updates if a new version is available. - -Edit-Profile - Opens the current user's profile for editing using the configured editor. - -touch - Creates a new empty file. - -ff - Finds files recursively with the specified name. - -Get-PubIP - Retrieves the public IP address of the machine. - -winutil - Runs the WinUtil script from Chris Titus Tech. - -uptime - Displays the system uptime. - -reload-profile - Reloads the current user's PowerShell profile. - -unzip - Extracts a zip file to the current directory. - -hb - Uploads the specified file's content to a hastebin-like service and returns the URL. - -grep [dir] - Searches for a regex pattern in files within the specified directory or from the pipeline input. - -df - Displays information about volumes. - -sed - Replaces text in a file. - -which - Shows the path of the command. - -export - Sets an environment variable. - -pkill - Kills processes by name. - -pgrep - Lists processes by name. - -head [n] - Displays the first n lines of a file (default 10). - -tail [n] - Displays the last n lines of a file (default 10). - -nf - Creates a new file with the specified name. - -mkcd - Creates and changes to a new directory. - -docs - Changes the current directory to the user's Documents folder. - -dtop - Changes the current directory to the user's Desktop folder. - -ep - Opens the profile for editing. - -k9 - Kills a process by name. - -la - Lists all files in the current directory with detailed formatting. - -ll - Lists all files, including hidden, in the current directory with detailed formatting. - -gs - Shortcut for 'git status'. - -ga - Shortcut for 'git add .'. - -gc - Shortcut for 'git commit -m'. - -gp - Shortcut for 'git push'. - -g - Changes to the GitHub directory. - -gcom - Adds all changes and commits with the specified message. - -lazyg - Adds all changes, commits with the specified message, and pushes to the remote repository. - -sysinfo - Displays detailed system information. - -flushdns - Clears the DNS cache. - -cpy - Copies the specified text to the clipboard. - -pst - Retrieves text from the clipboard. - -Use 'Show-Help' to display this help message. + $helpText = @" +$($PSStyle.Foreground.Cyan)PowerShell Profile Help$($PSStyle.Reset) +$($PSStyle.Foreground.Yellow)=======================$($PSStyle.Reset) +$($PSStyle.Foreground.Green)Edit-Profile$($PSStyle.Reset) - Opens the current user's profile for editing using the configured editor. +$($PSStyle.Foreground.Green)Update-Profile$($PSStyle.Reset) - Checks for profile updates from a remote repository and updates if necessary. +$($PSStyle.Foreground.Green)Update-PowerShell$($PSStyle.Reset) - Checks for the latest PowerShell release and updates if a new version is available. + +$($PSStyle.Foreground.Cyan)Git Shortcuts$($PSStyle.Reset) +$($PSStyle.Foreground.Yellow)=======================$($PSStyle.Reset) +$($PSStyle.Foreground.Green)g$($PSStyle.Reset) - Changes to the GitHub directory. +$($PSStyle.Foreground.Green)ga$($PSStyle.Reset) - Shortcut for 'git add .'. +$($PSStyle.Foreground.Green)gc$($PSStyle.Reset) - Shortcut for 'git commit -m'. +$($PSStyle.Foreground.Green)gcl$($PSStyle.Reset) - Shortcut for 'git clone'. +$($PSStyle.Foreground.Green)gcom$($PSStyle.Reset) - Adds all changes and commits with the specified message. +$($PSStyle.Foreground.Green)gp$($PSStyle.Reset) - Shortcut for 'git push'. +$($PSStyle.Foreground.Green)gpull$($PSStyle.Reset) - Shortcut for 'git pull'. +$($PSStyle.Foreground.Green)gpush$($PSStyle.Reset) - Shortcut for 'git push'. +$($PSStyle.Foreground.Green)gs$($PSStyle.Reset) - Shortcut for 'git status'. +$($PSStyle.Foreground.Green)lazyg$($PSStyle.Reset) - Adds all changes, commits with the specified message, and pushes to the remote repository. + +$($PSStyle.Foreground.Cyan)Shortcuts$($PSStyle.Reset) +$($PSStyle.Foreground.Yellow)=======================$($PSStyle.Reset) +$($PSStyle.Foreground.Green)cpy$($PSStyle.Reset) - Copies the specified text to the clipboard. +$($PSStyle.Foreground.Green)df$($PSStyle.Reset) - Displays information about volumes. +$($PSStyle.Foreground.Green)docs$($PSStyle.Reset) - Changes the current directory to the user's Documents folder. +$($PSStyle.Foreground.Green)dtop$($PSStyle.Reset) - Changes the current directory to the user's Desktop folder. +$($PSStyle.Foreground.Green)ep$($PSStyle.Reset) - Opens the profile for editing. +$($PSStyle.Foreground.Green)export$($PSStyle.Reset) - Sets an environment variable. +$($PSStyle.Foreground.Green)ff$($PSStyle.Reset) - Finds files recursively with the specified name. +$($PSStyle.Foreground.Green)flushdns$($PSStyle.Reset) - Clears the DNS cache. +$($PSStyle.Foreground.Green)pubip$($PSStyle.Reset) - Retrieves the public IP address of the machine. +$($PSStyle.Foreground.Green)grep$($PSStyle.Reset) [dir] - Searches for a regex pattern in files within the specified directory or from the pipeline input. +$($PSStyle.Foreground.Green)hb$($PSStyle.Reset) - Uploads the specified file's content to a hastebin-like service and returns the URL. +$($PSStyle.Foreground.Green)head$($PSStyle.Reset) [n] - Displays the first n lines of a file (default 10). +$($PSStyle.Foreground.Green)k9$($PSStyle.Reset) - Kills a process by name. +$($PSStyle.Foreground.Green)la$($PSStyle.Reset) - Lists all files in the current directory with detailed formatting. +$($PSStyle.Foreground.Green)ll$($PSStyle.Reset) - Lists all files, including hidden, in the current directory with detailed formatting. +$($PSStyle.Foreground.Green)mkcd$($PSStyle.Reset) - Creates and changes to a new directory. +$($PSStyle.Foreground.Green)nf$($PSStyle.Reset) - Creates a new file with the specified name. +$($PSStyle.Foreground.Green)pgrep$($PSStyle.Reset) - Lists processes by name. +$($PSStyle.Foreground.Green)pkill$($PSStyle.Reset) - Kills processes by name. +$($PSStyle.Foreground.Green)pst$($PSStyle.Reset) - Retrieves text from the clipboard. +$($PSStyle.Foreground.Green)sed$($PSStyle.Reset) - Replaces text in a file. +$($PSStyle.Foreground.Green)sysinfo$($PSStyle.Reset) - Displays detailed system information. +$($PSStyle.Foreground.Green)tail$($PSStyle.Reset) [n] - Displays the last n lines of a file (default 10). +$($PSStyle.Foreground.Green)touch$($PSStyle.Reset) - Creates a new empty file. +$($PSStyle.Foreground.Green)unzip$($PSStyle.Reset) - Extracts a zip file to the current directory. +$($PSStyle.Foreground.Green)uptime$($PSStyle.Reset) - Displays the system uptime. +$($PSStyle.Foreground.Green)which$($PSStyle.Reset) - Shows the path of the command. +$($PSStyle.Foreground.Green)winutil$($PSStyle.Reset) - Runs the latest WinUtil full-release script from Chris Titus Tech. +$($PSStyle.Foreground.Green)winutildev$($PSStyle.Reset) - Runs the latest WinUtil pre-release script from Chris Titus Tech. +$($PSStyle.Foreground.Yellow)=======================$($PSStyle.Reset) + +Use '$($PSStyle.Foreground.Magenta)Show-Help$($PSStyle.Reset)' to display this help message. "@ + Write-Host $helpText } -Write-Host "Use 'Show-Help' to display help" \ No newline at end of file + +if (Test-Path "$PSScriptRoot\CTTcustom.ps1") { + Invoke-Expression -Command "& `"$PSScriptRoot\CTTcustom.ps1`"" +} + +Write-Host "$($PSStyle.Foreground.Yellow)Use 'Show-Help' to display help$($PSStyle.Reset)" diff --git a/README.md b/README.md index c126960a..89860237 100644 --- a/README.md +++ b/README.md @@ -12,15 +12,56 @@ irm "https://github.com/ChrisTitusTech/powershell-profile/raw/main/setup.ps1" | ## 🛠️ Fix the Missing Font -After running the script, you'll find a downloaded `cove.zip` file in the folder you executed the script from. Follow these steps to install the required nerd fonts: +After running the script, you'll have two options for installing a font patched to support icons in PowerShell: + +### 1) You will find a downloaded `cove.zip` file in the folder you executed the script from. Follow these steps to install the patched `Caskaydia Cove` nerd font family: 1. Extract the `cove.zip` file. 2. Locate and install the nerd fonts. +### 2) With `oh-my-posh` (loaded automatically through the PowerShell profile script hosted on this repo): +1. Run the command `oh-my-posh font install` +2. A list of Nerd Fonts will appear like so: +
+PS> oh-my-posh font install
+
+   Select font
+
+  > 0xProto
+    3270
+    Agave
+    AnonymousPro
+    Arimo
+    AurulentSansMono
+    BigBlueTerminal
+    BitstreamVeraSansMono
+
+    •••••••••
+    ↑/k up • ↓/j down • q quit • ? more
+3. With the up/down arrow keys, select the font you would like to install and press ENTER +4. DONE! + ## Customize this profile **Do not make any changes to the `Microsoft.PowerShell_profile.ps1` file**, since it's hashed and automatically overwritten by any commits to this repository. -After the profile is installed and active, run the `Edit-Profile` function to create a separate profile file for your current user. Make any changes and customizations in this new file named `profile.ps1`. +After the profile is installed and active, run the `Edit-Profile` function to create a separate profile file [`profile.ps1`] for your current user. Add any custom code, and/or override VARIABLES/FUNCTIONS in `Microsoft.PowerShell_profile.ps1` by adding any of the following Variable or Function names: + +THE FOLLOWING VARIABLES RESPECT _Override: +
+$EDITOR_Override
+$debug_Override
+$repo_root_Override  [To point to a fork, for example]
+$timeFilePath_Override
+$updateInterval_Override
+
-Now, enjoy your enhanced and stylish PowerShell experience! 🚀 +THE FOLLOWING FUNCTIONS RESPECT _Override: _(do not call the original function from your override function, or you'll create an infinite loop)_ +
+Debug-Message_Override
+Update-Profile_Override
+Update-PowerShell_Override
+Clear-Cache_Override
+Get-Theme_Override
+WinUtilDev_Override [To call a fork, for example]
+
diff --git a/setprofile.ps1 b/setprofile.ps1 new file mode 100644 index 00000000..32e135c2 --- /dev/null +++ b/setprofile.ps1 @@ -0,0 +1,2 @@ +$profilePath = Split-Path -Path $PROFILE +copy .\Microsoft.PowerShell_profile.ps1 $profilePath \ No newline at end of file diff --git a/setup.ps1 b/setup.ps1 index e2149df1..8e4546ee 100644 --- a/setup.ps1 +++ b/setup.ps1 @@ -7,7 +7,7 @@ if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdenti # Function to test internet connectivity function Test-InternetConnection { try { - $testConnection = Test-Connection -ComputerName www.google.com -Count 1 -ErrorAction Stop + Test-Connection -ComputerName www.google.com -Count 1 -ErrorAction Stop | Out-Null return $true } catch { @@ -16,6 +16,60 @@ function Test-InternetConnection { } } +# Function to install Nerd Fonts +function Install-NerdFonts { + param ( + [string]$FontName = "CascadiaCode", + [string]$FontDisplayName = "CaskaydiaCove NF", + [string]$Version = "3.2.1" + ) + + try { + [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") + $fontFamilies = (New-Object System.Drawing.Text.InstalledFontCollection).Families.Name + if ($fontFamilies -notcontains "${FontDisplayName}") { + $fontZipUrl = "https://github.com/ryanoasis/nerd-fonts/releases/download/v${Version}/${FontName}.zip" + $zipFilePath = "$env:TEMP\${FontName}.zip" + $extractPath = "$env:TEMP\${FontName}" + + $webClient = New-Object System.Net.WebClient + $webClient.DownloadFileAsync((New-Object System.Uri($fontZipUrl)), $zipFilePath) + + while ($webClient.IsBusy) { + Start-Sleep -Seconds 2 + } + + Expand-Archive -Path $zipFilePath -DestinationPath $extractPath -Force + $destination = (New-Object -ComObject Shell.Application).Namespace(0x14) + Get-ChildItem -Path $extractPath -Recurse -Filter "*.ttf" | ForEach-Object { + If (-not(Test-Path "C:\Windows\Fonts\$($_.Name)")) { + $destination.CopyHere($_.FullName, 0x10) + } + } + + Remove-Item -Path $extractPath -Recurse -Force + Remove-Item -Path $zipFilePath -Force + } else { + Write-Host "Font ${FontDisplayName} already installed" + } + } + catch { + Write-Error "Failed to download or install ${FontDisplayName} font. Error: $_" + } +} + +# Helper function for cross-edition compatibility +function Get-ProfileDir { + if ($PSVersionTable.PSEdition -eq "Core") { + return "$env:userprofile\Documents\PowerShell" + } elseif ($PSVersionTable.PSEdition -eq "Desktop") { + return "$env:userprofile\Documents\WindowsPowerShell" + } else { + Write-Error "Unsupported PowerShell edition: $($PSVersionTable.PSEdition)" + break + } +} + # Check for internet connectivity before proceeding if (-not (Test-InternetConnection)) { break @@ -24,19 +78,10 @@ if (-not (Test-InternetConnection)) { # Profile creation or update if (!(Test-Path -Path $PROFILE -PathType Leaf)) { try { - # Detect Version of PowerShell & Create Profile directories if they do not exist. - $profilePath = "" - if ($PSVersionTable.PSEdition -eq "Core") { - $profilePath = "$env:userprofile\Documents\Powershell" - } - elseif ($PSVersionTable.PSEdition -eq "Desktop") { - $profilePath = "$env:userprofile\Documents\WindowsPowerShell" - } - + $profilePath = Get-ProfileDir if (!(Test-Path -Path $profilePath)) { - New-Item -Path $profilePath -ItemType "directory" + New-Item -Path $profilePath -ItemType "directory" -Force } - Invoke-RestMethod https://github.com/ChrisTitusTech/powershell-profile/raw/main/Microsoft.PowerShell_profile.ps1 -OutFile $PROFILE Write-Host "The profile @ [$PROFILE] has been created." Write-Host "If you want to make any personal changes or customizations, please do so at [$profilePath\Profile.ps1] as there is an updater in the installed profile which uses the hash to update the profile and will lead to loss of changes" @@ -47,13 +92,37 @@ if (!(Test-Path -Path $PROFILE -PathType Leaf)) { } else { try { - Get-Item -Path $PROFILE | Move-Item -Destination "oldprofile.ps1" -Force + $backupPath = Join-Path (Split-Path $PROFILE) "oldprofile.ps1" + Move-Item -Path $PROFILE -Destination $backupPath -Force Invoke-RestMethod https://github.com/ChrisTitusTech/powershell-profile/raw/main/Microsoft.PowerShell_profile.ps1 -OutFile $PROFILE - Write-Host "The profile @ [$PROFILE] has been created and old profile removed." - Write-Host "Please back up any persistent components of your old profile to [$HOME\Documents\PowerShell\Profile.ps1] as there is an updater in the installed profile which uses the hash to update the profile and will lead to loss of changes" + Write-Host "✅ PowerShell profile at [$PROFILE] has been updated." + Write-Host "📦 Your old profile has been backed up to [$backupPath]" + Write-Host "⚠️ NOTE: Please back up any persistent components of your old profile to [$HOME\Documents\PowerShell\Profile.ps1] as there is an updater in the installed profile which uses the hash to update the profile and will lead to loss of changes" + } + catch { + Write-Error "❌ Failed to backup and update the profile. Error: $_" + } +} + +# Function to download Oh My Posh theme locally +function Install-OhMyPoshTheme { + param ( + [string]$ThemeName = "cobalt2", + [string]$ThemeUrl = "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/cobalt2.omp.json" + ) + $profilePath = Get-ProfileDir + if (!(Test-Path -Path $profilePath)) { + New-Item -Path $profilePath -ItemType "directory" + } + $themeFilePath = Join-Path $profilePath "$ThemeName.omp.json" + try { + Invoke-RestMethod -Uri $ThemeUrl -OutFile $themeFilePath + Write-Host "Oh My Posh theme '$ThemeName' has been downloaded to [$themeFilePath]" + return $themeFilePath } catch { - Write-Error "Failed to backup and update the profile. Error: $_" + Write-Error "Failed to download Oh My Posh theme. Error: $_" + return $null } } @@ -65,37 +134,14 @@ catch { Write-Error "Failed to install Oh My Posh. Error: $_" } -# Font Install -try { - [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") - $fontFamilies = (New-Object System.Drawing.Text.InstalledFontCollection).Families.Name - - if ($fontFamilies -notcontains "CaskaydiaCove NF") { - $webClient = New-Object System.Net.WebClient - $webClient.DownloadFileAsync((New-Object System.Uri("https://github.com/ryanoasis/nerd-fonts/releases/download/v3.2.1/CascadiaCode.zip")), ".\CascadiaCode.zip") - - while ($webClient.IsBusy) { - Start-Sleep -Seconds 2 - } +# Download Oh My Posh theme locally +$themeInstalled = Install-OhMyPoshTheme -ThemeName "cobalt2" - Expand-Archive -Path ".\CascadiaCode.zip" -DestinationPath ".\CascadiaCode" -Force - $destination = (New-Object -ComObject Shell.Application).Namespace(0x14) - Get-ChildItem -Path ".\CascadiaCode" -Recurse -Filter "*.ttf" | ForEach-Object { - If (-not(Test-Path "C:\Windows\Fonts\$($_.Name)")) { - $destination.CopyHere($_.FullName, 0x10) - } - } - - Remove-Item -Path ".\CascadiaCode" -Recurse -Force - Remove-Item -Path ".\CascadiaCode.zip" -Force - } -} -catch { - Write-Error "Failed to download or install the Cascadia Code font. Error: $_" -} +# Font Install +Install-NerdFonts -FontName "CascadiaCode" -FontDisplayName "CaskaydiaCove NF" # Final check and message to the user -if ((Test-Path -Path $PROFILE) -and (winget list --name "OhMyPosh" -e) -and ($fontFamilies -contains "CaskaydiaCove NF")) { +if ((Test-Path -Path $PROFILE) -and (winget list --name "OhMyPosh" -e) -and ($fontFamilies -contains "CaskaydiaCove NF") -and $themeInstalled) { Write-Host "Setup completed successfully. Please restart your PowerShell session to apply changes." } else { Write-Warning "Setup completed with errors. Please check the error messages above." @@ -103,7 +149,10 @@ if ((Test-Path -Path $PROFILE) -and (winget list --name "OhMyPosh" -e) -and ($fo # Choco install try { - Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1')) + Set-ExecutionPolicy Bypass -Scope Process -Force + [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072 + $chocoScript = (New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1') + Invoke-Expression $chocoScript } catch { Write-Error "Failed to install Chocolatey. Error: $_"