From c239e191450f3472e9449c5ef2439af35bf20daf Mon Sep 17 00:00:00 2001 From: Andre <200930841+Andre-Bzk@users.noreply.github.com> Date: Sat, 18 Jul 2026 15:58:41 +0200 Subject: [PATCH] feat: auto-install a Nerd Font per-user when none is present Making the installer a turnkey package means a fresh PC should end up render-ready without manual font setup or an elevated prompt. The font is placed in the per-user store and registered under HKCU so no admin rights are required, and a failed download stays non-fatal because the statusline already renders through the OS emoji fallback. Detection matches both the spaced and unspaced registry naming so a second run correctly skips the download instead of re-copying a font file that is locked while in use. --- README.md | 35 ++++++++++++++++++-- install.ps1 | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 124 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index c206be9..1d29adb 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,9 @@ with a single command. - An **emoji-capable terminal font** so `🤖 ⚡ 🧠 █░` render. **No Nerd Font required** — the statusline deliberately uses only standard Unicode (emoji + block-drawing chars), never Nerd-Font private-use glyphs. Windows Terminal - renders them via `Segoe UI Emoji` out of the box. + renders them via `Segoe UI Emoji` out of the box. As a safety net the + installer nevertheless installs a Nerd Font (Hack, ~17 MB) **per-user, no + admin rights** if none is present — see below. ### Install on a new PC @@ -42,10 +44,23 @@ The installer is **idempotent** — run it again anytime. It auto-detects the existing `settings.json` **without** overwriting your other settings (a `.bak` backup is written first). +### Nerd Font (automatic, no admin) + +If no Nerd Font is detected, the installer downloads one from +[ryanoasis/nerd-fonts](https://github.com/ryanoasis/nerd-fonts) and installs it +into the **per-user** font store (`%LOCALAPPDATA%\Microsoft\Windows\Fonts` + +`HKCU`), so **no administrator rights** are needed. If a download fails (e.g. no +internet), the step is skipped and the statusline still works via the emoji +fallback. To actually *use* the font, set it as your terminal font and restart +the terminal — the statusline renders correctly either way. + ### Options - `.\install.ps1 -Force` — also overwrite an existing `~/.config/cship.toml` (by default an existing config is kept, so local tweaks survive). +- `.\install.ps1 -SkipFont` — skip the Nerd-Font check/install entirely. +- `.\install.ps1 -FontName FiraCode` — install a different Nerd Font + (default `Hack`; e.g. `FiraCode`, `CascadiaCode`, `JetBrainsMono`). ### Updating the kit @@ -81,7 +96,9 @@ einem einzigen Befehl auf jeden Windows-PC installiert. - Ein **emoji-fähiger Terminal-Font**, damit `🤖 ⚡ 🧠 █░` dargestellt werden. **Kein Nerd-Font nötig** — die Statusline nutzt bewusst nur Standard-Unicode (Emoji + Block-Zeichen), keine Nerd-Font-Privatzeichen. Windows Terminal - rendert sie standardmäßig über `Segoe UI Emoji`. + rendert sie standardmäßig über `Segoe UI Emoji`. Als Sicherheitsnetz + installiert der Installer dennoch einen Nerd-Font (Hack, ~17 MB) **per-user, + ohne Admin-Rechte**, falls keiner vorhanden ist — siehe unten. ### Installation auf einem neuen PC @@ -98,11 +115,25 @@ Der Installer ist **idempotent** — jederzeit erneut ausführbar. Er erkennt de eine vorhandene `settings.json` ein, **ohne** deine anderen Einstellungen zu überschreiben (vorher wird ein `.bak`-Backup geschrieben). +### Nerd-Font (automatisch, ohne Admin) + +Wird kein Nerd-Font erkannt, lädt der Installer einen von +[ryanoasis/nerd-fonts](https://github.com/ryanoasis/nerd-fonts) und installiert +ihn in den **benutzereigenen** Font-Speicher (`%LOCALAPPDATA%\Microsoft\Windows\Fonts` ++ `HKCU`) — **ohne Administrator-Rechte**. Schlägt der Download fehl (z. B. kein +Internet), wird der Schritt übersprungen und die Statusline funktioniert dank +Emoji-Fallback trotzdem. Um den Font tatsächlich zu *nutzen*, ihn als +Terminal-Font einstellen und das Terminal neu starten — die Statusline wird so +oder so korrekt dargestellt. + ### Optionen - `.\install.ps1 -Force` — überschreibt zusätzlich eine vorhandene `~/.config/cship.toml` (standardmäßig wird eine bestehende Config behalten, damit lokale Anpassungen erhalten bleiben). +- `.\install.ps1 -SkipFont` — überspringt die Nerd-Font-Prüfung/-Installation. +- `.\install.ps1 -FontName FiraCode` — installiert einen anderen Nerd-Font + (Standard `Hack`; z. B. `FiraCode`, `CascadiaCode`, `JetBrainsMono`). ### Kit aktualisieren diff --git a/install.ps1 b/install.ps1 index 41d3638..91549d9 100644 --- a/install.ps1 +++ b/install.ps1 @@ -9,7 +9,15 @@ [CmdletBinding()] param( # Overwrite an existing cship.toml even if one is already present. - [switch]$Force + [switch]$Force, + + # Skip the Nerd-Font check/install entirely. + [switch]$SkipFont, + + # Nerd Font to install if none is present (matches a ryanoasis/nerd-fonts + # release asset name). Default 'Hack' is small (~17 MB); others: FiraCode, + # CascadiaCode, JetBrainsMono (large, ~123 MB), SpaceMono, Meslo. + [string]$FontName = 'Hack' ) $ErrorActionPreference = 'Stop' @@ -19,6 +27,78 @@ function Write-Step { param($m) Write-Host " $m" -ForegroundColor Cyan } function Write-Ok { param($m) Write-Host " OK $m" -ForegroundColor Green } function Write-Warn { param($m) Write-Host " !! $m" -ForegroundColor Yellow } +# Install a Nerd Font for the current user only (no admin rights): drop the .ttf +# files into the per-user font store and register them under HKCU. Available to +# apps launched after a terminal restart. Any failure is non-fatal — the +# statusline renders via the OS emoji fallback regardless. +function Install-NerdFont { + param([string]$FontName) + + $installed = @() + foreach ($k in @( + 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts', + 'HKCU:\Software\Microsoft\Windows NT\CurrentVersion\Fonts')) { + if (Test-Path $k) { $installed += (Get-Item $k).Property } + } + # Match both "Hack Nerd Font" (double-click install) and "HackNerdFont-..." + # (the per-user name we register from the file stem). + if ($installed | Where-Object { $_ -match 'Nerd\s*Font' }) { + Write-Ok "A Nerd Font is already installed - skipping font download." + return + } + + $family = "$FontName Nerd Font" + Write-Step "No Nerd Font found. Installing '$family' for the current user (no admin needed)..." + try { + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + } catch { } + + $url = "https://github.com/ryanoasis/nerd-fonts/releases/latest/download/$FontName.zip" + $tmp = Join-Path ([IO.Path]::GetTempPath()) ('nf_' + [guid]::NewGuid().ToString('N')) + New-Item -ItemType Directory -Force -Path $tmp | Out-Null + $zip = Join-Path $tmp 'font.zip' + + try { + Invoke-WebRequest -Uri $url -OutFile $zip -UseBasicParsing + Expand-Archive -Path $zip -DestinationPath $tmp -Force + } catch { + Write-Warn "Font download/extract failed: $($_.Exception.Message)" + Write-Warn "Skipping font install - the statusline still works via emoji fallback." + Remove-Item $tmp -Recurse -Force -ErrorAction SilentlyContinue + return + } + + # Prefer the base variant (single-width spacing files carry 'Mono'/'Propo'). + $ttfs = Get-ChildItem $tmp -Recurse -Filter '*.ttf' | Where-Object { $_.Name -match 'NerdFont-' } + if (-not $ttfs) { $ttfs = Get-ChildItem $tmp -Recurse -Filter '*.ttf' } + + $fontDir = Join-Path $env:LOCALAPPDATA 'Microsoft\Windows\Fonts' + New-Item -ItemType Directory -Force -Path $fontDir | Out-Null + $regKey = 'HKCU:\Software\Microsoft\Windows NT\CurrentVersion\Fonts' + if (-not (Test-Path $regKey)) { New-Item -Path $regKey -Force | Out-Null } + + $count = 0 + foreach ($ttf in $ttfs) { + $dest = Join-Path $fontDir $ttf.Name + try { + Copy-Item $ttf.FullName $dest -Force -ErrorAction Stop + } catch { + # A file already in use (loaded by a running app) is fine - it exists. + if (-not (Test-Path $dest)) { throw } + } + New-ItemProperty -Path $regKey -Name "$($ttf.BaseName) (TrueType)" -Value $dest -PropertyType String -Force | Out-Null + $count++ + } + Remove-Item $tmp -Recurse -Force -ErrorAction SilentlyContinue + + if ($count -gt 0) { + Write-Ok "Installed $count '$family' file(s) into the per-user font store." + Write-Warn "To use them, set your terminal font to '$family' and restart the terminal." + } else { + Write-Warn "Archive contained no .ttf files - font skipped." + } +} + Write-Host "`nClaude Code statusline installer" -ForegroundColor White # --- Target locations ------------------------------------------------------- @@ -82,9 +162,17 @@ if ($settings.PSObject.Properties.Name -contains 'statusLine') { $settings | ConvertTo-Json -Depth 20 | Set-Content $settingsPath -Encoding UTF8 Write-Ok "settings.json statusLine wired to detected pwsh" +# --- 6. Nerd Font (safety net, per-user) ------------------------------------ +if ($SkipFont) { + Write-Warn "Font step skipped (-SkipFont)." +} else { + Install-NerdFont -FontName $FontName +} + # --- Post-install notes ----------------------------------------------------- Write-Host "`nDone." -ForegroundColor Green Write-Host "Notes:" -ForegroundColor White -Write-Host " -> Icons use standard Unicode (emoji + block chars), so no Nerd Font is needed." -Write-Host " Any emoji-capable terminal font works (Windows Terminal renders them via Segoe UI Emoji by default)." +Write-Host " -> The statusline itself needs no special font - its icons are standard Unicode" +Write-Host " (emoji + block chars) that render via the OS emoji fallback. The Nerd Font above" +Write-Host " is an optional safety net for the widest glyph coverage." Write-Host " Restart Claude Code (or start a new session) to see the statusline.`n"