mirror of
https://github.com/Andre-Bzk/claude-statusline-kit.git
synced 2026-11-15 09:33:04 +01:00
Windows PowerShell 5.1 reads a BOM-less .ps1 as the ANSI codepage, so literal UTF-8 emoji bytes rendered as mojibake (⏳ -> â³, 📅 -> 📅). Build the hourglass and calendar glyphs from codepoints instead, matching how the bar/§ characters are already produced. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UW1aWQoAa6BPe1nNvkEYJU
67 lines
3.0 KiB
PowerShell
67 lines
3.0 KiB
PowerShell
# cship statusline wrapper
|
|
# Runs cship, then converts its usage_limits markers (§ 5|7 § pct § reset §)
|
|
# into progress bars styled identically to the context_bar module.
|
|
$ErrorActionPreference = 'SilentlyContinue'
|
|
[Console]::InputEncoding = New-Object System.Text.UTF8Encoding $false
|
|
[Console]::OutputEncoding = New-Object System.Text.UTF8Encoding $false
|
|
$OutputEncoding = New-Object System.Text.UTF8Encoding $false
|
|
|
|
$cship = Join-Path $env:USERPROFILE '.local\bin\cship.exe'
|
|
|
|
# Read the full Claude Code session JSON from stdin, hand it to cship.
|
|
$inp = [Console]::In.ReadToEnd()
|
|
$out = $inp | & $cship
|
|
# Starship's prompt carries a leading blank line (add_newline); drop leading/trailing
|
|
# blank lines so the statusline doesn't waste a row.
|
|
$text = ($out -join "`n").Trim("`r", "`n")
|
|
|
|
$S = [char]0x00A7
|
|
$esc = [char]0x1B
|
|
$rx = [regex]("$S([57])$S([0-9.]+)$S([^$S]*)$S")
|
|
|
|
$render = [System.Text.RegularExpressions.MatchEvaluator]{
|
|
param($m)
|
|
$type = $m.Groups[1].Value
|
|
$pct = [double]$m.Groups[2].Value
|
|
$reset = $m.Groups[3].Value
|
|
|
|
$width = 10
|
|
$filled = [int][math]::Round($pct / 100.0 * $width)
|
|
if ($filled -lt 0) { $filled = 0 }
|
|
if ($filled -gt $width) { $filled = $width }
|
|
$bar = ([string]([char]0x2588) * $filled) + ([string]([char]0x2591) * ($width - $filled))
|
|
|
|
# Same thresholds/colors as context_bar: warn >=40 (yellow), critical >=70 (bold red)
|
|
if ($pct -ge 70) { $color = '1;31' }
|
|
elseif ($pct -ge 40) { $color = '33' }
|
|
else { $color = '38;2;125;207;255' }
|
|
|
|
# Emoji are built from codepoints (not string literals) so the label renders
|
|
# correctly regardless of how this .ps1 is read from disk. Windows PowerShell
|
|
# 5.1 decodes a BOM-less script as the ANSI codepage, which would turn literal
|
|
# UTF-8 emoji bytes into mojibake (e.g. ⏳ -> â³, 📅 -> 📅).
|
|
$hourglass = [char]0x23F3 # ⏳ (BMP)
|
|
$calendar = [char]::ConvertFromUtf32(0x1F4C5) # 📅 (astral, needs surrogate pair)
|
|
if ($type -eq '5') { $label = "$hourglass 5h" } else { $label = "$calendar 7d" }
|
|
$pctTxt = '{0:0}%' -f $pct
|
|
$resetTxt = if ([string]::IsNullOrWhiteSpace($reset)) { '' } else { " ($reset)" }
|
|
|
|
"$esc[${color}m$label $bar $pctTxt$resetTxt$esc[0m"
|
|
}
|
|
|
|
# Compact the context_window token count marker (§T§<n>§) to K/M form, e.g.
|
|
# 42329 -> 42.3K. cship has no native thousands formatting, so it's done here.
|
|
$rxTok = [regex]("${S}T${S}([0-9]+)${S}")
|
|
$renderTok = [System.Text.RegularExpressions.MatchEvaluator]{
|
|
param($m)
|
|
$n = [double]$m.Groups[1].Value
|
|
$ic = [Globalization.CultureInfo]::InvariantCulture
|
|
if ($n -ge 1000000) { $v = ($n / 1000000).ToString('0.0', $ic) + 'M' }
|
|
elseif ($n -ge 1000) { $v = ($n / 1000).ToString('0.0', $ic) + 'K' }
|
|
else { $v = $n.ToString('0', $ic) }
|
|
$v -replace '\.0([KM]?)$', '$1'
|
|
}
|
|
$text = $rxTok.Replace($text, $renderTok)
|
|
|
|
[Console]::Out.Write($rx.Replace($text, $render))
|