Releases: kinncj/Heimdall
Release list
v2.7.3 — Tiny-viewport windowing fix
Fixed
- Very short scroll viewports (1–2 rows) now show content instead of only a `▲/▼ more` marker. After the v2.7.2 windowing unification, a body overflowing a 1-row viewport could render just the marker and no content; the pane cursor could also be overwritten by an edge marker at a scrolled boundary. Both are fixed.
Full Changelog: v2.7.2...v2.7.3
v2.7.2 — Unify TUI windowing (internal)
Changed
- Dashboard modals and detail view now scroll through the shared `pane.Window`; the duplicate `scrollWindow`/`keepSelVisible` are gone, so modals show the same `▲/▼ more · y/total` affordance as the top view. The unused pane `Filterable`/`Sortable` API was dropped (the dashboard keeps its own filter/sort idiom). No user-facing change. Closes #8.
Full Changelog: v2.7.1...v2.7.2
v2.7.1 — Ghostty over SSH shows the splash image
Fixed
- Ghostty over SSH now shows the splash image, not the ASCII fallback. Image detection recognised Ghostty only by `TERM_PROGRAM=ghostty`, which SSH and tmux strip — so a dashboard run over SSH from Ghostty fell back to ASCII even though the terminal can render images. It now also treats `TERM=xterm-ghostty` (always forwarded) as Kitty-graphics-capable.
Full Changelog: v2.7.0...v2.7.1
v2.7.0 - Himinbjörg — one focus + scroll primitive for the TUI
What's Changed
Himinbjörg — one focus + scroll primitive for the TUI
Scrolling was implemented three times in the TUI and behaved differently in each
place; the full-screen top view (t) didn't scroll like the rest, selection lists
let the cursor drift off-screen, and the mouse wheel did nothing in the top view.
This introduces one shared primitive and moves the top view onto it.
Story: docs/stories/himinbjorg-focus-scroll-20260702235455-0021/Story.md
Design: docs/specs/himinbjorg-focus-scroll/design.md · ADR: docs/architecture/0023-tui-focus-scroll-pane-primitive.md
What's in it
- New
app/internal/tui/panepackage — the primitive:Pane: scroll, selection that follows the cursor,/filter, sort cycle,
▲/▼ more · y/totalaffordances, focus ring by border weight (survivesNO_COLOR).Group: grid layout (side-by-side panels), reading-orderTab, two-level
page+pane scroll, pointer-targeted mouse, and a whole-page-first focus stop.- Capability interfaces (
Filterable/Sortable) are opt-in — static panels get neither.
- Top view (
t) runs on the primitive. The whole view is the first focus (↑/↓ +
wheel scroll the entire screen at any resolution);Tabsteps the ring through each
panel and wraps back; wheel/click target the panel under the pointer (was dead here);
the wide 2-column grid is kept.topview's ownscrollWindowcopy is deleted. - Selection stays in view in the command / log-source / sort pickers (
keepSelVisible). - Search / filter, one idiom (
/), extended to every list: command picker (by
name), command result (by output line), and the process view (by command) — on top
of the existing log search. Each clears withescand keeps the cursor in view. - Docs: new
docs/guides/18-using-the-dashboard.md(drive the TUI after setup +
a full keybindings table), corrected12-top-view.md, updated in-app help (?).
Verification
- 303 unit tests green across 29 packages (
go test ./...);go vetclean. - New tests cover the pane math (clamp, keep-visible, page-auto-scroll), grid focus +
mouse hit-testing, whole-page focus, and every new filter. - Top view verified with a headless render (grid alignment, focus ring, whole-page +
page scroll). - Gates: Karpathy 87 (PASS_WITH_APPROVAL), a11y PASS (0 critical/serious,
NO_COLOR-safe).
Deferred (tracked as a follow-up)
Migrating the remaining dashboard modal bodies onto pane.Group so the dashboard's
own scrollWindow copy can be deleted and the Filterable/Sortable API gets a
direct consumer. It's internal dedup with no user-facing change (log/cmd/process
filters already work via the dashboard's own idiom), so it's kept out of this PR to
avoid a large rewrite of a stable view. See the follow-up issue.
Full Changelog: v2.6.3...v2.7.0
v2.6.3
Standardization boundary + P/E core grouping fix
The fix you can see
The top view's P/E core grouping was computed in the dashboard, keyed off a metric field (Gauge) that the wire drops for per-core metrics — so every host fell back to the plain grid. The grouping now lives in the domain core and is derived from data that actually crosses the wire.
The architecture
Moved core grouping out of the views into domain.CoreGroups / domain.CoreTypeSummary (framework-free, tested once). The rule, now written down in ADR-0022 and a contributor guide:
Adapters translate platform specifics into neutral metrics · the
domaincore standardizes them · consumers (TUI, CLI, dashboard) render only.
- CLI now reports
core_groups(standardized P/E/LP with real core ids + util) — agents read structured groups, not a12P + 4Estring. - Rule codified in CLAUDE.md, AGENTS.md, Copilot instructions, a Cursor rule, and the architect agent across all harnesses.
Security
Update
heimdall-dashboardto see the grouped grid;heimdall-cliforcore_groups. Daemons need no change for this.
v2.6.2
Fix: top view showed the plain per-core grid instead of P/E grouping
The grouping keyed off cpu.topology's Gauge (the distinct-core-type count), but per-core metrics ride the protobuf per_core oneof — so the Gauge is dropped on the wire and the dashboard received it as 0, falling back to the unlabelled grid on every host.
It now derives the type count from the PerCore slice, which does cross the wire. Worked locally (--once) and in unit tests because the Gauge is set in-process; only the wire round-trip lost it. The cpu clusters: power line was never affected.
Update the heimdall-dashboard binary on the machine you watch from.
v2.6.1
Fix: CPU power blank on Linux/AMD hosts with a GPU
The daemon's in-process-first gate treated power.total as proof it already had CPU power. But power.total is synthesised from power.gpu on any GPU-equipped host — so the daemon short-circuited and never consulted the root helper for RAPL power.cpu. Result: power.cpu went blank on every Linux/AMD box with a GPU, even with the helper running.
The gate now checks for an actual CPU rail (power.cpu / power.pkg), not the whole-machine total. A non-root Linux daemon with a running helper reads RAPL again.
- Regression from the v2.5.0 power standardization.
- Apple (SMC/IOReport in-process) and Windows (Scaphandre in-process) were never affected.
- ARM hosts with no RAPL (GB10) correctly stay a dash.
Update the heimdall-daemon binary on Linux/AMD hosts that have the root helper running.
v2.6.0
Top view: per-core grid labelled by core type + per-cluster CPU power
- P/E-labelled core grid. On hybrid silicon the top view groups per-core bars under
P-cores (N):/E-cores (N):headers with real core ids. New unprivilegedcpu.topologymetric: sysctlhw.perflevel*(Apple Silicon — E-cluster owns the low ids, verified live on an M3 Max),cpu_core/cpu_atomsysfs (Intel hybrid Linux),EfficiencyClass(Windows). AMD / non-hybrid / ARM stay uniform and keep the plain grid. - Apple Pro/Max cluster power. The SMC P/E rails now ship as
power.cpu.pcluster/power.cpu.ecluster; the POWER panel showscpu clusters: P … W · E … Wunder the CPU figure. Hosts without cluster rails emit nothing — never a fabricated 0. - BC: new metric names only. Older daemons and dashboards render exactly as before.
Story 0028 · #5
v2.5.2
Hardening: the Apple SMC CPU-power fallback can never show a garbage number on an unmapped chip.
Fixed
- The SMC CPU cluster keys are verified on an M3 Max; on a different Apple die the same keys could mean something else.
power.cpufrom SMC is now rejected if the sum is implausible (NaN/Inf, negative, or > 200 W), so such a chip falls back to Unavailable rather than showing garbage. IOReport stays the first source (covers every base die), so this only affects the Pro/Max SMC fallback — no change on Linux (RAPL) or Windows (Scaphandre).
v2.5.1
Apple Pro/Max CPU power now covers the whole CPU, not just the P-cores.
Changed
power.cpuon Apple Pro/Max is the full CPU complex. The SMC CPU sum wasPC02+PC42(the two P-clusters only), which undercounted by missing the E-cores. It now also includes the E-core / cluster rails that scale with CPU load (PC03+PC43) — verified by sweeping the SMC keys under a full 16-core load on an M3 Max (memoryPC32and GPUPC1x/PC2xkeys stay excluded).power.cpureads ~18–20 W under load, labelledCPU complex (SMC).