Files
SpoTUI-Fork/scripts/install/windows/install.ps1
T

518 lines
15 KiB
PowerShell

$ThemeName = "SpoTUI"
$RepoUrl = "https://github.com/SkenSMasteR/SpoTUI"
$ThemesDir = Join-Path $env:APPDATA "spicetify\Themes"
$ThemePath = Join-Path $ThemesDir $ThemeName
$Esc = [char]27
$Reset = "$Esc[0m"
$BlockFull = [char]0x2588
$BlockLower = [char]0x2584
$BlockUpper = [char]0x2580
$BlockLeft = [char]0x258C
function Enable-VTMode {
$sig = @"
using System;
using System.Runtime.InteropServices;
public static class SpoTUINative {
[DllImport("kernel32.dll")]
public static extern IntPtr GetStdHandle(int nStdHandle);
[DllImport("kernel32.dll")]
public static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode);
[DllImport("kernel32.dll")]
public static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode);
}
"@
try {
Add-Type -TypeDefinition $sig -ErrorAction SilentlyContinue
$handle = [SpoTUINative]::GetStdHandle(-11)
$mode = 0
[SpoTUINative]::GetConsoleMode($handle, [ref]$mode) | Out-Null
[SpoTUINative]::SetConsoleMode($handle, $mode -bor 0x0004) | Out-Null
} catch {}
}
Enable-VTMode
try {
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
} catch {}
function Get-RGBCode($r, $g, $b) {
return "$Esc[38;2;$r;$g;${b}m"
}
$OrangeLight = Get-RGBCode 255 140 66
$OrangeDark = Get-RGBCode 224 123 57
$OrangeMid = Get-RGBCode 240 131 61
$SelectBg = "$Esc[48;2;255;140;66m$Esc[38;2;0;0;0m"
$White = "White"
$Gray = "DarkGray"
$Red = "Red"
$Green = "Green"
$Cyan = "Cyan"
function Get-GradientColor($index, $total) {
$r1 = 255; $g1 = 140; $b1 = 66
$r2 = 224; $g2 = 123; $b2 = 57
$t = if ($total -le 1) { 0 } else { $index / ($total - 1) }
$r = [int]($r1 + ($r2 - $r1) * $t)
$g = [int]($g1 + ($g2 - $g1) * $t)
$b = [int]($b1 + ($b2 - $b1) * $t)
return Get-RGBCode $r $g $b
}
function Get-AsciiArtLine($template) {
$out = $template
$out = $out.Replace("A", $BlockFull)
$out = $out.Replace("B", $BlockLower)
$out = $out.Replace("C", $BlockUpper)
$out = $out.Replace("D", $BlockLeft)
return $out
}
function Show-Header {
Clear-Host
Write-Host ""
$templates = @(
" BAAAAAAAA BAAAAAAAB BAAAAAAAB AAA AAA AB BA ",
" AAA AAA AAA AAA AAA AAA CAAAAAAAAAB AAA AAA AAA ",
" AAA AC AAA AAA AAA AAA CAAACCAA AAA AAA AAAD ",
" AAA AAA AAA AAA AAA AAA C AAA AAA AAAD ",
"CAAAAAAAAAAA CAAAAAAAAAC AAA AAA AAA AAA AAA AAAD ",
" AAA AAA AAA AAA AAA AAA AAA AAA ",
" BA AAA AAA AAA AAA AAA AAA AAA AAA ",
" BAAAAAAAAC BAAAAC CAAAAAAC BAAAAC AAAAAAAAC AC "
)
for ($i = 0; $i -lt $templates.Count; $i++) {
$color = Get-GradientColor $i $templates.Count
$line = Get-AsciiArtLine $templates[$i]
Write-Host "$color$line$Reset"
}
Write-Host ""
Write-Host "$OrangeMid Spicetify Theme Manager$Reset"
Write-Host "$OrangeDark =============================================================$Reset"
Write-Host ""
}
function Test-Dependencies {
$missing = @()
if (-not (Get-Command git -ErrorAction SilentlyContinue)) { $missing += "git" }
if (-not (Get-Command spicetify -ErrorAction SilentlyContinue)) { $missing += "spicetify" }
if ($missing.Count -gt 0) {
Write-Host " Missing dependencies: $($missing -join ', ')" -ForegroundColor $Red
Write-Host " Please install them before continuing." -ForegroundColor $Gray
return $false
}
return $true
}
function Get-DefaultBranch {
Push-Location $ThemePath
$ref = git symbolic-ref refs/remotes/origin/HEAD 2>$null
Pop-Location
if ($ref) {
$parts = $ref -split "/"
return $parts[$parts.Count - 1]
}
return "main"
}
function Test-IsDetached {
Push-Location $ThemePath
git symbolic-ref -q HEAD | Out-Null
$attached = $?
Pop-Location
return -not $attached
}
function Get-ThemeStatusDetailed {
if (-not (Test-Path $ThemePath)) {
return @{ Text = "Not Installed"; Color = $Red }
}
if (-not (Get-Command git -ErrorAction SilentlyContinue)) {
return @{ Text = "Installed"; Color = $Green }
}
Push-Location $ThemePath
git fetch origin --quiet 2>$null
$localHash = (git rev-parse HEAD 2>$null)
Pop-Location
if (Test-IsDetached) {
$shortHash = if ($localHash) { $localHash.Substring(0, 7) } else { "unknown" }
return @{ Text = "Installed (custom commit $shortHash)"; Color = $Cyan }
}
$branch = Get-DefaultBranch
Push-Location $ThemePath
$remoteHash = (git rev-parse "origin/$branch" 2>$null)
Pop-Location
if (-not $localHash -or -not $remoteHash) {
return @{ Text = "Installed"; Color = $Green }
}
if ($localHash -eq $remoteHash) {
return @{ Text = "Installed (up to date)"; Color = $Green }
}
return @{ Text = "Installed (outdated)"; Color = $Red }
}
function Install-Theme {
Show-Header
Write-Host "$OrangeLight Installing $ThemeName...$Reset"
Write-Host ""
if (-not (Test-Dependencies)) {
Pause-Return
return
}
if (-not (Test-Path $ThemesDir)) {
New-Item -ItemType Directory -Path $ThemesDir -Force | Out-Null
}
if (Test-Path $ThemePath) {
Write-Host "$OrangeMid Theme already exists locally. Pulling latest changes...$Reset"
Push-Location $ThemePath
git pull
Pop-Location
}
else {
Push-Location $ThemesDir
git clone $RepoUrl $ThemeName
Pop-Location
}
if (Test-Path $ThemePath) {
Write-Host ""
Write-Host "$OrangeMid Setting current theme to $ThemeName...$Reset"
spicetify config current_theme $ThemeName
Write-Host "$OrangeMid Applying Spicetify...$Reset"
spicetify apply
Write-Host ""
Write-Host " $ThemeName installed and applied successfully." -ForegroundColor $Green
}
else {
Write-Host ""
Write-Host " Installation failed. Check the errors above." -ForegroundColor $Red
}
Pause-Return
}
function Update-Theme {
Show-Header
Write-Host "$OrangeLight Updating $ThemeName...$Reset"
Write-Host ""
if (-not (Test-Path $ThemePath)) {
Write-Host " $ThemeName is not installed. Use Install instead." -ForegroundColor $Red
Pause-Return
return
}
if (-not (Test-Dependencies)) {
Pause-Return
return
}
if (Test-IsDetached) {
$branch = Get-DefaultBranch
Write-Host "$OrangeMid Currently on a custom commit. Returning to $branch...$Reset"
Push-Location $ThemePath
git checkout $branch --quiet
Pop-Location
}
Push-Location $ThemePath
git pull
Pop-Location
Write-Host ""
Write-Host "$OrangeMid Re-applying Spicetify...$Reset"
spicetify apply
Write-Host ""
Write-Host " $ThemeName updated successfully." -ForegroundColor $Green
Pause-Return
}
function Uninstall-Theme {
Show-Header
Write-Host "$OrangeLight Uninstalling $ThemeName...$Reset"
Write-Host ""
if (-not (Test-Path $ThemePath)) {
Write-Host " $ThemeName is not installed." -ForegroundColor $Red
Pause-Return
return
}
Write-Host " This will remove the theme folder and switch to Marketplace." -ForegroundColor $Gray
$confirm = Read-Host " Type Y to confirm"
if ($confirm -ne "Y" -and $confirm -ne "y") {
Write-Host " Cancelled." -ForegroundColor $Gray
Pause-Return
return
}
if (Get-Command spicetify -ErrorAction SilentlyContinue) {
Write-Host "$OrangeMid Switching Spicetify theme...$Reset"
spicetify config current_theme SpoTUI-
spicetify config current_theme marketplace
spicetify apply
}
Remove-Item -Path $ThemePath -Recurse -Force
Write-Host ""
Write-Host " $ThemeName has been uninstalled." -ForegroundColor $Green
Pause-Return
}
function Read-ArrowSelection {
param(
[string[]]$Items,
[int]$CurrentIndex = -1,
[scriptblock]$HeaderRenderer
)
$selectedIndex = 0
if ($CurrentIndex -ge 0) { $selectedIndex = $CurrentIndex }
while ($true) {
& $HeaderRenderer
for ($i = 0; $i -lt $Items.Count; $i++) {
$prefix = if ($i -eq $CurrentIndex) { "> " } else { " " }
$text = "$prefix$($Items[$i])"
if ($i -eq $selectedIndex) {
Write-Host "$SelectBg $text $Reset"
}
elseif ($i -eq $CurrentIndex) {
Write-Host $text -ForegroundColor $Green
}
else {
Write-Host $text -ForegroundColor $White
}
}
Write-Host ""
Write-Host "$OrangeDark =============================================================$Reset"
Write-Host " Up/Down to move, Enter to select, Esc to go back" -ForegroundColor $Gray
$key = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
$code = $key.VirtualKeyCode
if ($code -eq 38) {
if ($selectedIndex -gt 0) { $selectedIndex-- } else { $selectedIndex = $Items.Count - 1 }
}
elseif ($code -eq 40) {
if ($selectedIndex -lt $Items.Count - 1) { $selectedIndex++ } else { $selectedIndex = 0 }
}
elseif ($code -eq 13) {
return $selectedIndex
}
elseif ($code -eq 27) {
return -1
}
}
}
function Get-CommitList {
Push-Location $ThemePath
git fetch origin --quiet 2>$null
$rawLog = git log --all --pretty=format:"%H|%h|%ad|%s" --date=short
Pop-Location
$commits = @()
foreach ($line in $rawLog) {
$parts = $line -split "\|", 4
if ($parts.Count -eq 4) {
$commits += [PSCustomObject]@{
FullHash = $parts[0]
ShortHash = $parts[1]
Date = $parts[2]
Subject = $parts[3]
}
}
}
return $commits
}
function Show-CommitHistory {
if (-not (Test-Path $ThemePath)) {
Show-Header
Write-Host " $ThemeName is not installed." -ForegroundColor $Red
Pause-Return
return
}
if (-not (Test-Dependencies)) {
Pause-Return
return
}
$viewing = $true
while ($viewing) {
$commits = Get-CommitList
if ($commits.Count -eq 0) {
Show-Header
Write-Host " No commits found." -ForegroundColor $Red
Pause-Return
return
}
Push-Location $ThemePath
$currentHash = (git rev-parse HEAD 2>$null)
Pop-Location
$currentIndex = -1
$items = @()
for ($i = 0; $i -lt $commits.Count; $i++) {
$commit = $commits[$i]
$items += "$($commit.ShortHash) $($commit.Date) $($commit.Subject)"
if ($commit.FullHash -eq $currentHash) {
$currentIndex = $i
}
}
$returnLatestIndex = $items.Count
$items += "Return to latest version"
$backIndex = $items.Count
$items += "Back"
$headerRenderer = {
Show-Header
Write-Host "$OrangeLight Commit History$Reset"
Write-Host ""
}
$selection = Read-ArrowSelection -Items $items -CurrentIndex $currentIndex -HeaderRenderer $headerRenderer
if ($selection -eq -1 -or $selection -eq $backIndex) {
$viewing = $false
}
elseif ($selection -eq $returnLatestIndex) {
Update-Theme
}
elseif ($selection -ge 0 -and $selection -lt $commits.Count) {
Checkout-Commit $commits[$selection]
}
}
}
function Checkout-Commit($commit) {
Show-Header
Write-Host "$OrangeLight Checking out commit $($commit.ShortHash)...$Reset"
Write-Host " $($commit.Date) $($commit.Subject)" -ForegroundColor $Gray
Write-Host ""
Write-Host " This will switch the theme to this specific version." -ForegroundColor $Gray
$confirm = Read-Host " Type Y to confirm"
if ($confirm -ne "Y" -and $confirm -ne "y") {
Write-Host " Cancelled." -ForegroundColor $Gray
Pause-Return
return
}
Push-Location $ThemePath
git checkout $commit.FullHash --quiet
Pop-Location
if (Get-Command spicetify -ErrorAction SilentlyContinue) {
Write-Host ""
Write-Host "$OrangeMid Applying Spicetify...$Reset"
spicetify apply
}
Write-Host ""
Write-Host " $ThemeName is now on commit $($commit.ShortHash)." -ForegroundColor $Green
Pause-Return
}
function Check-ForUpdates {
Show-Header
Write-Host "$OrangeLight Checking for updates...$Reset"
Write-Host ""
if (-not (Test-Path $ThemePath)) {
Write-Host " $ThemeName is not installed." -ForegroundColor $Red
Pause-Return
return
}
if (-not (Test-Dependencies)) {
Pause-Return
return
}
$status = Get-ThemeStatusDetailed
Write-Host " Status: " -NoNewline -ForegroundColor $White
Write-Host $status.Text -ForegroundColor $status.Color
if ($status.Text -eq "Installed (outdated)") {
Write-Host ""
Write-Host " A newer version is available." -ForegroundColor $Gray
$confirm = Read-Host " Type Y to update now"
if ($confirm -eq "Y" -or $confirm -eq "y") {
Update-Theme
return
}
}
Pause-Return
}
function Pause-Return {
Write-Host ""
Write-Host " Press any key to return to the menu..." -ForegroundColor $Gray
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
}
function Show-Menu {
Show-Header
$status = Get-ThemeStatusDetailed
Write-Host " Status: " -NoNewline -ForegroundColor $White
Write-Host $status.Text -ForegroundColor $status.Color
Write-Host ""
Write-Host " [1] Install $ThemeName" -ForegroundColor $White
Write-Host " [2] Update $ThemeName" -ForegroundColor $White
Write-Host " [3] Uninstall $ThemeName" -ForegroundColor $White
Write-Host " [4] Commit History / Downgrade" -ForegroundColor $White
Write-Host " [5] Check for Updates" -ForegroundColor $White
Write-Host " [6] Exit" -ForegroundColor $White
Write-Host ""
Write-Host "$OrangeDark =============================================================$Reset"
Write-Host ""
$choice = Read-Host " Select an option"
return $choice
}
$running = $true
while ($running) {
$choice = Show-Menu
switch ($choice) {
"1" { Install-Theme }
"2" { Update-Theme }
"3" { Uninstall-Theme }
"4" { Show-CommitHistory }
"5" { Check-ForUpdates }
"6" { $running = $false }
default {
Show-Header
Write-Host " Invalid option." -ForegroundColor $Red
Pause-Return
}
}
}
Clear-Host