From 64d244cfcb4aeeacb114cb9279640a5ebceab264 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Sat, 30 May 2026 13:59:05 +0200 Subject: [PATCH 1/2] Refactor: extract private setAppStatusContent helper method --- pkg/gui/controllers/helpers/app_status_helper.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pkg/gui/controllers/helpers/app_status_helper.go b/pkg/gui/controllers/helpers/app_status_helper.go index fa402962cc4..e1915f027ef 100644 --- a/pkg/gui/controllers/helpers/app_status_helper.go +++ b/pkg/gui/controllers/helpers/app_status_helper.go @@ -140,9 +140,7 @@ func (self *AppStatusHelper) renderAppStatusSync(stop chan struct{}) { for { select { case <-ticker.C: - appStatus, color := self.statusMgr().GetStatusString(self.c.UserConfig()) - self.c.Views().AppStatus.FgColor = color - self.c.SetViewContent(self.c.Views().AppStatus, appStatus) + self.setAppStatusContent() // Redraw all views of the bottom line: bottomLineViews := []*gocui.View{ self.c.Views().AppStatus, self.c.Views().Options, self.c.Views().Information, @@ -155,3 +153,9 @@ func (self *AppStatusHelper) renderAppStatusSync(stop chan struct{}) { } }() } + +func (self *AppStatusHelper) setAppStatusContent() { + appStatus, color := self.statusMgr().GetStatusString(self.c.UserConfig()) + self.c.Views().AppStatus.FgColor = color + self.c.SetViewContent(self.c.Views().AppStatus, appStatus) +} From 101d7965aef51e8e916ea2015c541e07f3ac4d26 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Sat, 30 May 2026 13:47:02 +0200 Subject: [PATCH 2/2] Fix the waiting status display for synchronous operations Commit 4f0393f97b06 caused a regression: for operations that use WithWaitingStatusSync (examples are squashing fixups, moving commits up or down, cherry-picking, creating fixup commits, and more), the waiting status wouldn't show during the operation; however, it would show after the operation was done, and then linger forever. The cause: since 4f0393f97b06, layout sizes the bottom line from the actual content of the AppStatus view rather than from the status manager. The async render path keeps the view in sync (it sets the buffer on the first tick and clears it to "" when the status ends), but the sync path used by WithWaitingStatusSync did not: - It called ForceLayoutAndRedraw before writing anything to the view, so layout saw an empty buffer and left no room; the status never appeared during the operation. - When the operation finished it just broke out of the loop, leaving the last spinner frame in the buffer. Every subsequent layout kept reserving room for that stale content, so the status stuck around forever. Fix this by writing the status into the view before the initial layout, and clearing it again when stopping. --- pkg/gui/controllers/helpers/app_status_helper.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pkg/gui/controllers/helpers/app_status_helper.go b/pkg/gui/controllers/helpers/app_status_helper.go index e1915f027ef..17c61ae2632 100644 --- a/pkg/gui/controllers/helpers/app_status_helper.go +++ b/pkg/gui/controllers/helpers/app_status_helper.go @@ -126,6 +126,13 @@ func (self *AppStatusHelper) renderAppStatusSync(stop chan struct{}) { ticker := time.NewTicker(time.Millisecond * time.Duration(self.c.UserConfig().Gui.Spinner.Rate)) defer ticker.Stop() + // Write the status into the view before the first layout below, so that + // layout (which sizes the bottom line based on the actual content of the + // AppStatus view) leaves room for it and it shows right away. The ticker + // only updates the spinner frame using ForceFlushViewsContentOnly, so this + // doesn't re-layout. + self.setAppStatusContent() + // Forcing a re-layout and redraw after we added the waiting status; // this is needed in case the gui.showBottomLine config is set to false, // to make sure the bottom line appears. It's also useful for redrawing @@ -148,6 +155,14 @@ func (self *AppStatusHelper) renderAppStatusSync(stop chan struct{}) { } _ = self.c.GocuiGui().ForceFlushViewsContentOnly(bottomLineViews) case <-stop: + // Clear the status from the view and re-layout, otherwise the + // stale content would keep layout reserving room for it forever. + // The UI thread is free again at this point, so we go through + // OnUIThread like the async renderAppStatus does. + self.c.OnUIThread(func() error { + self.c.SetViewContent(self.c.Views().AppStatus, "") + return nil + }) break outer } }