detached 0a598e1

fix(accent-picker): resolve deadlock when selecting accent with Enter/Escape

commit
0a598e1271d4ea015b1ae5b9af739dc4f798cebc
author
brice · · 2026-01-08 10:48 UTC
committer
brice · · 2026-01-08 13:04 UTC
parent
172cc2c
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
2 files · +8 -6
M CHANGELOG.md +2 -0
@@ -13,6 +13,8 @@ All notable changes to this project will be documented in this file.
1313 - **Navigation shortcuts**: `Ctrl+Left/Right` now triggers back/forward navigation, `Alt+Left/Right` moves focus between panes. Fixes #98.
1414
1515 ### Fixed
16+- **Accent picker freeze**: Fixed UI freeze when selecting an accent by pressing Enter or Escape. The deadlock was caused by the callback (which calls `Hide()`) being invoked while holding the mutex that `Hide()` also needs to acquire.
17+- **Stacked pane title mismatch**: Fixed title bar showing wrong page title after closing panes in a stack. Refactored stacked pane management to use proper use cases, ensuring domain model stays in sync with UI.
1618 - **Flatpak favicons**: Fixed favicons not displaying in omnibox for Flatpak installs by ensuring cache directory exists before saving. Fixes #105.
1719 - **Flatpak desktop launcher**: Fixed .desktop file not launching application from app launchers by adding locale environment variables. Fixes #104.
1820 - **Flatpak update notifications**: Skip GitHub update checks for Flatpak and AUR installs (use package manager instead). Fixes #107.
@@ -13,6 +13,8 @@ All notable changes to this project will be documented in this file.
13 - **Navigation shortcuts**: `Ctrl+Left/Right` now triggers back/forward navigation, `Alt+Left/Right` moves focus between panes. Fixes #98.13 - **Navigation shortcuts**: `Ctrl+Left/Right` now triggers back/forward navigation, `Alt+Left/Right` moves focus between panes. Fixes #98.
14 14
15 ### Fixed15 ### Fixed
16+- **Accent picker freeze**: Fixed UI freeze when selecting an accent by pressing Enter or Escape. The deadlock was caused by the callback (which calls `Hide()`) being invoked while holding the mutex that `Hide()` also needs to acquire.
17+- **Stacked pane title mismatch**: Fixed title bar showing wrong page title after closing panes in a stack. Refactored stacked pane management to use proper use cases, ensuring domain model stays in sync with UI.
16 - **Flatpak favicons**: Fixed favicons not displaying in omnibox for Flatpak installs by ensuring cache directory exists before saving. Fixes #105.18 - **Flatpak favicons**: Fixed favicons not displaying in omnibox for Flatpak installs by ensuring cache directory exists before saving. Fixes #105.
17 - **Flatpak desktop launcher**: Fixed .desktop file not launching application from app launchers by adding locale environment variables. Fixes #104.19 - **Flatpak desktop launcher**: Fixed .desktop file not launching application from app launchers by adding locale environment variables. Fixes #104.
18 - **Flatpak update notifications**: Skip GitHub update checks for Flatpak and AUR installs (use package manager instead). Fixes #107.20 - **Flatpak update notifications**: Skip GitHub update checks for Flatpak and AUR installs (use package manager instead). Fixes #107.
M internal/ui/component/accent_picker.go +6 -6
@@ -177,9 +177,9 @@ func (ap *AccentPicker) selectAccent(index int) {
177177 return
178178 }
179179 accent := ap.accents[index]
180- // Invoke callback while holding lock to avoid race with Hide() clearing callbacks
181- ap.selectedCb(accent)
182- ap.mu.Unlock()
180+ cb := ap.selectedCb // Copy callback reference before unlocking
181+ ap.mu.Unlock() // Release lock BEFORE calling callback to avoid deadlock
182+ cb(accent) // Callback may call Hide() which needs to acquire ap.mu
183183 }
184184
185185 // cancel cancels the accent picker.
@@ -189,9 +189,9 @@ func (ap *AccentPicker) cancel() {
189189 ap.mu.Unlock()
190190 return
191191 }
192- // Invoke callback while holding lock to avoid race with Hide() clearing callbacks
193- ap.cancelCb()
194- ap.mu.Unlock()
192+ cb := ap.cancelCb // Copy callback reference before unlocking
193+ ap.mu.Unlock() // Release lock BEFORE calling callback to avoid deadlock
194+ cb() // Callback may call Hide() which needs to acquire ap.mu
195195 }
196196
197197 // setupKeyboardHandling sets up key event handling for navigation.
@@ -177,9 +177,9 @@ func (ap *AccentPicker) selectAccent(index int) {
177 return177 return
178 }178 }
179 accent := ap.accents[index]179 accent := ap.accents[index]
180- // Invoke callback while holding lock to avoid race with Hide() clearing callbacks180+ cb := ap.selectedCb // Copy callback reference before unlocking
181- ap.selectedCb(accent)181+ ap.mu.Unlock() // Release lock BEFORE calling callback to avoid deadlock
182- ap.mu.Unlock()182+ cb(accent) // Callback may call Hide() which needs to acquire ap.mu
183 }183 }
184 184
185 // cancel cancels the accent picker.185 // cancel cancels the accent picker.
@@ -189,9 +189,9 @@ func (ap *AccentPicker) cancel() {
189 ap.mu.Unlock()189 ap.mu.Unlock()
190 return190 return
191 }191 }
192- // Invoke callback while holding lock to avoid race with Hide() clearing callbacks192+ cb := ap.cancelCb // Copy callback reference before unlocking
193- ap.cancelCb()193+ ap.mu.Unlock() // Release lock BEFORE calling callback to avoid deadlock
194- ap.mu.Unlock()194+ cb() // Callback may call Hide() which needs to acquire ap.mu
195 }195 }
196 196
197 // setupKeyboardHandling sets up key event handling for navigation.197 // setupKeyboardHandling sets up key event handling for navigation.

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 8

Tags 42

↑↓ moveenter openesc close