From 4f0393f97b06e4586c74d18b260f67c472c06119 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Fri, 8 May 2026 22:37:00 +0200 Subject: [PATCH 01/11] Fix visual glitch with status bar spinner at very slow spinner rates With a very slow spinner rate (seconds), you can see that at the beginning of an operation the bottom line leaves a gap for where the status will go, but the status (and spinner) is only drawn the first time the spinner ticks. The reason is that layout looks at GetStatusString to decide how much room to leave, rather than at the actual content of the AppStatus view; the view content is only set by renderAppStatus the first time the spinner ticks. Fix this by making layout look at the actual content of the view so that the layout is in sync with what is drawn. This also avoids flicker if an operation is so fast that it finishes before the spinner ticks for the first time, especially for users who set gui.showBottomLine to false. --- pkg/gui/layout.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/gui/layout.go b/pkg/gui/layout.go index 21d83ca04d9..dacd93f68bc 100644 --- a/pkg/gui/layout.go +++ b/pkg/gui/layout.go @@ -5,6 +5,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/gocui" "github.com/jesseduffield/lazygit/pkg/gui/types" + "github.com/jesseduffield/lazygit/pkg/utils" "github.com/samber/lo" ) @@ -23,7 +24,11 @@ func (gui *Gui) layout(g *gocui.Gui) error { informationStr := gui.informationStr() - appStatus := gui.helpers.AppStatus.GetStatusString() + var appStatus string + appStatusView, err := g.View("appStatus") + if err == nil { + appStatus = utils.Decolorise(appStatusView.Buffer()) + } viewDimensions := gui.getWindowDimensions(informationStr, appStatus) From b3deef31ad7ec14ab315807a2bc75c13c2ce3901 Mon Sep 17 00:00:00 2001 From: Antoine Gaudreau Simard Date: Thu, 7 May 2026 21:49:00 -0400 Subject: [PATCH 02/11] When drawing tainted views in ForceFlushViewsContentOnly, also draw views that overlap them This prevents views from drawing over higher z-order views. Currently this is not an issue in practice, because we use ForceFlushViewsContentOnly only for the bottom line status spinner, and there are never views on top of it. However, later in the branch we will use the mechanism to redraw the inline spinners in panels (e.g. the "Pushing..." status next to a branch name), and there could be a popup on top of it. Co-authored-by: Stefan Haller --- pkg/gocui/flush_test.go | 90 +++++++++++++++++++++++++++++++++++++++++ pkg/gocui/gui.go | 38 +++++++++++++++-- 2 files changed, 124 insertions(+), 4 deletions(-) diff --git a/pkg/gocui/flush_test.go b/pkg/gocui/flush_test.go index a27943d7c98..59bae427ccf 100644 --- a/pkg/gocui/flush_test.go +++ b/pkg/gocui/flush_test.go @@ -1,6 +1,7 @@ package gocui import ( + "strings" "testing" "github.com/stretchr/testify/assert" @@ -200,3 +201,92 @@ func TestProcessRemainingEvents_EmptyQueue_ReturnsTrue(t *testing.T) { assert.NoError(t, err) assert.True(t, contentOnly, "should return true when no events are queued") } + +// Ensure an overlapping view that is not tainted does not get overdrawn +func TestFlushContentOnly_DoesNotOverdrawHigherZViews(t *testing.T) { + g := newTestGui(t) + + // Base view + list, _ := g.SetView("list", 0, 0, 79, 23, 0) + list.Frame = false + list.SetContent(strings.Repeat("LIST LINE FILLER FILLER FILLER FILLER FILLER FILLER FILLER FILLER FILLER\n", 22)) + + // Overlapping 'popup' + popup, _ := g.SetView("popup", 20, 8, 60, 16, 0) + popup.Frame = false + popupLine := strings.Repeat("P", 60) + popup.SetContent(strings.Repeat(popupLine+"\n", 16)) + + // Full flush — popup ends up on top. + assert.NoError(t, g.flush()) + + cellAt := func(x, y int) string { + s, _, _ := g.screen.Get(x, y) + return s + } + + // Taint only the list view + list.SetContent(strings.Repeat(strings.Repeat("X", 80)+"\n", 22)) + assert.True(t, list.IsTainted(), "list should be tainted after SetContent") + assert.False(t, popup.IsTainted(), "popup should not be tainted") + + // flushContentOnly is what spinner ticks ultimately invoke. + assert.NoError(t, g.flushContentOnly(g.views)) + + assert.Equal(t, "P", cellAt(21, 9), + "popup region must still show popup content after flushContentOnly; "+ + "if this fails the popup-overdraw bug is present") + + // Additional checks to be sure + assert.Equal(t, "P", cellAt(40, 11), "interior popup cell should still show popup content") + assert.Equal(t, "P", cellAt(58, 14), "near-edge popup cell should still show popup content") + + // Ensure tainted view was updated + assert.Equal(t, "X", cellAt(5, 5), "list cell outside popup should show new list content") + assert.Equal(t, "X", cellAt(70, 20), "list cell outside popup should show new list content") +} + +// Ensure transitive overlap: with views in z-order [a, b, c] where b overlaps a +// and c overlaps b but c does NOT overlap a, tainting a must redraw all three — +// otherwise b's redraw paints over c. +func TestFlushContentOnly_RedrawsTransitivelyOverlappingViews(t *testing.T) { + g := newTestGui(t) + + // Geometry: b straddles a and c; a and c are disjoint. + // a: (0,0)-(40,10) b: (30,5)-(60,15) c: (50,12)-(75,20) + a, _ := g.SetView("a", 0, 0, 40, 10, 0) + a.Frame = false + a.SetContent(strings.Repeat(strings.Repeat("A", 60)+"\n", 20)) + + b, _ := g.SetView("b", 30, 5, 60, 15, 0) + b.Frame = false + b.SetContent(strings.Repeat(strings.Repeat("B", 60)+"\n", 20)) + + c, _ := g.SetView("c", 50, 12, 75, 20, 0) + c.Frame = false + c.SetContent(strings.Repeat(strings.Repeat("C", 60)+"\n", 20)) + + assert.NoError(t, g.flush()) + + cellAt := func(x, y int) string { + s, _, _ := g.screen.Get(x, y) + return s + } + + // Taint only a. + a.SetContent(strings.Repeat(strings.Repeat("X", 60)+"\n", 20)) + assert.True(t, a.IsTainted()) + assert.False(t, b.IsTainted()) + assert.False(t, c.IsTainted()) + + assert.NoError(t, g.flushContentOnly(g.views)) + + // a redrawn (direct). + assert.Equal(t, "X", cellAt(5, 5), "a should be redrawn (tainted)") + // b redrawn (overlaps a). + assert.Equal(t, "B", cellAt(45, 7), "b should be redrawn (overlaps a)") + // c redrawn transitively (overlaps b, which overlaps a). Without the + // transitive case, b's redraw would paint over c at this cell. + assert.Equal(t, "C", cellAt(55, 14), + "c should be redrawn transitively; if 'B' here, b's redraw painted over c") +} diff --git a/pkg/gocui/gui.go b/pkg/gocui/gui.go index dac7295a3f1..645329d6b10 100644 --- a/pkg/gocui/gui.go +++ b/pkg/gocui/gui.go @@ -13,7 +13,9 @@ import ( "github.com/gdamore/tcell/v3" "github.com/go-errors/errors" + "github.com/jesseduffield/generics/set" "github.com/rivo/uniseg" + "github.com/samber/lo" ) // OutputMode represents an output mode, which determines how colors @@ -1170,11 +1172,9 @@ func (g *Gui) flush() error { // Redraws only tainted views and skips the layout pass. // tcell's cell-level dirty tracking ensures only // actually-changed cells are emitted to the terminal. +// Will also redraw any views that overlap tainted views func (g *Gui) flushContentOnly(views []*View) error { - for _, v := range views { - if !v.tainted { - continue - } + for _, v := range viewsToRedrawContentOnly(views) { if err := g.draw(v); err != nil { return err } @@ -1184,6 +1184,36 @@ func (g *Gui) flushContentOnly(views []*View) error { return nil } +func viewsToRedrawContentOnly(views []*View) []*View { + redrawIndexes := set.New[int]() + + for i, v := range views { + if !v.tainted && !redrawIndexes.Includes(i) { + continue + } + + redrawIndexes.Add(i) + + for j, above := range views[i+1:] { + aboveIndex := i + 1 + j + if !redrawIndexes.Includes(aboveIndex) && rectsOverlap(v, above) { + redrawIndexes.Add(aboveIndex) + } + } + } + + return lo.FilterMap(views, func(view *View, i int) (*View, bool) { + return view, redrawIndexes.Includes(i) + }) +} + +// Reports whether two views' rectangles share at least one cell. +func rectsOverlap(a, b *View) bool { + ax0, ay0, ax1, ay1 := a.Dimensions() + bx0, by0, bx1, by1 := b.Dimensions() + return ax0 <= bx1 && ax1 >= bx0 && ay0 <= by1 && ay1 >= by0 +} + func (g *Gui) ForceLayoutAndRedraw() error { return g.flush() } From 670565c175b0eaec0d0c0ae28b5eba2086668f30 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Thu, 7 May 2026 14:11:57 +0200 Subject: [PATCH 03/11] Have renderAppStatus trigger a full layout when the appStatus width changes In 0d195077e4a3 we improved the performance of the status bar spinner by avoiding a layout. This is fine from one spinner tick to the next, but it's a problem when spinning starts or ends (or in the hypothetical case that the status text changes in the middle of the operation, which we never do in lazygit, but theoretically could). In this case a layout is needed so that the rest of the status bar gets pushed over appropriately (or moves back to the left when the spinner ends), and also so that the bottom line is shown or hidden properly for users who set gui.showBottomLine to false. To fix this, keep track of the status string width and force a layout whenever it changes. This includes the beginning and end of an operation when it changes from empty to non-empty or vice versa. There is currently no observable misbehavior from this bug, but that's only because we must have a HandleRender call somewhere that forces a full layout when an operation starts or ends. We will remove the Render() call from HandleRender at the end of this branch, at which point the misbehavior would be visible if we didn't fix it here. --- pkg/gui/controllers/helpers/app_status_helper.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/pkg/gui/controllers/helpers/app_status_helper.go b/pkg/gui/controllers/helpers/app_status_helper.go index d71faeb6542..83500d3a7d5 100644 --- a/pkg/gui/controllers/helpers/app_status_helper.go +++ b/pkg/gui/controllers/helpers/app_status_helper.go @@ -6,6 +6,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/gocui" "github.com/jesseduffield/lazygit/pkg/gui/status" "github.com/jesseduffield/lazygit/pkg/gui/types" + "github.com/jesseduffield/lazygit/pkg/utils" ) type AppStatusHelper struct { @@ -93,13 +94,24 @@ func (self *AppStatusHelper) renderAppStatus() { self.c.OnWorker(func(_ gocui.Task) error { ticker := time.NewTicker(time.Millisecond * time.Duration(self.c.UserConfig().Gui.Spinner.Rate)) defer ticker.Stop() + prevAppStatus := "" for range ticker.C { appStatus, color := self.statusMgr().GetStatusString(self.c.UserConfig()) self.c.Views().AppStatus.FgColor = color - self.c.OnUIThreadContentOnly(func() error { + + update := self.c.OnUIThreadContentOnly + if utils.StringWidth(appStatus) != utils.StringWidth(prevAppStatus) { + // Need a full layout whenever the width of the status string changes. This can't + // happen during normal spinning because we validate that all spinner frames have + // the same width, so typically this will only be triggered at the beginning and end + // of a status, or if the status string changes midway for some reason. + update = self.c.OnUIThread + } + update(func() error { self.c.SetViewContent(self.c.Views().AppStatus, appStatus) return nil }) + prevAppStatus = appStatus if appStatus == "" { break From 79440c0945263fce7373efcd25cf2fbf710d4911 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Thu, 7 May 2026 20:40:09 +0200 Subject: [PATCH 04/11] Set the view color on the UI thread too Unrelated to this branch, just because we're touching this code: there's little reason to set the color on the background thread but the text on the UI thread. Set them both together on the UI thread. Avoids a data race (unlikely to be a problem in practice, we're talking about a 64-bit int, but still). --- pkg/gui/controllers/helpers/app_status_helper.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/gui/controllers/helpers/app_status_helper.go b/pkg/gui/controllers/helpers/app_status_helper.go index 83500d3a7d5..fa402962cc4 100644 --- a/pkg/gui/controllers/helpers/app_status_helper.go +++ b/pkg/gui/controllers/helpers/app_status_helper.go @@ -97,7 +97,6 @@ func (self *AppStatusHelper) renderAppStatus() { prevAppStatus := "" for range ticker.C { appStatus, color := self.statusMgr().GetStatusString(self.c.UserConfig()) - self.c.Views().AppStatus.FgColor = color update := self.c.OnUIThreadContentOnly if utils.StringWidth(appStatus) != utils.StringWidth(prevAppStatus) { @@ -108,6 +107,7 @@ func (self *AppStatusHelper) renderAppStatus() { update = self.c.OnUIThread } update(func() error { + self.c.Views().AppStatus.FgColor = color self.c.SetViewContent(self.c.Views().AppStatus, appStatus) return nil }) From 0d1caf5c22c17e785fb625dd04ac5231f706e89b Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Thu, 7 May 2026 08:28:50 +0200 Subject: [PATCH 05/11] Bounce refreshView to the UI thread An async refresh dispatches refreshXyz on a worker goroutine, which then calls refreshView -> PostRefreshUpdate -> HandleRender. Today the final self.c.Render() inside ListContextTrait.HandleRender is what triggers a UI flush from the worker. We're going to remove that Render() call, so prepare by wrapping refreshView's body in OnUIThread. This moves the entire rendering of the view (and the ReApplyFilter/ReApplySearch stuff) to the UI thread, not just the layout. I don't expect this to make a difference in practice, and it is already one step towards my long-term goal of moving all view rendering to the UI thread (see https://github.com/jesseduffield/lazygit/issues/2974#issuecomment-1729154768). --- pkg/gui/controllers/helpers/refresh_helper.go | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/pkg/gui/controllers/helpers/refresh_helper.go b/pkg/gui/controllers/helpers/refresh_helper.go index 4a61bde1851..6638d150cc5 100644 --- a/pkg/gui/controllers/helpers/refresh_helper.go +++ b/pkg/gui/controllers/helpers/refresh_helper.go @@ -780,22 +780,27 @@ func (self *RefreshHelper) refForLog() string { } func (self *RefreshHelper) refreshView(context types.Context) { - // Re-applying the filter must be done before re-rendering the view, so that - // the filtered list model is up to date for rendering. - self.searchHelper.ReApplyFilter(context) - - self.c.PostRefreshUpdate(context) - - self.c.AfterLayout(func() error { - // Re-applying the search must be done after re-rendering the view though, - // so that the "x of y" status is shown correctly. - // - // Also, it must be done after layout, because otherwise FocusPoint - // hasn't been called yet (see ListContextTrait.FocusLine), which means - // that the scroll position might be such that the entire visible - // content is outside the viewport. And this would cause problems in - // searchModelCommits. - self.searchHelper.ReApplySearch(context) + // refreshView is called from the worker goroutine that drives async + // refreshes, so bounce to the UI thread before mutating view content. + self.c.OnUIThread(func() error { + // Re-applying the filter must be done before re-rendering the view, so that + // the filtered list model is up to date for rendering. + self.searchHelper.ReApplyFilter(context) + + self.c.PostRefreshUpdate(context) + + self.c.AfterLayout(func() error { + // Re-applying the search must be done after re-rendering the view though, + // so that the "x of y" status is shown correctly. + // + // Also, it must be done after layout, because otherwise FocusPoint + // hasn't been called yet (see ListContextTrait.FocusLine), which means + // that the scroll position might be such that the entire visible + // content is outside the viewport. And this would cause problems in + // searchModelCommits. + self.searchHelper.ReApplySearch(context) + return nil + }) return nil }) } From a364a8d75ca5e584d5dfd678fb856a1d9c062657 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Thu, 7 May 2026 08:30:43 +0200 Subject: [PATCH 06/11] Bounce explicit LocalCommits render in refreshBranches to UI thread refreshBranches runs on a worker goroutine and re-renders the commits view directly to refresh the branch-head visualization. As with refreshView, this currently only flushes because HandleRender ends with self.c.Render(). Wrap the explicit HandleRender call (along with the LocalCommitsMutex pair around it) in OnUIThread so it keeps working once Render() is removed. --- pkg/gui/controllers/helpers/refresh_helper.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pkg/gui/controllers/helpers/refresh_helper.go b/pkg/gui/controllers/helpers/refresh_helper.go index 6638d150cc5..cb17ffd43b8 100644 --- a/pkg/gui/controllers/helpers/refresh_helper.go +++ b/pkg/gui/controllers/helpers/refresh_helper.go @@ -532,9 +532,12 @@ func (self *RefreshHelper) refreshBranches(refreshWorktrees bool, keepBranchSele // Need to re-render the commits view because the visualization of local // branch heads might have changed - self.c.Mutexes().LocalCommitsMutex.Lock() - self.c.Contexts().LocalCommits.HandleRender() - self.c.Mutexes().LocalCommitsMutex.Unlock() + self.c.OnUIThread(func() error { + self.c.Mutexes().LocalCommitsMutex.Lock() + self.c.Contexts().LocalCommits.HandleRender() + self.c.Mutexes().LocalCommitsMutex.Unlock() + return nil + }) self.refreshStatus() } From 3d324ed7fbfec2fb15f107c34dc975221db290aa Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Thu, 7 May 2026 08:31:03 +0200 Subject: [PATCH 07/11] Bounce SuggestionsContext.SetSuggestions to UI thread SetSuggestions has two callers: prepareConfirmationPanel calls it directly on the UI thread, while editors.promptEditor and SuggestionsContext.RefreshSuggestions call it via AsyncHandler, which runs the result closure on a worker goroutine. The worker path currently relies on HandleRender's self.c.Render() to flush the view update. Wrap the body in OnUIThread so the worker path stays correct when Render() is removed; for the UI-thread caller the extra bounce is harmless. --- pkg/gui/context/suggestions_context.go | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/pkg/gui/context/suggestions_context.go b/pkg/gui/context/suggestions_context.go index eafe7fb7cda..fb69b34d98e 100644 --- a/pkg/gui/context/suggestions_context.go +++ b/pkg/gui/context/suggestions_context.go @@ -67,10 +67,17 @@ func NewSuggestionsContext( } func (self *SuggestionsContext) SetSuggestions(suggestions []*types.Suggestion) { - self.State.Suggestions = suggestions - self.SetSelection(0) - self.c.ResetViewOrigin(self.GetView()) - self.HandleRender() + // SetSuggestions is invoked from AsyncHandler (a worker goroutine) when + // the prompt input changes, as well as from prepareConfirmationPanel on + // the UI thread. Bounce to the UI thread either way so the worker path + // keeps flushing once HandleRender stops calling Render() itself. + self.c.OnUIThread(func() error { + self.State.Suggestions = suggestions + self.SetSelection(0) + self.c.ResetViewOrigin(self.GetView()) + self.HandleRender() + return nil + }) } func (self *SuggestionsContext) RefreshSuggestions() { From 225bcaa619c4986fcd879d6aa6dca3b6727863c7 Mon Sep 17 00:00:00 2001 From: Antoine Gaudreau Simard Date: Thu, 7 May 2026 19:52:46 -0400 Subject: [PATCH 08/11] Bounce commit files selection render to UI thread The redraw of the selection color (using ) would be tied to the spinner drawing. To reproduce, having a high spinner refresh rate and toggling a file would see a delay equivalent to the time spinner refresh rate. --- pkg/gui/controllers/commits_files_controller.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/gui/controllers/commits_files_controller.go b/pkg/gui/controllers/commits_files_controller.go index 7cde6c15da2..b12819c398b 100644 --- a/pkg/gui/controllers/commits_files_controller.go +++ b/pkg/gui/controllers/commits_files_controller.go @@ -476,7 +476,11 @@ func (self *CommitFilesController) toggleForPatch(selectedNodes []*filetree.Comm self.c.Git().Patch.PatchBuilder.Reset() } - self.c.PostRefreshUpdate(self.context()) + self.c.OnUIThread(func() error { + self.c.PostRefreshUpdate(self.context()) + return nil + }) + return nil }) } From 86758622289bafb2780844f0c40acdd734e9ed1a Mon Sep 17 00:00:00 2001 From: Antoine Gaudreau Simard Date: Thu, 7 May 2026 20:55:00 -0400 Subject: [PATCH 09/11] Bounce setGithubPullRequests' Branches render to UI thread Fixes an issue mostly noticeable using the go -race mode, this resolves them. --- pkg/gui/controllers/helpers/refresh_helper.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/gui/controllers/helpers/refresh_helper.go b/pkg/gui/controllers/helpers/refresh_helper.go index cb17ffd43b8..77de9ca4a0f 100644 --- a/pkg/gui/controllers/helpers/refresh_helper.go +++ b/pkg/gui/controllers/helpers/refresh_helper.go @@ -949,7 +949,11 @@ func (self *RefreshHelper) setGithubPullRequests(authToken string, baseRemote *m self.savePullRequestsToCache(prs) self.rebuildPullRequestsMap() - self.c.PostRefreshUpdate(self.c.Contexts().Branches) + self.c.OnUIThread(func() error { + self.c.PostRefreshUpdate(self.c.Contexts().Branches) + return nil + }) + return nil } From 76211eea68fd45c78180108198688cf76d6ffa0b Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Thu, 7 May 2026 08:43:10 +0200 Subject: [PATCH 10/11] Remove Render() from ListContextTrait.HandleRender self.c.Render() at the end of HandleRender was there to schedule a gocui Update tick so the view content modified above would actually get drawn. For UI-thread callers (the great majority -- keybinding handlers, the layout function itself, popup resize, etc.) this was unnecessary work, since gocui already runs a layout/redraw cycle after every event. SimpleContext.HandleRender doesn't call Render() either, so this aligns the two implementations. The few callers that drove HandleRender from a worker goroutine and relied on Render() for the flush were wrapped in OnUIThread in the preceding commits, so the implicit Render is no longer needed. Also, Render() being called *before* setFooter() looks like it might have been a theoretical race; this is no longer an issue now. --- pkg/gui/context/list_context_trait.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/gui/context/list_context_trait.go b/pkg/gui/context/list_context_trait.go index 98833fdb2f7..597fc99df29 100644 --- a/pkg/gui/context/list_context_trait.go +++ b/pkg/gui/context/list_context_trait.go @@ -124,7 +124,6 @@ func (self *ListContextTrait) HandleRender() { content := self.renderLines(-1, -1) self.GetViewTrait().SetContent(content) } - self.c.Render() self.setFooter() } From 4734bab896ad8bfe9791bb0ed32a00495c179200 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Thu, 7 May 2026 19:28:20 +0200 Subject: [PATCH 11/11] Improve performance of inline status spinner Now that HandleRender no longer does an implicit Render(), we can use it inside OnUIThreadContentOnly to save performance, on the assumption that rerendering a view that contains an inline spinner never changes the layout. --- pkg/gui/controllers/helpers/inline_status_helper.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/gui/controllers/helpers/inline_status_helper.go b/pkg/gui/controllers/helpers/inline_status_helper.go index 13c1128116e..02afcdd501e 100644 --- a/pkg/gui/controllers/helpers/inline_status_helper.go +++ b/pkg/gui/controllers/helpers/inline_status_helper.go @@ -149,7 +149,7 @@ func (self *InlineStatusHelper) stop(opts InlineStatusOpts) { } func (self *InlineStatusHelper) renderContext(contextKey types.ContextKey) { - self.c.OnUIThread(func() error { + self.c.OnUIThreadContentOnly(func() error { self.c.ContextForKey(contextKey).HandleRender() return nil })