cmux NEXT: critical host pressure banner and three indicator looks, off by default (part 3)

Merged
TE
14d ago
+537-22

At critical host pressure, cmux NEXT can now show a banner that names what is overloaded, with Show Details to open the indicator's popover. It's part 3 of cmuxterm-hq#2995, on top of the top-row indicator (#24507, merged). It also adds three looks for the indicator so the gallery can compare them. Look A (the default) was picked.

coord-deputy's order, quoted:

"app-eff/host-pressure-banner: put all 3 banner looks in the gallery, pick the best yourself, and land that one. ... It's a new dev surface, so both ship off by default behind a Debug/Labs toggle (cmux#22662). Codex review at the exact head, green compile ... each under ~800 lines."

Ships off by default behind Debug toggle hostPressure.banner (cmux#22662), on top of the indicator's own hostPressure.enabled (also off). Release and RC never activate the tunable store, so they keep both off.

What it does

  • Banner: at critical, with hostPressure.banner on, an untilClosed toast says "This Mac is overloaded: …" with the popover's lines (the load against its limit in words, then acpmux's other reasons) and Show Details. If you close it, or use Show Details, that window's banner stays closed for the critical spell. HostPressureService.criticalSpell counts each time the level the windows show (a forced level wins) enters critical, including after the monitor stops and its level resets. The level falling, the toggle going off, and the indicator stopping sampling or leaving its window all end it in code. So does releasing it: HostPressureBanner dismisses its toast on the main actor when released, even after an off-main deinit. The close is remembered per window, so a window that is closing (its toast also ends with .closed) never keeps other windows' banners shut.
  • Indicator looks (hostPressure.style, Debug Settings only): A, gauge (the default: a gauge at warning, a triangle at critical); B, a triangle at both levels; C, the gauge plus "146.0 / 12" (the load against its limit). C's label widens the indicator and the top row relays out.
  • Fixes found while capturing:
    • The indicator's visibility change called the root's layoutSubtreeIfNeeded() without marking the root, so its frame stayed at zero until something else relaid out the window. In light the banner showed but the glyph did not. It now sets superview?.needsLayout first.
    • Capture minis report their windows as occluded, so the indicator's window gate kept a forced level from ever showing there. A forced level (hostPressure.force, which only captures use) now counts as a visible window while the indicator is in one, still only with hostPressure.enabled on.

Timers and animations

Adds none. The banner follows the indicator's existing Observation of HostPressureService (the one 15 s monitor read from #24507). Its cleanup is HostPressureBanner.end() in leave() and when the condition goes false, plus the box's own release. The toast's fade is the shared CmuxToastCenter one, behind its motion gate. Nothing pulses or blinks. One more case runs the monitor: while a level is forced in Debug Settings, it runs even when the window is occluded. Setting the force back to Off restores the window gate.

Gallery: the three looks at critical, light and dark

Chosen: look A, the gauge (a triangle at critical). The glyph's shape alone tells warning from critical, and it stays one fixed square, so the top row never moves when a new sample changes the numbers. The numbers are already one click away in the popover and, at critical, in the banner itself. B (a triangle at both levels) is identical to A at critical, as below, but it loses the shape difference at warning, so you can't tell the two levels apart at a glance. C puts "146.0 / 12" in the top row. That pushes the tab strip right, shifts the row whenever the number changes, and turns the warning into a meter to watch, which "just warn for now" didn't ask for. B and C stay in Debug Settings (hostPressure.style) because keeping them costs one enum and a label. A is the default.

Each pair: the indicator turned on, the banner turned on, the level forced to critical (the cosmic-gray incident's numbers), then light and dark. These are real fleet builds of 7dfd9c585ac3 driven on a capture mini, and the text was checked for private data.

A, gauge (chosen, the default)

Capture steps-163ee3f7e8 at 7dfd9c585ac3 (fleet build, capture mini, text checked for private data)

LightDark
lightdark

B, triangle at both levels

Capture steps-15f6f6403c at 7dfd9c585ac3 (fleet build, capture mini, text checked for private data)

LightDark
lightdark

C, gauge with the load against its limit

Capture steps-95e9f30c29 at 7dfd9c585ac3 (fleet build, capture mini, text checked for private data)

LightDark
lightdark

Design spec

teamleaderleo/leo-workspace#929 adds design/host-pressure.md (tokens, spacing, the looks, the banner rules and what the surface must never do).

Strings

hostPressure.banner ("This Mac is overloaded: %@") and hostPressure.showDetails ("Show Details") are in HostPressure.xcstrings in all 21 locales. That catalog landed with #24508.

Observed / Not run

  • Ran at the head 51a271a11ee3: compile and suites (results in a comment).
  • The gallery stills are from 7dfd9c585ac3. Later changes (dismissal rules, the banner box, a forced level only in a window, the rebase onto feat-cmux-next, strings) don't change what the stills show.
  • prepush-gate.py: no new violations.
  • Not run: a live host at critical. The captures use the forced level.

Leo-ask: ask-427cc7d8f5

Changelog

none (Debug-only, off by default)

Agent session: cosmic-gray

🤖 Generated with Claude Code