>_POWERLINERS — RUST POWERLINE
A Rust port of Python's powerline-status — the canonical statusline / prompt renderer used by tmux, zsh, bash, vim, ipython. The Python implementation pays a ~100 ms interpreter-startup tax on every render; powerliners is a single static binary with sub-millisecond startup.
Why a port?
- Python interpreter startup adds ~100 ms per render — on every keystroke that triggers a zsh `precmd` redraw, every tmux statusline tick, every shell continuation line.
- At zpwr scale (256-thread workstation, 100+ zsh-in-tmux sessions), powerline-status compounds into seconds of cumulative latency per minute.
- Rust gives a static binary, microsecond startup, zero runtime deps, cross-arch builds, and predictable memory.
- The upstream theme grammar (JSON files in
powerline/config/) is preserved verbatim — users keep their themes unchanged.
Targets
tmux
statusline + window-list + continuation lines
zsh
PS1 / RPROMPT segments, precmd integration
bash
PS1 / PROMPT_COMMAND segments
vim
statusline + tabline (without `+python3` dependency)
ipython
REPL prompt (via shell hook, not embedded)
Port discipline
Every fn name landed in src/ported/ must exist in upstream powerline Python source. Enforced by tests/ported_fn_names_match_py.rs against docs/powerline_py_functions.txt (currently 624 names extracted from github.com/powerline/powerline). Maintainer-approved Rust-only exemptions live in tests/data/fake_fn_allowlist.txt.
# regenerate the upstream allowlist ./scripts/regen_py_functions.sh # audit will fail with a list of any invented fn names cargo test --test ported_fn_names_match_py
This pattern mirrors the zshrs ported_fn_names_match_c.rs enforcement — the audit test is load-bearing and must not be weakened.
Status
Port is structurally complete at the function level. 134 of 137 upstream Python files are at DONE tier per the citation-density classifier (scripts/gen_port_checklist.py); the 3 remaining NEAR files are Python lipstick — zero-fn class shells (renderers/shell/readline.py and renderers/shell/zsh.py each hold two escape-marker string constants and a class alias; bindings/i3/powerline-i3.py's single render fn is already 100% ported per docs/port_report.html). No behavioral surface left to port — the classifier's py_fn_total == 0 denominator just can't promote 0/0 → DONE. The Rust port spans ~66,043 LOC across src/ported/ with 2,473 lib tests + 2 audit tests + 462 parity tests passing. Parity tests pipe identical inputs through the upstream Python interpreter (via the vendored powerline-status) and the Rust port, asserting byte/value-identical results — 11 real port bugs were surfaced by this harness and fixed in-tree (ThreadedSegment.daemon, Spec.did_type, encoding locale lookup, _clear_special_values UB, Spec.ident regex, Spec.context_message recursion, Spec.tuple bounds, Spec.unknown_msg/_spec dispatch, NON_PRINTABLE_RE inversion, Spec.printable/unsigned). Every fn name under src/ported/ traces to one of 624 upstream Python fn names (or to tests/data/fake_fn_allowlist.txt for maintainer-approved exemptions); every ported body carries // py:NNN line-citations against upstream powerline/. End-to-end statusline rendering against a real .json theme tree runs through the Powerline orchestrator chain (Powerline class + Theme + Renderer base + segment dispatcher) in both powerline-render and powerline-daemon via the shared render_runtime::render_once entry — byte-identical output with upstream python3 powerline-render is asserted across 45 daemon-parity fixtures (tests/daemon_parity.rs) on top of the 462 unit-parity tests. Watch tags for milestone bumps.
Install
# source + build
git clone https://github.com/MenkeTechnologies/powerliners.git
cd powerliners
cargo build
ls target/debug/powerliners target/debug/powerline-{render,lint,config,daemon}
Five binaries land in target/debug/: powerliners (demo CLI for ported leaf utilities), powerline-render, powerline-lint, powerline-config, and powerline-daemon. The daemon lifecycle (UNIX socket bind, daemonize, pidfile lock, accept loop, EOF shutdown) is fully functional and the render path is wired end-to-end through render_runtime::render_once — same code path as powerline-render, with per-ext Configs caching and is_stale() mtime-reload (py:851-866). powerline-render / powerline-config shim through the ported scripts/ module mains and drive real theme JSON tree resolution; powerline-lint still has the markedjson loader + Spec checks behind the argparse, with the full orchestrator-integrated lint pipeline as the remaining substrate.
CLI reference
The suite ships five binaries. powerline is the hot-path client; the rest are management / fallback tools. All are 1:1 ports of the upstream scripts/ and client/ entry points.
| binary | role |
|---|---|
powerline | Native Rust client (port of client/powerline.c). Packs argv + cwd + env into the wire request, sends it to the daemon over a Unix socket, copies the reply to stdout. On connect-fail it execvps powerline-render as a daemon-less fallback. |
powerline-daemon | Long-lived render server. Binds the per-UID socket, daemonizes, holds a pidfile lock, runs the accept loop, and renders each request through render_runtime::render_once against the cached theme tree. |
powerline-render | One-shot direct render through the same orchestrator path as the daemon. Used by shell hooks when no daemon is running and as the client's fallback exec. |
powerline-config | tmux / shell / vim integration helper — emits setup, deduces the $POWERLINE_COMMAND binding, extracts the bundled vim plugin. |
powerline-lint | Config linter — markedjson loader + Spec checks behind an argparse front end. |
powerline <EXT> — extensions
The first positional after the optional --socket PATH selects the extension to render. Stock extensions (from man powerline):
| EXT | renders |
|---|---|
tmux | tmux statusline segments |
shell | shell prompt — next positional is left / right / continuation |
vim | vim statusline / tabline |
ipython | IPython prompt / continuation |
wm | i3 / awesome / qtile widget binding |
render-request flags
Everything after <EXT> is forwarded to the daemon and parsed by parse_client_argv (src/ported/scripts/powerline_daemon.rs). Each long flag has the upstream short alias:
| flag | meaning |
|---|---|
-w / --width | statusline width hint (columns) |
-r / --renderer-module | override the renderer module |
-c / --config-override | per-request config override (repeatable) |
-t / --theme-override | per-request theme override (repeatable) |
-R / --renderer-arg | renderer arg key=value (repeatable) |
-p / --config-path | extra config-path root (repeatable) |
-m / --mode | current mode — shorthand for -R mode=VALUE, drives mode_translations |
--last-exit-code | last command exit code (int_or_sig) — for the last_status segment |
--last-pipe-status | space-separated pipe exit codes — for last_pipe_status |
--jobnum | background-job count — for the jobnum segment |
--socket | socket path (must precede <EXT> on the client) |
powerline-daemon — process flags
| flag | effect |
|---|---|
-q / --quiet | double-fork + detach silently (the canonical login-time start) |
-f / --foreground | stay in the foreground (don't daemonize) |
-s / --socket PATH | bind a custom socket path instead of the per-UID default |
-k / --kill | kill the running daemon for this UID |
-r / --replace | replace an already-running daemon |
powerline-config — subcommands
| invocation | does |
|---|---|
powerline-config tmux setup | emit the tmux integration (sources the version-matched powerline-base.conf variant from the per-user cache) |
powerline-config shell command | print the deduced $POWERLINE_COMMAND binary, or exit 1 if none |
powerline-config shell uses COMPONENT [-s SHELL] | exit 0/1 depending on whether a component (e.g. tmux, prompt) is enabled for the shell — checks POWERLINE_NO_* env gates |
powerline-config vim source-path | extract the bundled powerline.vim plugin and print its cache path (non-port convenience action) |
Environment variables
| variable | effect |
|---|---|
POWERLINE_COMMAND | The command shell integrations invoke to render. Set to powerline (this binary); existing setups that hardcode the upstream Python shim keep working because the wire format is identical. |
POWERLINE_CONFIG_PATHS | Colon-separated config-root search list (config.json + themes / colorschemes / segments JSON). Mirrors upstream search order. |
POWERLINE_CONFIG_OVERRIDES | Theme-override key=value pairs applied per extension. |
POWERLINERS_ICONS | Icon tier: unset/default → Nerd Font glyphs, =unicode → text-presentation Unicode fallback, =ascii → label-style ASCII. Read once per process (see src/extensions/icons.rs). |
$XDG_CACHE_HOME | Root for the extracted tmux conf (powerliners/tmux/) and vim plugin (powerliners/vim/); default ~/.cache. |
$FUSEVM_JIT_CACHE · ${ZSHRS,STRYKE,AWKRS}_RKYV_CACHE / _HOME · $XDG_DATA_HOME | Override roots for the four cache-size segments — see the resolution chain in Bundled segments below. |
$KUBECONFIG · $AWS_PROFILE / $AWS_REGION / $AWS_DEFAULT_REGION · $CLOUDSDK_* | Honored by the kubecontext, aws.context, and gcp.context segments respectively. |
Per-UID socket path: /tmp/powerline-ipc-$UID on macOS / BSD, abstract \0powerline-ipc-$UID on Linux (no filesystem path — invisible to ls / fuser). Pidfile lock at /tmp/powerline-ipc-$UID.pid.
Shell & tmux integration
The wire format is byte-compatible with upstream's C client, so existing .tmux.conf / .zshrc invocations work unchanged once $POWERLINE_COMMAND resolves to this powerline binary.
tmux (~/.tmux.conf)
set -g status-interval 2 set -g status-left "#(powerline tmux left)" set -g status-right "#(powerline tmux right)" # or the install-method-agnostic helper: run-shell "powerline-config tmux setup" run-shell -b "powerline-daemon -q &>/dev/null || exit 0"
zsh (~/.zshrc)
powerline-daemon -q # start once at login
function powerline_precmd() {
PS1="$(powerline shell left --last-exit-code=$? --jobnum=$(jobs -p | wc -l))"
}
precmd_functions+=(powerline_precmd)
verify against a config root
Point the daemon at a copy of your real config and confirm the rendered tmux markup matches upstream before swapping anything:
# spawn on a throwaway socket POWERLINE_CONFIG_PATHS=~/.config/powerline \ ./target/release/powerline-daemon --foreground --socket /tmp/powerliners-probe & # render the right side via the wire protocol powerline --socket /tmp/powerliners-probe tmux right # compare against upstream Python (powerline-status installed) powerline-render tmux right -p ~/.config/powerline
Custom segments
Upstream Python lets you drop a .py file in ~/.config/powerline/segments/ and reference it by dotted path; __import__ makes it callable. The Rust binary has no dynamic import, so the dispatch layer in src/extensions/exec_segment.rs provides two equivalent surfaces that fall through to a subprocess.
Pattern A — explicit exec adapter
Reference the built-in exec adapter directly in theme JSON when you want the script path, args, and format string spelled out inline:
{
"function": "exec",
"args": {
"command": "/usr/local/bin/cpu_temp.sh",
"args": ["--unit", "C"],
"format": "%s°C",
"highlight_groups": ["cpu_load"]
}
}
Pattern B — dotted-path filesystem dispatch
Reference your segment by dotted path and drop the script under <config_path>/segments/. The daemon resolves myseg.cpu_temp → <config_path>/segments/myseg/cpu_temp.{sh,py,rb,pl,lua,js,executable} (first hit wins; order in src/extensions/exec_segment.rs::SCRIPT_EXTENSIONS):
{ "function": "myseg.cpu_temp" }
mkdir -p ~/.config/powerline/segments/myseg cat > ~/.config/powerline/segments/myseg/cpu_temp.sh <<'EOF' #!/bin/sh echo "$(osx-cpu-temp | sed 's/°C//')" EOF chmod +x ~/.config/powerline/segments/myseg/cpu_temp.sh
Dotted-path resolution honors the full config-path cascade (POWERLINE_CONFIG_PATHS, --config-path, ~/.config/).
Output protocol
Both patterns parse the script's stdout on the first non-whitespace byte:
| stdout starts with | treated as | wrapped how |
|---|---|---|
[ (valid JSON array) | verbatim segment list | used directly — full control over highlight_groups, gradient_level, divider_highlight_group, multi-chunk output |
| anything else | plain text | [{"contents": <trimmed-stdout>, "highlight_groups": [...]}] — format template applies (%s → contents, %% → literal %) |
Highlight-group caveat (Rust-port divergence)
The current Rust gen_segment_getter (src/ported/segment.rs) uses the segment's function_name as the only highlight group for "type": "function" segments — it ignores the theme's "highlight_groups" override (upstream Python respects it). Practical fix: name your colorscheme group to match the function name ("exec" for Pattern A, "cpu_temp" for Pattern B), or emit explicit highlight_groups inline via a JSON-array stdout.
Migration & rollback
Drop-in replacement for the Python powerline-daemon — same wire format, same EOF\0\0 shutdown sentinel, same socket path. The C client shipped with powerline-status talks to this daemon unchanged.
| step | command |
|---|---|
| 1. stop the Python daemon | powerline-daemon -k |
2. put the Rust binary ahead in $PATH | ln -sf "$(pwd)/target/release/powerline-daemon" /usr/local/bin/powerline-daemon |
| 3. restart tmux | tmux kill-server && tmux new-session |
| 4. confirm resolution | ls -la $(which powerline-daemon) — must report the symlink |
| rollback | powerline-daemon -k; rm /usr/local/bin/powerline-daemon; powerline-daemon -q |
The Python C client at ~/.local/bin/powerline does not need replacing — it speaks the same wire protocol to whichever daemon owns the socket. No config, .tmux.conf, or shell-rc edits.
Vim setup
Vim statusline + tabline rendering ships in the same powerline binary — no +python3 requirement, no pip install powerline-status. The bundled powerline.vim plugin is embedded via include_str! and extracted to ~/.cache/powerliners/vim/powerline.vim on first source. Works on vim 7.4+, vim 9, and neovim (vim8-compatible syntax only — no vim9script lock-in).
Step 1 — source the plugin from .vimrc
One-line install — extracts and sources the plugin on every launch:
if executable('powerline-config')
execute 'source' trim(system('powerline-config vim source-path'))
endif
powerline-config vim source-path extracts the bundled plugin and prints its path; the :execute 'source' … then sources it. Caches in $XDG_CACHE_HOME/powerliners/vim/ (default ~/.cache/powerliners/vim/) so subsequent launches re-source from disk.
Or the manual two-step (pin the path, skip the per-launch fork):
# one-time extraction $ powerline-config vim source-path /Users/<you>/.cache/powerliners/vim/powerline.vim " .vimrc set runtimepath+=~/.cache/powerliners/vim source ~/.cache/powerliners/vim/powerline.vim
Step 2 — pre-start the daemon
The plugin shells out via system('powerline vim left …') on every statusline refresh. With the daemon running that's a microsecond Unix-socket round-trip; without it every refresh fork-execs powerline-render (~30 ms cold per call). Start the daemon once at shell login (.zshrc / .bash_profile):
powerline-daemon -q
The -q flag double-forks and detaches; vim sees the socket immediately. No per-vim daemon — one process per UID handles tmux, shell prompts, and every running vim simultaneously.
Step 3 — verify
Open a fresh vim session and check:
:echo &statusline " expected: a %#Pl_…# markup string ending in segment chunks :PowerlinersRefresh " manually re-runs the refresh; useful for debugging :messages " no errors; if 'powerline: write() to daemon failed' shows up, " the daemon isn't reachable — re-run `powerline-daemon -q`
The plugin sets laststatus=2 automatically so the statusline shows even in single-window sessions.
What the plugin wires up
Triggered autocmds (augroup powerliners):
| event | fires when |
|---|---|
VimEnter | initial render on launch |
WinEnter / BufWinEnter / BufEnter / TabEnter | refresh on window / buffer / tab context switch |
ModeChanged (vim ≥ 8.2.2871) | mode transitions (normal → insert …) |
CursorMoved / CursorMovedI (legacy fallback) | every cursor move on ancient vim — upgrade if possible |
Per-request keys sent to the renderer (matches upstream's powerline.bindings.vim so theme JSON written for Python upstream renders identically): mode (mode(1)), bufnr (bufnr('%')), winnr (winnr()), buf_name (expand('%:p'), omitted when empty).
Override the binary name
If you've installed under a non-default name or want to test a build from target/release/, set the global before sourcing:
let g:powerliners_binary = expand('~/code/powerliners/target/release/powerline')
if executable(g:powerliners_binary)
execute 'source' trim(system(g:powerliners_binary . '-config vim source-path'))
endif
Customize the theme
The vim statusline pulls from ~/.config/powerline/themes/vim/default.json plus ~/.config/powerline/colorschemes/vim/default.json. Bundled defaults live under src/ported/config_files/themes/vim/ and src/ported/config_files/colorschemes/vim/; copy any of them into ~/.config/powerline/ and edit. Same JSON shape as upstream — segments listed under segments.left / segments.right, inheritance via extends, per-mode highlight overrides via mode_translations.
Troubleshooting
| symptom | cause / fix |
|---|---|
| empty statusline | set laststatus=2 got stomped by something else in .vimrc; re-set after sourcing the plugin |
| garbled escape codes | terminal doesn't support truecolor; renderer falls back to cterm but vim must run in a 256-color tty ($TERM=xterm-256color) |
| colors don't match terminal | colorscheme JSON missing your custom palette — copy colorschemes/vim/default.json into ~/.config/powerline/colorschemes/vim/ |
powerline: write() to daemon failed | daemon isn't running; powerline-daemon -q |
| refresh stutters / flickers | the legacy CursorMoved fallback is firing on every keystroke (vim < 8.2.2871); upgrade vim |
Bundled segments
src/extensions/ ships net-new segments above upstream powerline-status, dispatched as standard built-ins via the daemon's ADAPTERS table. All accept a format theme arg and ship NF-default icons with Unicode + ASCII fallbacks (controlled by POWERLINERS_ICONS).
| segment | summary |
|---|---|
powerliners.gpu.gpu_usage_percent | Vendor-dispatched GPU compute % (nvidia-smi → rocm-smi → intel_gpu_top → ioreg fallback). |
powerliners.gpu.gpu_vram | GPU VRAM USED/TOTAL via the same dispatch chain. |
powerliners.disk.disk_usage | Filesystem USED/TOTAL for any mount. |
powerliners.disk.disk_usage_percent | Filesystem percent-used. |
powerliners.disk.disk_io | Live read/write throughput for any device (device arg, default auto). Format uses positional %s substitution — first %s → read rate, second %s → write rate (default <disk> <io> R %s W %s). Args: short (default true), recv_max/sent_max (gradient ceilings, default 100 MiB/s). |
powerliners.thermal.thermal | CPU/GPU temp + fan RPM. /sys/class/hwmon on Linux, powermetrics/istats on macOS. |
powerliners.vcs.git_status | Single-probe git status — branch, ahead/behind, dirty/staged/untracked, stash count, action (rebase/merge/cherry-pick), tag at HEAD. github-remote detection prepends the octocat glyph. |
powerliners.docker.containers | Container counts via docker ps. Tokens: {running} / {total} / {images} / {stopped}. Falls through silently when the daemon is unreachable. |
powerliners.k8s.kubecontext | Current kubectl context + active namespace. Honors $KUBECONFIG cascade; hide_default arg suppresses the namespace fragment when it equals the configured default. |
powerliners.proc.process_count | POSIX process tally via ps -eo stat=. Tokens: {total}/{running}/{sleeping}/{zombie}/{dwait}/{stopped}. warn_zombie flips the highlight group when defunct processes are present. |
powerliners.github.ci_status | Current branch's HEAD check-runs via gh api repos/:o/:r/commits/:sha/check-runs, disk-cached by SHA (default ttl_secs=30). Tokens: {icon}/{state}/{passed}/{failed}/{running}/{total}. State-picked highlight group plus information:regular neutral fallback so the chunk renders in any colorscheme. |
powerliners.aws.context | Active AWS profile + region. Pure-fs probe of $AWS_PROFILE/$AWS_REGION/$AWS_DEFAULT_REGION then ~/.aws/config (handles the [profile NAME] quirk). Tokens: {icon}/{profile}/{region}. hide_default_profile strips the profile fragment when it equals default. |
powerliners.gcp.context | Active gcloud configuration's project + account. Pure-fs probe of ~/.config/gcloud/active_config + configurations/config_<NAME>. Env overrides $CLOUDSDK_ACTIVE_CONFIG_NAME / $CLOUDSDK_CORE_PROJECT / $CLOUDSDK_CORE_ACCOUNT win. |
powerliners.fusevm.jit_cache | fusevm Cranelift JIT cache stats — recursively walks $FUSEVM_JIT_CACHE / $XDG_CACHE_HOME/fusevm-jit / ~/.cache/fusevm-jit. Tokens: {icon}/{count}/{entries}/{harddisk}/{size} (du -sh-style block allocation)/{bytes} (raw disk bytes)/{logical_size}/{logical_bytes} (stat-style content sum). |
powerliners.zshrs.rkyv_cache | Single-file stat of the zshrs authoritative rkyv archive at $ZSHRS_RKYV_CACHE / $ZSHRS_HOME/scripts.rkyv / $XDG_DATA_HOME/zshrs/scripts.rkyv / ~/.zshrs/scripts.rkyv. Same {size} / {bytes} / {logical_*} token surface as fusevm.jit_cache. |
powerliners.stryke.rkyv_cache | Single-file stat of ~/.stryke/scripts.rkyv (stryke's bytecode store, Cranelift-JIT'd via the shared fusevm runtime). |
powerliners.awkrs.rkyv_cache | Single-file stat of ~/.awkrs/scripts.rkyv. |
powerliners.zshrs.version | Latest installed zshrs version (parsed from <bin> --version). In-process TTL cache (default 300 s, configurable via ttl_secs) so the daemon doesn't fork on every prompt tick. Tokens: {icon}/{version}. Returns None when the binary isn't on PATH. |
powerliners.stryke.version | Same as zshrs.version for the stryke binary — handles the This is stryke vX.Y.Z — ... prefix shape via the shared extract_version SemVer scanner. |
powerliners.awkrs.version | Same shared helper, default binary awkrs. |
powerliners.exec.exec | User-defined exec adapter — spawn an arbitrary script, parse stdout, render with a user-supplied format. Resolves via bare "function": "exec" as well as the dotted path. |
All segments live under src/extensions/<module>.rs and register through the daemon's ADAPTERS table in src/bin/shared/render_runtime.rs — see the audit section below for the live icon → segment cross-reference.
Cache-size segments — shared resolution chain & token surface
The four cache-size segments — powerliners.fusevm.jit_cache, powerliners.zshrs.rkyv_cache, powerliners.stryke.rkyv_cache, powerliners.awkrs.rkyv_cache — share a deliberately uniform contract so the same theme JSON works across all four. Each segment is a pure filesystem probe; no subprocess, no daemon RPC.
Resolution chain (4-level fallback, first hit wins). For <NAME> ∈ {ZSHRS, STRYKE, AWKRS} the rkyv segments resolve path as:
$<NAME>_RKYV_CACHE— explicit override, used verbatim$<NAME>_HOME/scripts.rkyv$XDG_DATA_HOME/<name>/scripts.rkyv— used only when the file exists; otherwise falls through~/.<name>/scripts.rkyv— final fallback
The fusevm.jit_cache sibling uses the directly-analogous 3-level chain ($FUSEVM_JIT_CACHE → $XDG_CACHE_HOME/fusevm-jit → ~/.cache/fusevm-jit) but recursively walks a cache directory rather than stat-ing a single archive, and also exposes an {entries} token for the recursive file count. Symlinks count as a single entry and are never followed — avoids infinite loops across re-symlinked cache dirs. The precedence chains are unit-tested via a pure-functional default_path_with(get_env, path_exists) seam in src/extensions/{zshrs,stryke,awkrs}_rkyv.rs and a matching default_root_with(get_env) seam in src/extensions/fusevm_jit.rs — every precedence level pinned without mutating the process env.
Format tokens (identical across all four segments):
| token | meaning |
|---|---|
{icon} | Segment glyph — substituted downstream by the render runtime, not by the segment module itself. The Nerd Font / Unicode / ASCII tier is selected by POWERLINERS_ICONS. |
{size} | On-disk allocation, human-formatted (B/K/M/G/T/P, 1024-base, precision tiers <10→2dp / <100→1dp / else→0dp). Matches du -sh. |
{bytes} | On-disk allocation as a raw integer (block count × 512 on Unix; stat .st_blocks). |
{logical_size} | File content size from stat .st_size, human-formatted. |
{logical_bytes} | File content size as a raw integer. |
Missing-archive behavior. When the target file doesn't exist (the runtime never ran on this machine, or the archive hasn't been written yet), the segment returns None by default and renders no chunk. Setting "show_when_empty": true in the segment args makes it render with zeroed stats (0B/0) so the chunk stays present as a UI affordance.
Highlight-group chain. Every cache-size segment ships a 3-level highlight chain — <segment>_rkyv_cache → <segment> → information:regular. The trailing information:regular is a neutral fallback so the chunk renders in any colorscheme even when the segment-specific groups are absent (see fusevm_jit::jit_cache() for the rationale that motivated this convention).
Glyph rendering
Every glyph below is a real Nerd Font codepoint loaded into this page via the @font-face block in docs/hud-static.css. The font is the official SymbolsNerdFont subset (~270 KB) served from jsdelivr CDN, with a unicode-range directive gating the load to PUA ranges only (U+E000-F8FF + U+F0000-FFFFD):
@font-face {
font-family: 'SymbolsNerdFont';
font-display: swap;
src: url('https://cdn.jsdelivr.net/gh/ryanoasis/nerd-fonts@master/patched-fonts/NerdFontsSymbolsOnly/SymbolsNerdFont-Regular.ttf') format('truetype');
unicode-range: U+E000-F8FF, U+F0000-FFFFD;
}
Body text and code blocks continue to render in Share Tech Mono / Orbitron — only PUA codepoints resolve through the NF subset, so the font swap has no layout impact. The same chain operates at runtime via POWERLINERS_ICONS: unset/default → NF glyph, =unicode → text-presentation Unicode fallback (e.g. ✓ ✗ 🐳 ⎈ ⛁), =ascii → label-style ASCII (e.g. OK FAIL D: k8s: D:).
Icon audit
Every Nerd Font codepoint shipped by src/extensions/icons.rs is listed here with its canonical upstream identity (verified against ryanoasis/nerd-fonts master, NF 3.4.0 default load order, legacy i_material.sh excluded). Each glyph below renders live via the SymbolsNerdFont @font-face declaration in hud-static.css — if a shipped glyph fails to match its claimed identity, it would be immediately visible in this table.
Three-tier fallback chain: Nerd Font (default) → Unicode (POWERLINERS_ICONS=unicode, text-presentation only) → ASCII (POWERLINERS_ICONS=ascii, label-style for non-UTF8 terminals).
A. Icon getters (33)
| fn | codepoint | NF name | source | NF | Unicode | ASCII | used by segment |
|---|---|---|---|---|---|---|---|
icons::disk() | U+F0A0 | i_fa_hard_drive | i_fa.sh | | ⛁ | D: | disk_io, disk_usage, disk_usage_percent |
icons::cpu() | U+F4BC | i_oct_cpu | i_oct.sh | | ⚙ | CPU: | ad_cpu_load_percent, thermal |
icons::gpu() | U+F2DB | i_fa_microchip | i_fa.sh | | ⎚ | GPU: | gpu_usage_percent, gpu_vram, thermal |
icons::memory() | U+EFC5 | i_fa_memory | i_fa.sh | | ▤ | MEM: | ad_mem_usage_percent, gpu_vram, mem_usage |
icons::thermometer() | U+F2C8 | i_fa_temperature_three_quarters | i_fa.sh | | 🌡 | T: | — unused |
icons::branch() | U+E0A0 | i_pl_branch | i_ple.sh | | ⎇ | b: | git_status |
icons::github() | U+F09B | i_fa_github | i_fa.sh | | 🐙 | gh: | ci_status, git_status |
icons::tag() | U+F02B | i_fa_tag | i_fa.sh | | ⚑ | tag: | git_status |
icons::docker() | U+F0868 | i_md_docker | i_md.sh | | 🐳 | D: | containers |
icons::kubernetes() | U+F10FE | i_md_kubernetes | i_md.sh | | ⎈ | k8s: | kubecontext |
icons::process() | U+F0493 | i_md_cog | i_md.sh | | ⚙ | P: | process_count |
icons::aws() | U+F0E0F | i_md_aws | i_md.sh | | aws | aws | context |
icons::gcp() | U+F11F6 | i_md_google_cloud | i_md.sh | | gcp | gcp | context |
icons::ci_ok() | U+F49E | i_oct_check_circle | i_oct.sh | | ✅ | OK | ci_status |
icons::ci_fail() | U+F52F | i_oct_x_circle | i_oct.sh | | ❌ | FAIL | ci_status |
icons::ci_run() | U+F46A | i_oct_sync | i_oct.sh | | 🔄 | RUN | ci_status |
icons::count() | U+F0222 | i_md_file_multiple | i_md.sh | | ▤ | n= | jit_cache |
icons::harddisk() | U+F02CA | i_md_harddisk | i_md.sh | | ⛁ | d= | jit_cache |
icons::swap() | U+F04E1 | i_md_swap_horizontal | i_md.sh | | ⇄ | SWAP: | ad_mem_swap_percentage, mem_swap |
icons::io() | U+F0E79 | i_md_arrow_up_down | i_md.sh | | ⇅ | IO: | disk_io |
icons::fusevm() | U+F01FA | i_md_engine | i_md.sh | | ⚙ | jit: | jit_cache |
icons::zshrs() | U+F07B7 | i_md_console_line | i_md.sh | | ❯ | zshrs: | rkyv_cache |
icons::stryke() | U+E769 | i_dev_perl | i_dev.sh | | λ | stryke: | rkyv_cache |
icons::awkrs() | U+E741 | i_dev_awk | i_dev.sh | | $0 | awkrs: | rkyv_cache |
icons::weather_sunny() | U+E30D | i_weather_day_sunny | i_weather.sh | ☀ | SUN | weather | |
icons::weather_night() | U+E32B | i_weather_night_clear | i_weather.sh | ☾ | NIGHT | weather | |
icons::weather_rainy() | U+E318 | i_weather_rain | i_weather.sh | ☔ | RAIN | weather | |
icons::weather_cloudy() | U+E312 | i_weather_cloudy | i_weather.sh | ☁ | CLOUDS | weather | |
icons::weather_snowy() | U+E31A | i_weather_snow | i_weather.sh | ❄ | SNOW | weather | |
icons::weather_stormy() | U+E31D | i_weather_thunderstorm | i_weather.sh | ⛈ | STORM | weather | |
icons::weather_foggy() | U+E313 | i_weather_fog | i_weather.sh | 🌫 | FOG | weather | |
icons::weather_windy() | U+E31F | i_weather_strong_wind | i_weather.sh | 🌬 | WIND | weather | |
icons::weather_unknown() | U+E374 | i_weather_na | i_weather.sh | ? | ? | weather |
The nine weather_* getters are keyed by upstream condition name and consumed by the weather adapter (ad_weather in src/bin/shared/render_runtime.rs) — it pre-seeds day/sunny/night/rainy/cloudy/snowy/stormy/foggy/windy/unknown defaults from the active icon tier before applying any theme icons override. icons::thermometer() remains the only getter with no live consumer (— unused above).
B. Segment defaults (24)
Default format strings shipped by each powerliners.* / powerlinemem.* adapter when the theme JSON omits format. Live NF render uses the same glyphs as Table A.
| segment name | icons used | live NF render |
|---|---|---|
powerliners.gpu.gpu_usage_percent | | 47% |
powerliners.gpu.gpu_vram | | 8.2G/12G |
powerliners.disk.disk_usage | | 142G/512G |
powerliners.disk.disk_usage_percent | | 28% |
powerliners.disk.disk_io | | R 23M W 5M |
powerliners.thermal.thermal | | 67°C 1240RPM |
powerliners.vcs.git_status | | main ⇡2 |
powerliners.docker.containers | | 4/7 |
powerliners.k8s.kubecontext | | prod:default |
powerliners.proc.process_count | | 348 |
powerliners.github.ci_status | | 5/5 |
powerliners.aws.context | | prod@us-east-1 |
powerliners.gcp.context | | my-proj:me@e.com |
powerliners.fusevm.jit_cache | | 207 828K |
powerliners.zshrs.rkyv_cache | | 4.00K |
powerliners.stryke.rkyv_cache | | 34.9M |
powerliners.awkrs.rkyv_cache | | 8.00K |
powerliners.zshrs.version | | 0.11.26 |
powerliners.stryke.version | | 0.16.8 |
powerliners.awkrs.version | | 0.4.13 |
powerlinemem.mem_usage.mem_usage | | 12G/32G |
powerlinemem.mem_usage.mem_usage_percent | | 47% |
powerlinemem.mem_usage.mem_swap | | 1.2G/4G |
powerlinemem.mem_usage.mem_swap_percent | | 30% |
Anti-fakery rationale: a future maintainer who swaps a glyph (intentionally or by typo) without updating both the code AND this audit table will produce visibly wrong rendering on this page — the symptom is observable, not hidden behind a Rust unit test. The used-by-segment column on Table A is computed from the real adapter code in src/bin/shared/render_runtime.rs at build time (each fn ad_X body is scanned for icons::Y() calls and cross-referenced against the ADAPTERS dispatch table); a getter listed as — unused is dead code awaiting either a consumer or removal. The i_material.sh legacy MDI set is intentionally excluded from verification since NF 3.4.0 does not load it by default; codepoints that collide between i_oct.sh and legacy i_material.sh (e.g. U+F52F) resolve via the Octicons subset in any standard Nerd Font install.