fix(daemon): skip redundant title recompute, refresh detached pickers
setDisplayFallback now early-returns under p.mu when the fallback is unchanged, avoiding two displayTitleLocked() compositions on the render hot path for a value (d.shell's basename) that never changes at runtime. pickerViews refreshes every live session's focused-pane titles before building views, so detached sessions no longer show stale process names in the session picker (previously only paint() refreshed titles, and only for the attached session).
- commit
- 3601ffd90676d40872ae5c13858a431d23d3c2d8
- author
- brice · · 2026-07-11 10:15 UTC
- parent
- d3df67b
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
3 files · +9 -0
M internal/usecase/daemon/pane_titles.go +4 -0
| @@ -47,6 +47,10 @@ func (p *pane) displayTitleLocked() string { | |||
| 47 | 47 | // cached composition unless that fallback changes the displayed title. | |
| 48 | 48 | func (p *pane) setDisplayFallback(fallback string) { | |
| 49 | 49 | p.mu.Lock() | |
| 50 | + | if p.title.displayFallback == fallback { | |
| 51 | + | p.mu.Unlock() | |
| 52 | + | return | |
| 53 | + | } | |
| 50 | 54 | oldTitle := p.displayTitleLocked() | |
| 51 | 55 | p.title.displayFallback = fallback | |
| 52 | 56 | if oldTitle != p.displayTitleLocked() { | |
| @@ -47,6 +47,10 @@ func (p *pane) displayTitleLocked() string { | |||||
| 47 | // cached composition unless that fallback changes the displayed title. | 47 | // cached composition unless that fallback changes the displayed title. | ||
| 48 | func (p *pane) setDisplayFallback(fallback string) { | 48 | func (p *pane) setDisplayFallback(fallback string) { | ||
| 49 | p.mu.Lock() | 49 | p.mu.Lock() | ||
| 50 | + | if p.title.displayFallback == fallback { | |||
| 51 | + | p.mu.Unlock() | |||
| 52 | + | return | |||
| 53 | + | } | |||
| 50 | oldTitle := p.displayTitleLocked() | 54 | oldTitle := p.displayTitleLocked() | ||
| 51 | p.title.displayFallback = fallback | 55 | p.title.displayFallback = fallback | ||
| 52 | if oldTitle != p.displayTitleLocked() { | 56 | if oldTitle != p.displayTitleLocked() { | ||
M internal/usecase/daemon/picker.go +4 -0
| @@ -63,6 +63,10 @@ func (d *Daemon) pickerViews(cur *session) ([]picker.SessionView, int) { | |||
| 63 | 63 | sort.Slice(sessions, func(i, j int) bool { return sessions[i].name < sessions[j].name }) | |
| 64 | 64 | sort.Slice(stopped, func(i, j int) bool { return stopped[i].name < stopped[j].name }) | |
| 65 | 65 | ||
| 66 | + | for _, s := range sessions { | |
| 67 | + | d.refreshSessionFocusedTitles(s) | |
| 68 | + | } | |
| 69 | + | ||
| 66 | 70 | views := make([]picker.SessionView, 0, len(sessions)+len(stopped)) | |
| 67 | 71 | curTab := 0 | |
| 68 | 72 | for _, s := range sessions { | |
| @@ -63,6 +63,10 @@ func (d *Daemon) pickerViews(cur *session) ([]picker.SessionView, int) { | |||||
| 63 | sort.Slice(sessions, func(i, j int) bool { return sessions[i].name < sessions[j].name }) | 63 | sort.Slice(sessions, func(i, j int) bool { return sessions[i].name < sessions[j].name }) | ||
| 64 | sort.Slice(stopped, func(i, j int) bool { return stopped[i].name < stopped[j].name }) | 64 | sort.Slice(stopped, func(i, j int) bool { return stopped[i].name < stopped[j].name }) | ||
| 65 | 65 | ||||
| 66 | + | for _, s := range sessions { | |||
| 67 | + | d.refreshSessionFocusedTitles(s) | |||
| 68 | + | } | |||
| 69 | + | ||||
| 66 | views := make([]picker.SessionView, 0, len(sessions)+len(stopped)) | 70 | views := make([]picker.SessionView, 0, len(sessions)+len(stopped)) | ||
| 67 | curTab := 0 | 71 | curTab := 0 | ||
| 68 | for _, s := range sessions { | 72 | for _, s := range sessions { | ||
M internal/usecase/daemon/picker_test.go +1 -0
| @@ -72,6 +72,7 @@ func TestPickerViewsComposesFocusedPaneTitleWithAttentionSuffix(t *testing.T) { | |||
| 72 | 72 | defer releasePTY1() | |
| 73 | 73 | defer releasePTY2() | |
| 74 | 74 | d, sess, _, _ := newManualSessionWithPTYs(t, p1, p2) | |
| 75 | + | d.shell = "/bin/sh" | |
| 75 | 76 | sess.tabs[1].name = "logs" | |
| 76 | 77 | ||
| 77 | 78 | pane0 := sess.tabs[0].focusedPane() | |
| @@ -72,6 +72,7 @@ func TestPickerViewsComposesFocusedPaneTitleWithAttentionSuffix(t *testing.T) { | |||||
| 72 | defer releasePTY1() | 72 | defer releasePTY1() | ||
| 73 | defer releasePTY2() | 73 | defer releasePTY2() | ||
| 74 | d, sess, _, _ := newManualSessionWithPTYs(t, p1, p2) | 74 | d, sess, _, _ := newManualSessionWithPTYs(t, p1, p2) | ||
| 75 | + | d.shell = "/bin/sh" | |||
| 75 | sess.tabs[1].name = "logs" | 76 | sess.tabs[1].name = "logs" | ||
| 76 | 77 | ||||
| 77 | pane0 := sess.tabs[0].focusedPane() | 78 | pane0 := sess.tabs[0].focusedPane() | ||