fix(tui): scrollable modal dialogs so action buttons stay reachable (#275) - #280
Conversation
|
Warning Review limit reached
Next review available in: 13 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
WalkthroughMultiple TUI modals now scroll long content while keeping action buttons and safety warnings visible. Resume confirmation routes through shared logic that re-checks engine liveness before launching. ChangesScrollable modal dialogs
Resume confirmation guard
Estimated code review effort: 3 (Moderate) | ~20 minutes Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Comprehensive review — changes requestedThe scrolling implementation works, and the PR checks are green, but two enabled actions can be reached while their safety warnings remain hidden below long content. I recommend addressing these before merge. Findings
Remediation plan
Verification performed
Review triage: 3 patch items, 2 pre-existing/follow-up items. |
…review) Address the PR #280 adversarial-review findings on the #275 modal-scroll fix: - Hoist the ConfirmModal double-drive warning and the EscalationModal restore-discard hint out of VerticalScroll(#body) so they dock with the action buttons and can no longer scroll out of view while an enabled Resume / Re-arm button stays clickable. - Route action_resume_run's confirm callback through _do_resume so it re-checks engine liveness at click time (matching the e/viewer and re-arm paths) instead of launching blind; removes the duplicated launch/notify. - Strengthen the regression tests: the decision test now scrolls to and activates #opt-8; new tests assert the Confirm/Escalation warnings stay on-screen while the destructive button is enabled, and that resume is refused when the engine may be live; the checkpoint test uses long content. - Soften the CHANGELOG "any terminal size" claim to the dialog's minimum frame height. Responsive dialog widths + compact small-height chrome tracked in #281.
Review addressed — pushed
|
| # | Finding | Resolution |
|---|---|---|
| 1 | High — Resume double-drive warning can scroll out of view | The ⚠ warning is now docked outside VerticalScroll(#body), directly above the button row (ConfirmModal.compose), so it's visible whenever the confirm button is. Plus: action_resume_run's confirm callback now routes through _do_resume, which re-checks _engine_possibly_live at click time (matching the e/viewer and re-arm paths) — a newly-live engine is refused even if the user confirmed. Removes the duplicated inline launch.resume_detached. |
| 2 | High — Re-arm restore-discard warning can scroll out of view | The restore-discard hint is now docked outside #body (EscalationModal.compose, id="hint"), so it stays on-screen while the enabled #act-rearm is reachable. (Re-arm already re-checked liveness via _resolve_blocked_by_liveness; the residual gap was purely warning visibility.) |
| 3 | Medium — Tests prove overflow, not scroll-access | test_decision_modal_scrolls_when_content_long now scrolls #opt-8 into view, asserts it's on-screen, clicks it, and asserts the eighth option is returned. New test_confirm_warning_stays_on_screen / test_escalation_rearm_warning_stays_on_screen assert the warnings are on-screen while the destructive button is enabled. New test_resume_confirm_rechecks_liveness asserts resume is refused (no resume_detached) when the engine may be live. The checkpoint case now uses genuinely long content and asserts the body scrolls. |
| 4 | Medium (follow-up) — "any terminal size" over-claims | CHANGELOG reworded to "down to the dialog's minimum frame height." The compact small-height-layout work is tracked in #281. |
| 5 | Medium (follow-up) — Fixed widths clip on narrow terminals | Pre-existing; out of scope for the vertical-overflow fix. Tracked in #281 (responsive dialog widths). |
Verification: uv run pytest -q tests/test_tui_app.py green (incl. the new/strengthened tests); full suite 2907 passed (the 2 test_module_skills_sync failures are pre-existing local-install drift — they skip on a clean checkout/CI); uvx pyright@1.1.411 clean; trunk fmt + trunk check clean.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/bmad_loop/tui/screens/modals.py`:
- Around line 213-232: Update the BaseDialog `#dialog` styling to use a definite
height, such as 90%, instead of height: auto, so the percentage max-height on
StartSweepModal and related modal bodies has a stable containing height.
Preserve the existing modal layout and scrolling behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: e5d5601a-1a95-4000-8ccc-681b0096cf2a
📒 Files selected for processing (4)
CHANGELOG.mdsrc/bmad_loop/tui/app.pysrc/bmad_loop/tui/screens/modals.pytests/test_tui_app.py
…280 review) CodeRabbit flagged the auto-parent + %-max-height pattern on the bounded modals (Confirm/StartSweep/StoryCheckpoint) and suggested a definite #dialog height: 90%. That regresses the deliberate compact tier — a one-line confirm balloons from 7 to 23 rows. Keep #dialog auto; add a CSS comment recording the two-tier intent and a regression test asserting a short confirm stays compact, so the guard is provable and the 'fix' can't silently land.
Closes #275.
Problem
The TUI "Decision Panel" (
DecisionModal) — and several sibling run-control modals — rendered their body content and their action buttons as direct children of a non-scrollingVertical(id="dialog")capped atmax-height: 90%. When the LLM-written decision/escalation text or option list was long, the dialog hit its cap and the plainVerticalclipped the overflow with no scrollbar and no scroll key, so the lowestchoose/skip(or Resolve/Re-arm/close) button became unreachable. This is a pure Textual layout bug — OS-independent (macOS in #275 was just where it surfaced); it reproduces on any terminal short enough that content exceeds 90% of the height.Fix — the repo's own docked-buttons pattern, in two tiers
The codebase already endorses the correct pattern (
modals.py:108, used byStartRunModal/DeferredEntryModal/SpecReviewModal/ValidateFindingsModal/TextOutputModal): aVerticalScrollbody region + a dockedHorizontal(classes="buttons")row that never scrolls off.DecisionModal,EscalationModal). Switch#dialogto a definiteheight: 90%and wrap all growable content inVerticalScroll(id="body") { height: 1fr }; the button row stays docked outside. (A1frchild needs a definite parent height — hence the fixed#dialogheight.)ConfirmModal→ alsoConfirmResumeModal,StartSweepModal,StoryCheckpointModal). Keep#dialog { height: auto; max-height: 90% }so small dialogs still hug their content, and wrap the variable body inVerticalScroll(id="body") { height: auto; max-height: N% }(grows to content, scrolls past the cap — the same mechanism the old#context/#blockingboxes used). No visual change in the common short case; the docked buttons can no longer be pushed off-bottom by an unexpectedly long body (e.g. an engine-writtenpaused_reason).All widget ids are preserved verbatim (tests click by id), and all user/LLM strings stay wrapped in
rich.Text. The five already-correct modals andBaseDialog's shared CSS are untouched.Tests
Adds four regression tests to
tests/test_tui_app.pythat push each modal directly with long content and assert (a) the#bodyscrolls (max_scroll_y > 0) and (b) the docked action buttons are fully on-screen (screen.region.contains_region(...)). The sweep/checkpoint test runs at height 14 — just above the modal frame floor (a thick-bordered dialog with a title and a 3-row button row needs ~13 rows before any body content), so the body-scroll fix is what keeps the buttons reachable.Verification
uv run pytest -q tests/test_tui_app.py— 135 passed (new + existing modal tests, including the#opt-1/#act-resolve/#act-rearmclick paths).uv run pytest -q— full suite green (the 2test_module_skills_syncfailures are pre-existing local-install drift; those tests skip on a clean checkout/CI and are unrelated to this change).uvx pyright@1.1.411clean;trunk fmt+trunk checkclean.dwith a long pending decision (or open an escalation) — the body scrolls (wheel/arrows/Tab) and every button stays reachable.Summary by CodeRabbit