feat(ux): determinate restore rail, and a shutdown board that is actually visible - #120
Merged
Conversation
…ally visible Two waiting states, treated differently on purpose. STARTUP — restore rail A 25-session restore runs ~131s with per-session launch cost swinging 12x (0.6s to 10.9s, measured from crash.log). There is no steady rate to extrapolate from, so an indeterminate spinner reads identically at session 2 and session 22. RestoreRail (2px, under the toolbar) plus a "7 / 25 restoring" pill give the aggregate k-of-N; per-session state stays on the existing placeholder sidebar rows. Restore does NOT block the user — placeholder rows mean you can click into a session the moment it lands — so this stays deliberately out of the way. Nothing modal. The counter advances after the try/catch, not inside it, so a session that fails to restore still moves the rail. Otherwise one bad session strands it short of full, which reads as exactly the hang it is meant to rule out. SHUTDOWN — the overlay was invisible, and that is the real bug Reported as "more like 1 line - thats hard to see. no spinner or anything". The markup looked correct: a Grid sibling with Panel.ZIndex="100" and a Storyboard-rotated arc dead centre. WebView2 is an HwndHost. A native child window is composited by the OS on top of everything WPF renders, and Panel.ZIndex does not enter into it. The overlay was being drawn BEHIND every terminal pane. What reached the user was scrim leaking through the few-pixel gaps between panes — hence a stray line and no spinner, because the spinner sat centred over a pane. The codebase already documents HwndHost for input (#108, three separate call sites). Nobody had written down the output half, which is the more expensive one to rediscover because the code looks right. Fix: collapse TerminalGrid before showing the overlay. Safe — every pane is about to be disposed, and a frozen terminal is worth nothing mid-shutdown. With it visible, it earns being more than a spinner. The board names every session and ticks it off (· pending, ◐ closing, ✓ clean, ⨯ force-disposed), with elapsed time per session, a k/N bar, and a budget bar against ClaudeShutdownBudgetMs. Force-disposed sessions are marked rather than hidden: that is the case a user most wants to see and it used to happen completely silently. ShutdownHint escalates with elapsed time to explain why the wait is long. Explanatory, not jokey — it has to read well on the four-hundredth shutdown, which is the argument against the "be patient" copy I originally sketched. Also here: - Board list scrollbar restyled (scoped to that ScrollViewer). Any setup past ~14 sessions scrolls, so the stock system-coloured bar was on screen for most shutdowns, not an edge case. - Board skipped entirely at zero sessions, so --clean runs don't flash a full-window "0 / 0". - Corrected a stale comment claiming the adaptive config gate is "still used at SHUTDOWN". #117 removed it from both paths. Verified: layout rendered offscreen via RenderTargetBitmap against the real markup extracted from MainWindow.xaml (9 rows and 27 rows, to confirm the card caps and the list scrolls). The airspace fix itself needs a live run — it cannot be proven headlessly, since it is about OS compositing. 304/304 tests pass, 0 warnings. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NDsEfog5kVkT5NmX1Ya5be
AThraen
force-pushed
the
feat/waiting-states
branch
from
September 6, 2026 17:47
d173a0a to
7665ee7
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements the two treatments chosen from the Waiting States options: Quiet Rail for startup, Restore Board for shutdown.
Startup — restore rail
A 25-session restore runs ~131s, with per-session launch cost swinging 12× (0.6s–10.9s, from
crash.log). There's no steady rate to extrapolate from, so an indeterminate spinner reads identically at session 2 and session 22.RestoreRail(2px, under the toolbar) + a7 / 25 restoringpill give the aggregate; per-session state stays on the placeholder sidebar rows that already exist.Deliberately non-blocking. Restore doesn't actually block you — placeholder rows let you click into a session the moment it lands — so nothing modal goes in front of it.
The counter advances after the
try/catch, not inside it: a session that fails to restore still moves the rail, otherwise one bad session strands it short of full and reads as the exact hang it's meant to rule out.Shutdown — the overlay was invisible, and that's the real bug
Reported as "more like 1 line - thats hard to see. no spinner or anything."
The markup looked fine — a
Gridsibling withPanel.ZIndex="100"and aStoryboard-rotated arc dead centre.WebView2 is an
HwndHost. A native child window is composited by the OS on top of everything WPF renders, andPanel.ZIndexdoes not enter into it. The overlay was being drawn behind every terminal pane. What reached the user was scrim leaking through the few-pixel gaps between panes — hence a stray line and no spinner, since the spinner sat centred over a pane.The codebase documents
HwndHostfor input in three places (#108). Nobody had written down the output half, which is the more expensive one to rediscover because the code looks correct.Fix: collapse
TerminalGridbefore showing the overlay. Safe — every pane is about to be disposed, and a frozen terminal is worth nothing mid-shutdown.Once visible, it earns being more than a spinner:
·pending,◐closing,✓clean,⨯force-disposedk / NbarClaudeShutdownBudgetMs, green → peach → pinkShutdownHintescalates with elapsed time to explain why the wait is longThe hint is explanatory rather than jokey. It has to read well on the four-hundredth shutdown, which is the argument against the "be patient" copy in the original sketch.
Also here
ScrollViewer). Any setup past ~14 sessions scrolls, so the stock system-coloured bar was on screen for most shutdowns.--cleanruns don't flash a full-window "0 / 0".CLAUDE.md: the airspace rule written down, cross-linked from the existing input-side note.Verification
Layout rendered offscreen via
RenderTargetBitmapagainst the real markup extracted fromMainWindow.xaml(not a copy), at 9 rows and 27 rows, to confirm the card caps atMaxHeightand the list scrolls with header/footer pinned.304/304 tests pass, 0 warnings.
The airspace fix itself needs a live run — it's about OS compositing and can't be proven headlessly. That's the one thing to eyeball on merge.
🤖 Generated with Claude Code
https://claude.ai/code/session_01NDsEfog5kVkT5NmX1Ya5be