Files
André BezekandClaude Opus 4.8 b94e653afa fix: build statusline emoji from codepoints to avoid mojibake
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
2026-07-19 12:27:07 +02:00

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))