fix: eliminate alt-screen flash when returning from repo/project detail

The hub hands its live terminal to detail/project_detail on open (already
flash-free going in), but both sub-views unconditionally tore the terminal
down on exit; the hub's next loop iteration then always called tui::setup()
again, producing a real LeaveAlternateScreen -> (plain terminal visible for
one frame) -> EnterAlternateScreen blink on every trip back.

- detail::run / project_detail::run now return the live terminal on a clean
  exit instead of tearing it down (only torn down if the inner loop itself
  errors); hub::tui_phase accepts an already-live terminal and skips its own
  setup() when given one, threading it across loop iterations
- project_detail's own nested repo-detail call (Enter on its Repos tab) had
  the same issue one level down, fixed the same way via Option<Tty> so the
  terminal can be taken out for the nested call and put back
- verified via raw pty byte counts: two full hub<->detail round trips now
  show zero EnterAlternateScreen/LeaveAlternateScreen pairs in between,
  vs. one pair per trip before
This commit is contained in:
dualfroz committed 2026-07-07 17:58:02 +02:00
1 parent 14ef7fb06c
commit a3658d5e97
4 files changed
+64 -22

No files matched your search

+10 -5
View File
@@ -38,12 +38,16 @@ use settings_io::{compute_activity, poll_api_rx, poll_prs_issues_rx, poll_settin
/// Opens the detail view for `summary`. Reuses an already-live terminal
/// handed over by the hub when given one (avoids a flash of the alternate
/// screen toggling off and back on), otherwise sets up a fresh one.
/// screen toggling off and back on), otherwise sets up a fresh one. On a
/// normal exit (back to the hub), hands the still-live terminal back to the
/// caller instead of tearing it down — the hub reuses it for its own next
/// frame, so the alternate screen never toggles off in between. Only torn
/// down here if `run_detail_loop` itself errors.
pub async fn run(
summary: RepoSummary,
cfg: &AppConfig,
maybe_term: Option<crate::tui::Tty>,
) -> Result<()> {
) -> Result<crate::tui::Tty> {
let path = summary.path.clone();
let name = summary.name.clone();
@@ -147,9 +151,10 @@ pub async fn run(
};
// Main loop — terminal stays alive the whole time.
let result = run_detail_loop(&mut st, &mut terminal).await;
crate::tui::teardown(terminal)?;
result
match run_detail_loop(&mut st, &mut terminal).await {
Ok(()) => Ok(terminal),
Err(e) => { crate::tui::teardown(terminal)?; Err(e) }
}
}
async fn run_detail_loop(st: &mut St, terminal: &mut crate::tui::Tty) -> Result<()> {