CCCPaste

shell_install.ps1

<# =======================
  Windows Terminal Pro Stack (no WSL)
  - Windows Terminal
  - PowerShell 7 (default)
  - Git + Git Bash profile
  - oh-my-posh + posh-git + PSReadLine
  - Nerd Font (Cascadia Code NF)
  - Pretty prompt (Powerlevel10k-style)
======================== #>

$ErrorActionPreference = 'Stop'

function Install-WingetPackage {
  param(
    [Parameter(Mandatory)] [string] $Id
  )
  Write-Host ">> Installing $Id via winget..." -ForegroundColor Cyan
  winget install --id $Id --silent --accept-source-agreements --accept-package-agreements | Out-Null
}

# 1) Base apps
Install-WingetPackage Microsoft.WindowsTerminal
Install-WingetPackage Microsoft.PowerShell
Install-WingetPackage Git.Git
Install-WingetPackage JanDeDobbeleer.OhMyPosh
Install-WingetPackage NerdFonts.CascadiaCode
# Скачивает и ставит Cascadia Code Nerd Font (Regular + Bold + Italic)
Invoke-WebRequest -Uri "https://github.com/ryanoasis/nerd-fonts/releases/latest/download/CascadiaCode.zip" -OutFile "$env:TEMP\CascadiaCode.zip"
Expand-Archive "$env:TEMP\CascadiaCode.zip" -DestinationPath "$env:TEMP\CascadiaCodeNF" -Force
Get-ChildItem "$env:TEMP\CascadiaCodeNF" -Filter "*.ttf" | ForEach-Object { $_.FullName | ForEach-Object { Write-Host "Installing font: $_"; Copy-Item $_ "$env:WINDIR\Fonts\" -Force } }


# Paths
$PwshPath = "${env:ProgramFiles}\PowerShell\7\pwsh.exe"
if (-not (Test-Path $PwshPath)) {
  # Sometimes installed per-user
  $PwshPath = "${env:LocalAppData}\Microsoft\PowerShell\pwsh.exe"
}
if (-not (Test-Path $PwshPath)) {
  throw "PowerShell 7 (pwsh.exe) not found after install."
}

# 2) PS modules (install into PowerShell 7 scope)
$moduleInstallCmd = @'
Install-Module posh-git -Scope CurrentUser -Force
Install-Module PSReadLine -Scope CurrentUser -Force -SkipPublisherCheck
'@

& $PwshPath -NoProfile -NoLogo -Command $moduleInstallCmd

# 3) PowerShell profile setup (pretty prompt with oh-my-posh)
$ProfilePath = & $PwshPath -NoLogo -NoProfile -Command '$PROFILE'
$ProfileDir  = Split-Path $ProfilePath -Parent
if (-not (Test-Path $ProfileDir)) { New-Item -ItemType Directory -Path $ProfileDir -Force | Out-Null }

# oh-my-posh themes path (winget EXE install)
$PoshThemes = Join-Path $env:LOCALAPPDATA 'Programs\oh-my-posh\themes'
$ThemeFile  = Join-Path $PoshThemes 'powerlevel10k_modern.omp.json'
if (-not (Test-Path $ThemeFile)) {
  # fallback
  $ThemeFile = Join-Path $PoshThemes 'jandedobbeleer.omp.json'
}

$ProfileContent = @"
# ---------- Pro Prompt ----------
Import-Module posh-git
Import-Module PSReadLine

# Oh My Posh init (needs EXE installed by winget)
oh-my-posh init pwsh --config "$ThemeFile" | Invoke-Expression

# PSReadLine: history & suggestions
Set-PSReadLineOption -EditMode Windows
Set-PSReadLineOption -PredictionSource History
Set-PSReadLineOption -PredictionViewStyle ListView

# QoL
Set-Alias reload '& `$PROFILE'
"@

Set-Content -Path $ProfilePath -Value $ProfileContent -Encoding UTF8
Write-Host ">> PowerShell profile configured at: $ProfilePath" -ForegroundColor Green

# 4) Windows Terminal settings patch (set pwsh default, add Git Bash profile, set Nerd Font)
# Detect settings.json location(s)
$WTSettingsCandidates = @(
  (Join-Path $env:LOCALAPPDATA 'Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json'),
  (Join-Path $env:LOCALAPPDATA 'Microsoft\Windows Terminal\settings.json')
) | Where-Object { $_ -ne $null }

$WTSettings = $WTSettingsCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1

if (-not $WTSettings) {
  # Create minimal settings.json if missing (Store app usually generates it on first run)
  $FallbackDir = Join-Path $env:LOCALAPPDATA 'Microsoft\Windows Terminal'
  if (-not (Test-Path $FallbackDir)) { New-Item -ItemType Directory -Path $FallbackDir -Force | Out-Null }
  $WTSettings = Join-Path $FallbackDir 'settings.json'
  '{ "profiles": { "list": [] } }' | Set-Content -Path $WTSettings -Encoding UTF8
}

Write-Host ">> Patching Windows Terminal settings: $WTSettings" -ForegroundColor Cyan

# Load JSON safely
$raw = Get-Content $WTSettings -Raw
try { $json = $raw | ConvertFrom-Json -ErrorAction Stop } catch {
  throw "Cannot parse Windows Terminal settings.json. Open once and save settings from UI, then rerun."
}

if (-not $json.profiles) { $json | Add-Member -NotePropertyName profiles -NotePropertyValue (@{}) }
if (-not $json.profiles.list) { $json.profiles.list = @() }

function Set-Or-AddProperty {
  param(
    [Parameter(Mandatory)] $Object,
    [Parameter(Mandatory)] [string] $Name,
    [Parameter()] $Value
  )
  if ($null -ne $Object.PSObject.Properties[$Name]) {
    $Object.$Name = $Value
  } else {
    $Object | Add-Member -NotePropertyName $Name -NotePropertyValue $Value -Force
  }
}

function Ensure-Profile {
  param(
    [Parameter(Mandatory)][string] $Name,
    [Parameter(Mandatory)][string] $Cmd,
    [Parameter()][string] $Args,
    [Parameter()][string] $FontFace = 'Cascadia Code NF',
    [Parameter()][string] $Icon
  )

  # Найдём существующий профиль по имени
  $existing = $json.profiles.list | Where-Object { $_.name -eq $Name } | Select-Object -First 1

  $cmdline = if ($Args) { "$Cmd $Args" } else { $Cmd }

  if (-not $existing) {
    # Создаём новый профиль
    $existing = [pscustomobject]@{
      guid             = ([guid]::NewGuid()).ToString("B")
      name             = $Name
      commandline      = $cmdline
      startingDirectory= $null
      hidden           = $false
      font             = @{ face = $FontFace }
    }
    if ($Icon) { $existing | Add-Member -NotePropertyName icon -NotePropertyValue $Icon }
    $json.profiles.list += $existing
  } else {
    # Обновляем/добавляем свойства безопасно
    Set-Or-AddProperty -Object $existing -Name 'commandline' -Value $cmdline
    Set-Or-AddProperty -Object $existing -Name 'hidden' -Value $false

    # font: может отсутствовать или быть простым строковым полем в старых схемах
    if ($null -eq $existing.PSObject.Properties['font'] -or $existing.font -isnot [object]) {
      Set-Or-AddProperty -Object $existing -Name 'font' -Value (@{ face = $FontFace })
    } else {
      Set-Or-AddProperty -Object $existing.font -Name 'face' -Value $FontFace
    }

    if ($Icon) { Set-Or-AddProperty -Object $existing -Name 'icon' -Value $Icon }
  }

  return $existing
}

# Ensure PowerShell 7 profile (default)
$pwshProf = Ensure-Profile -Name 'PowerShell 7' -Cmd $PwshPath -Args '' -FontFace 'Cascadia Code NF' -Icon ''
# Ensure Git Bash profile
$GitBash = 'C:\Program Files\Git\bin\bash.exe'
$gitIcon = 'C:\Program Files\Git\mingw64\share\git\git-for-windows.ico'
$gitArgs = '--login -i'
$gitProf = $null
if (Test-Path $GitBash) {
	$iconPath = if (Test-Path $gitIcon) { $gitIcon } else { $null }
	$gitProf = Ensure-Profile -Name 'Git Bash' -Cmd $GitBash -Args $gitArgs -FontFace 'Cascadia Code NF' -Icon $iconPath
}

# Set default profile to PowerShell 7
$json.defaultProfile = $pwshProf.guid

# Save JSON (preserve formatting reasonably)
($json | ConvertTo-Json -Depth 10) | Set-Content -Path $WTSettings -Encoding UTF8

Write-Host "`n✅ Done. Please restart Windows Terminal." -ForegroundColor Green
Write-Host "   PowerShell 7 is the default profile with a pretty Git-aware prompt."
Write-Host "   Font set to: Cascadia Code Nerd Font"
Write-Host "   Extra profile added: Git Bash" -ForegroundColor DarkGray