4.5 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
A Wayland status bar for Sway/i3, written entirely in QML and driven by Quickshell, packaged with a Nix flake.
Commands
# run without building (hot-reloads on file save)
nix develop # shell with quickshell + qmlls/qmlformat on PATH
quickshell --path . # or: qs -p .
nix run . # run straight from the flake
nix build . && ./result/bin/quickshell-bar # build the wrapper, then run
There is no test suite, no linter, and no build step for the QML itself —
it's interpreted and hot-reloaded. The only "build" is the Nix wrapper that
pins fonts and runtime tools. qmlformat/qmlls (from qt6.qtdeclarative,
available in the dev shell) are the only static tooling. Verify changes by
running the bar and looking at it.
Architecture
Entry point. shell.qml → ShellRoot with Variants { model: Quickshell.screens }
spawns one widgets/Bar.qml (PanelWindow) per monitor. Bar.qml is a fixed
three-zone layout: workspaces (left), clock (center), a RowLayout of metric
modules (right). Changing module order/presence is purely editing that RowLayout.
Three import roots, registered via qmldir:
config/—ThemeandIcons, both singletons (pragma Singleton+singletonline inconfig/qmldir). Global palette/geometry/fonts and Nerd Font glyphs. Import withimport "../config", reference asTheme.x/Icons.x.services/—SysStats, a singleton that is the single source of truth for all/proc+/sysmetrics. Import withimport "../services".widgets/— the visual modules.Pill.qmlis the rounded container every module reuses (default property alias contentlets children sit inside a centered RowLayout);MetricPill.qmladds the icon + value convention.
The metrics pipeline (services/SysStats.qml) is the core design. One Timer
(1 Hz, interval property) calls _tickOnce(), which reload()s a set of
FileViews and parses them into reactive properties (cpu, cpuFreq, mem,
temp, rates, battery, …). Widgets just bind to those properties. Key conventions:
FileView { blockLoading: true }makesreload()synchronous sotext()is fresh on the same tick. Use this for tiny virtual files in/proc//sys.- Add a metric by: declaring a property, adding a
FileView(or reusing one by reassigning itspath, as_parseNetdoes for the active interface), writing a_parseX(), and callingreload()+_parseX()inside_tickOnce(). - Deltas (CPU%, net rates) keep a
_prev*field and diff against the last tick. - Two metrics escape the pure-virtual-file rule: disk usage runs
dfvia aProcessonly every 30 ticks (_tick % 30), and a one-shotProcessrunsservices/discover.shat startup. The bar avoids per-tick subprocess spawns by design — keep new metrics reading virtual files, not shelling out.
discover.sh handshake. At startup it prints TEMP <path> and BAT <path>
(hwmon temp sensor + main battery, skipping scope=Device peripherals) on stdout;
the QML side parses those lines into tempPath/batPath, which then feed
FileViews. sysfs paths are stable for a boot, so this runs once, not per tick.
Conventions & gotchas
- Icons are referenced by Unicode codepoint (
config/Icons.qml,String.fromCharCode(0x...)) so source stays ASCII. The glyph must exist in the bundled Nerd Font —flake.nixbundlesnerd-fonts.jetbrains-mono+nerd-fonts.symbols-only(Font Awesome 4 range, plus Material Design). Font Awesome 5+ codepoints are NOT present and render as tofu squares; verify a codepoint is covered before using it. Codepoints above0xFFFF(e.g. Material Design0xf0xxx) needString.fromCodePoint, notString.fromCharCode. - Fonts are pinned via
FONTCONFIG_FILEin the flake wrapper, so glyphs render regardless of the host's installed fonts. Adding a font means editingfontsConfinflake.nix. Bar.qmlremaps its layer surface on geometry change (geomKey→ togglevisible). This is a deliberate workaround for stale-buffer garbage after output rotation/transform under Sway — don't remove it.- Quickshell upstream is tracked directly in
flake.nix(git, not nixpkgs) to pick up layer-surface fixes ahead of release. - Native Quickshell services back some modules:
Quickshell.I3(workspaces),Quickshell.Services.SystemTray(tray), PipeWire (volume).