detached 3af1508

fix(daemon): read the client theme under the pane lock when theming a new pane

commit
3af150874d24681b653c4a03c9b5edd37ebc9d40
author
brice · · 2026-10-08 09:31 UTC
committer
brice · · 2026-10-08 09:31 UTC
parent
ad96dc0
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
2 files · +10 -7
M internal/usecase/daemon/floating_spare.go +9 -3
@@ -160,10 +160,16 @@ func (d *Daemon) applySessionThemeToPane(sess *session, p *pane) {
160160 if len(attachments) == 0 {
161161 return
162162 }
163- t := d.effectiveTheme(attachments[0].getClientTheme())
163+ applyClientThemeToPane(d, attachments[0], p)
164+}
165+
166+// applyClientThemeToPane reads ac's theme while holding p.mu, so a concurrent
167+// theme application either is seen here or writes the pane after it; a stale
168+// theme can never overwrite a newer one. ac.themeMu is a leaf lock.
169+func applyClientThemeToPane(d *Daemon, ac *attachedClient, p *pane) {
164170 p.mu.Lock()
165- applyPaneThemeLocked(p, t, false)
166- p.mu.Unlock()
171+ defer p.mu.Unlock()
172+ applyPaneThemeLocked(p, d.effectiveTheme(ac.getClientTheme()), false)
167173 }
168174
169175 // floatingSparePane returns the session's unclaimed spare pane, if any.
@@ -160,10 +160,16 @@ func (d *Daemon) applySessionThemeToPane(sess *session, p *pane) {
160 if len(attachments) == 0 {160 if len(attachments) == 0 {
161 return161 return
162 }162 }
163- t := d.effectiveTheme(attachments[0].getClientTheme())163+ applyClientThemeToPane(d, attachments[0], p)
164+}
165+
166+// applyClientThemeToPane reads ac's theme while holding p.mu, so a concurrent
167+// theme application either is seen here or writes the pane after it; a stale
168+// theme can never overwrite a newer one. ac.themeMu is a leaf lock.
169+func applyClientThemeToPane(d *Daemon, ac *attachedClient, p *pane) {
164 p.mu.Lock()170 p.mu.Lock()
165- applyPaneThemeLocked(p, t, false)171+ defer p.mu.Unlock()
166- p.mu.Unlock()172+ applyPaneThemeLocked(p, d.effectiveTheme(ac.getClientTheme()), false)
167 }173 }
168 174
169 // floatingSparePane returns the session's unclaimed spare pane, if any.175 // floatingSparePane returns the session's unclaimed spare pane, if any.
M internal/usecase/daemon/session.go +1 -4
@@ -726,14 +726,11 @@ func (d *Daemon) createTabForAttachment(sess *session, ac *attachedClient, _ dom
726726 themeClient = attachments[0]
727727 }
728728 if themeClient != nil {
729- t := d.effectiveTheme(themeClient.getClientTheme())
730729 tb.mu.Lock()
731730 p := tb.focusedPane()
732731 tb.mu.Unlock()
733732 if p != nil {
734- p.mu.Lock()
735- applyPaneThemeLocked(p, t, false)
736- p.mu.Unlock()
733+ applyClientThemeToPane(d, themeClient, p)
737734 }
738735 }
739736 d.mu.Lock()
@@ -726,14 +726,11 @@ func (d *Daemon) createTabForAttachment(sess *session, ac *attachedClient, _ dom
726 themeClient = attachments[0]726 themeClient = attachments[0]
727 }727 }
728 if themeClient != nil {728 if themeClient != nil {
729- t := d.effectiveTheme(themeClient.getClientTheme())
730 tb.mu.Lock()729 tb.mu.Lock()
731 p := tb.focusedPane()730 p := tb.focusedPane()
732 tb.mu.Unlock()731 tb.mu.Unlock()
733 if p != nil {732 if p != nil {
734- p.mu.Lock()733+ applyClientThemeToPane(d, themeClient, p)
735- applyPaneThemeLocked(p, t, false)
736- p.mu.Unlock()
737 }734 }
738 }735 }
739 d.mu.Lock()736 d.mu.Lock()

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 12

Tags 11

↑↓ moveenter openesc close