detached 93cde2a

content: apply simplify cleanup

commit
93cde2a4cc302183afda5ac716b4f9194d589057
author
brice · · 2026-04-22 13:16 UTC
parent
c21009b
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
4 files · +54 -22
M internal/infrastructure/cef/webview.go +4 -0
@@ -952,12 +952,16 @@ func (wv *WebView) Destroy() {
952952 wv.mu.Lock()
953953 wv.closeCallbacks = nil
954954 wv.navigationCallbacks = nil
955+ wv.openerMessageCallbacks = nil
956+ wv.openerNavigationCallbacks = nil
955957 wv.popupReadyToShow = nil
956958 wv.pendingNativePopups = nil
957959 wv.nativePopupParent = nil
958960 wv.nativePopupCandidate = false
959961 wv.nativePopupID = 0
960962 wv.nativePopupFallbackStarted = false
963+ wv.popupOpenerBridgeParent = nil
964+ wv.popupOpenerBridgeParentURI = ""
961965 wv.mu.Unlock()
962966 wv.stopNativePopupFallbackTimer()
963967 wv.cancelSelectionDebounce()
@@ -952,12 +952,16 @@ func (wv *WebView) Destroy() {
952 wv.mu.Lock()952 wv.mu.Lock()
953 wv.closeCallbacks = nil953 wv.closeCallbacks = nil
954 wv.navigationCallbacks = nil954 wv.navigationCallbacks = nil
955+ wv.openerMessageCallbacks = nil
956+ wv.openerNavigationCallbacks = nil
955 wv.popupReadyToShow = nil957 wv.popupReadyToShow = nil
956 wv.pendingNativePopups = nil958 wv.pendingNativePopups = nil
957 wv.nativePopupParent = nil959 wv.nativePopupParent = nil
958 wv.nativePopupCandidate = false960 wv.nativePopupCandidate = false
959 wv.nativePopupID = 0961 wv.nativePopupID = 0
960 wv.nativePopupFallbackStarted = false962 wv.nativePopupFallbackStarted = false
963+ wv.popupOpenerBridgeParent = nil
964+ wv.popupOpenerBridgeParentURI = ""
961 wv.mu.Unlock()965 wv.mu.Unlock()
962 wv.stopNativePopupFallbackTimer()966 wv.stopNativePopupFallbackTimer()
963 wv.cancelSelectionDebounce()967 wv.cancelSelectionDebounce()
M internal/infrastructure/cef/webview_popup_proxy.go +15 -10
@@ -22,7 +22,10 @@ func (wv *WebView) syntheticPopupState(proxyID string) *syntheticPopupState {
2222
2323 wv.syntheticPopupMu.Lock()
2424 defer wv.syntheticPopupMu.Unlock()
25- return wv.syntheticPopupStateLocked(proxyID)
25+ if wv.syntheticPopups == nil {
26+ return nil
27+ }
28+ return wv.syntheticPopups[proxyID]
2629 }
2730
2831 func (wv *WebView) syntheticPopupStateLocked(proxyID string) *syntheticPopupState {
@@ -134,12 +137,8 @@ func (wv *WebView) handleSyntheticPopupNavigate(proxyID, targetURL string) {
134137 }
135138
136139 trimmedURI := strings.TrimSpace(targetURL)
137- state := wv.syntheticPopupState(proxyID)
138- if state == nil {
139- return
140- }
141-
142140 wv.syntheticPopupMu.Lock()
141+ state := wv.syntheticPopupStateLocked(proxyID)
143142 if state.Closed {
144143 wv.syntheticPopupMu.Unlock()
145144 return
@@ -175,12 +174,18 @@ func (wv *WebView) handleSyntheticPopupClose(proxyID string) {
175174 }
176175
177176 wv.syntheticPopupMu.Lock()
178- state := wv.syntheticPopupStateLocked(proxyID)
177+ if wv.syntheticPopups == nil {
178+ wv.syntheticPopupMu.Unlock()
179+ return
180+ }
181+ state := wv.syntheticPopups[proxyID]
182+ if state == nil {
183+ wv.syntheticPopupMu.Unlock()
184+ return
185+ }
179186 state.Closed = true
180187 popupWV := state.WebView
181- if popupWV != nil {
182- delete(wv.syntheticPopups, proxyID)
183- }
188+ delete(wv.syntheticPopups, proxyID)
184189 wv.syntheticPopupMu.Unlock()
185190 if popupWV == nil {
186191 return
@@ -22,7 +22,10 @@ func (wv *WebView) syntheticPopupState(proxyID string) *syntheticPopupState {
22 22
23 wv.syntheticPopupMu.Lock()23 wv.syntheticPopupMu.Lock()
24 defer wv.syntheticPopupMu.Unlock()24 defer wv.syntheticPopupMu.Unlock()
25- return wv.syntheticPopupStateLocked(proxyID)25+ if wv.syntheticPopups == nil {
26+ return nil
27+ }
28+ return wv.syntheticPopups[proxyID]
26 }29 }
27 30
28 func (wv *WebView) syntheticPopupStateLocked(proxyID string) *syntheticPopupState {31 func (wv *WebView) syntheticPopupStateLocked(proxyID string) *syntheticPopupState {
@@ -134,12 +137,8 @@ func (wv *WebView) handleSyntheticPopupNavigate(proxyID, targetURL string) {
134 }137 }
135 138
136 trimmedURI := strings.TrimSpace(targetURL)139 trimmedURI := strings.TrimSpace(targetURL)
137- state := wv.syntheticPopupState(proxyID)
138- if state == nil {
139- return
140- }
141-
142 wv.syntheticPopupMu.Lock()140 wv.syntheticPopupMu.Lock()
141+ state := wv.syntheticPopupStateLocked(proxyID)
143 if state.Closed {142 if state.Closed {
144 wv.syntheticPopupMu.Unlock()143 wv.syntheticPopupMu.Unlock()
145 return144 return
@@ -175,12 +174,18 @@ func (wv *WebView) handleSyntheticPopupClose(proxyID string) {
175 }174 }
176 175
177 wv.syntheticPopupMu.Lock()176 wv.syntheticPopupMu.Lock()
178- state := wv.syntheticPopupStateLocked(proxyID)177+ if wv.syntheticPopups == nil {
178+ wv.syntheticPopupMu.Unlock()
179+ return
180+ }
181+ state := wv.syntheticPopups[proxyID]
182+ if state == nil {
183+ wv.syntheticPopupMu.Unlock()
184+ return
185+ }
179 state.Closed = true186 state.Closed = true
180 popupWV := state.WebView187 popupWV := state.WebView
181- if popupWV != nil {188+ delete(wv.syntheticPopups, proxyID)
182- delete(wv.syntheticPopups, proxyID)
183- }
184 wv.syntheticPopupMu.Unlock()189 wv.syntheticPopupMu.Unlock()
185 if popupWV == nil {190 if popupWV == nil {
186 return191 return
M internal/ui/coordinator/content/coordinator.go +23 -2
@@ -29,8 +29,9 @@ type Coordinator struct {
2929 settingsApplier port.SettingsApplier // optional: nil if engine doesn't support
3030 filterApplier port.FilterApplier // optional: nil if engine doesn't support
3131
32- webViews map[entity.PaneID]port.WebView
33- webViewsMu sync.RWMutex
32+ webViews map[entity.PaneID]port.WebView
33+ webViewPaneIDs map[port.WebViewID]entity.PaneID
34+ webViewsMu sync.RWMutex
3435
3536 activePaneOverride entity.PaneID
3637 activePaneOverrideMu sync.RWMutex
@@ -165,6 +166,7 @@ func NewCoordinator(
165166 zoomUC: zoomUC,
166167 permissionUC: permissionUC,
167168 webViews: make(map[entity.PaneID]port.WebView),
169+ webViewPaneIDs: make(map[port.WebViewID]entity.PaneID),
168170 paneTitles: make(map[entity.PaneID]string),
169171 navOrigins: make(map[entity.PaneID]string),
170172 pendingReveal: make(map[entity.PaneID]bool),
@@ -311,7 +313,16 @@ func (c *Coordinator) getWebViewLocked(paneID entity.PaneID) port.WebView {
311313
312314 func (c *Coordinator) setWebViewLocked(paneID entity.PaneID, wv port.WebView) {
313315 c.webViewsMu.Lock()
316+ if c.webViews == nil {
317+ c.webViews = make(map[entity.PaneID]port.WebView)
318+ }
319+ if existing := c.webViews[paneID]; existing != nil && c.webViewPaneIDs != nil {
320+ delete(c.webViewPaneIDs, existing.ID())
321+ }
314322 c.webViews[paneID] = wv
323+ if wv != nil && c.webViewPaneIDs != nil {
324+ c.webViewPaneIDs[wv.ID()] = paneID
325+ }
315326 c.webViewsMu.Unlock()
316327 }
317328
@@ -320,5 +331,15 @@ func (c *Coordinator) deleteWebViewLocked(paneID entity.PaneID) port.WebView {
320331 defer c.webViewsMu.Unlock()
321332 wv := c.webViews[paneID]
322333 delete(c.webViews, paneID)
334+ if wv != nil && c.webViewPaneIDs != nil {
335+ delete(c.webViewPaneIDs, wv.ID())
336+ }
323337 return wv
324338 }
339+
340+func (c *Coordinator) paneIDByWebViewID(webViewID port.WebViewID) (entity.PaneID, bool) {
341+ c.webViewsMu.RLock()
342+ defer c.webViewsMu.RUnlock()
343+ paneID, ok := c.webViewPaneIDs[webViewID]
344+ return paneID, ok
345+}
@@ -29,8 +29,9 @@ type Coordinator struct {
29 settingsApplier port.SettingsApplier // optional: nil if engine doesn't support29 settingsApplier port.SettingsApplier // optional: nil if engine doesn't support
30 filterApplier port.FilterApplier // optional: nil if engine doesn't support30 filterApplier port.FilterApplier // optional: nil if engine doesn't support
31 31
32- webViews map[entity.PaneID]port.WebView32+ webViews map[entity.PaneID]port.WebView
33- webViewsMu sync.RWMutex33+ webViewPaneIDs map[port.WebViewID]entity.PaneID
34+ webViewsMu sync.RWMutex
34 35
35 activePaneOverride entity.PaneID36 activePaneOverride entity.PaneID
36 activePaneOverrideMu sync.RWMutex37 activePaneOverrideMu sync.RWMutex
@@ -165,6 +166,7 @@ func NewCoordinator(
165 zoomUC: zoomUC,166 zoomUC: zoomUC,
166 permissionUC: permissionUC,167 permissionUC: permissionUC,
167 webViews: make(map[entity.PaneID]port.WebView),168 webViews: make(map[entity.PaneID]port.WebView),
169+ webViewPaneIDs: make(map[port.WebViewID]entity.PaneID),
168 paneTitles: make(map[entity.PaneID]string),170 paneTitles: make(map[entity.PaneID]string),
169 navOrigins: make(map[entity.PaneID]string),171 navOrigins: make(map[entity.PaneID]string),
170 pendingReveal: make(map[entity.PaneID]bool),172 pendingReveal: make(map[entity.PaneID]bool),
@@ -311,7 +313,16 @@ func (c *Coordinator) getWebViewLocked(paneID entity.PaneID) port.WebView {
311 313
312 func (c *Coordinator) setWebViewLocked(paneID entity.PaneID, wv port.WebView) {314 func (c *Coordinator) setWebViewLocked(paneID entity.PaneID, wv port.WebView) {
313 c.webViewsMu.Lock()315 c.webViewsMu.Lock()
316+ if c.webViews == nil {
317+ c.webViews = make(map[entity.PaneID]port.WebView)
318+ }
319+ if existing := c.webViews[paneID]; existing != nil && c.webViewPaneIDs != nil {
320+ delete(c.webViewPaneIDs, existing.ID())
321+ }
314 c.webViews[paneID] = wv322 c.webViews[paneID] = wv
323+ if wv != nil && c.webViewPaneIDs != nil {
324+ c.webViewPaneIDs[wv.ID()] = paneID
325+ }
315 c.webViewsMu.Unlock()326 c.webViewsMu.Unlock()
316 }327 }
317 328
@@ -320,5 +331,15 @@ func (c *Coordinator) deleteWebViewLocked(paneID entity.PaneID) port.WebView {
320 defer c.webViewsMu.Unlock()331 defer c.webViewsMu.Unlock()
321 wv := c.webViews[paneID]332 wv := c.webViews[paneID]
322 delete(c.webViews, paneID)333 delete(c.webViews, paneID)
334+ if wv != nil && c.webViewPaneIDs != nil {
335+ delete(c.webViewPaneIDs, wv.ID())
336+ }
323 return wv337 return wv
324 }338 }
339+
340+func (c *Coordinator) paneIDByWebViewID(webViewID port.WebViewID) (entity.PaneID, bool) {
341+ c.webViewsMu.RLock()
342+ defer c.webViewsMu.RUnlock()
343+ paneID, ok := c.webViewPaneIDs[webViewID]
344+ return paneID, ok
345+}
M internal/ui/coordinator/content/popup.go +12 -10
@@ -704,18 +704,20 @@ func (c *Coordinator) handlePopupClose(ctx context.Context, popupID port.WebView
704704 return
705705 }
706706
707- // Find pane by WebView ID
708- var paneID entity.PaneID
709- c.webViewsMu.RLock()
710- for pid, wv := range c.webViews {
711- if wv != nil && wv.ID() == popupID {
712- paneID = pid
713- break
707+ // Find pane by WebView ID.
708+ paneID, ok := c.paneIDByWebViewID(popupID)
709+ if (!ok || paneID == "") && popupID != 0 {
710+ c.webViewsMu.RLock()
711+ for pid, wv := range c.webViews {
712+ if wv != nil && wv.ID() == popupID {
713+ paneID = pid
714+ ok = true
715+ break
716+ }
714717 }
718+ c.webViewsMu.RUnlock()
715719 }
716- c.webViewsMu.RUnlock()
717-
718- if paneID == "" {
720+ if !ok || paneID == "" {
719721 c.handlePopupOAuthClose(ctx, popupID)
720722 c.clearReusableNamedPopupByWebViewID(popupID)
721723 log.Warn().Msg("popup close: could not find pane for webview")
@@ -704,18 +704,20 @@ func (c *Coordinator) handlePopupClose(ctx context.Context, popupID port.WebView
704 return704 return
705 }705 }
706 706
707- // Find pane by WebView ID707+ // Find pane by WebView ID.
708- var paneID entity.PaneID708+ paneID, ok := c.paneIDByWebViewID(popupID)
709- c.webViewsMu.RLock()709+ if (!ok || paneID == "") && popupID != 0 {
710- for pid, wv := range c.webViews {710+ c.webViewsMu.RLock()
711- if wv != nil && wv.ID() == popupID {711+ for pid, wv := range c.webViews {
712- paneID = pid712+ if wv != nil && wv.ID() == popupID {
713- break713+ paneID = pid
714+ ok = true
715+ break
716+ }
714 }717 }
718+ c.webViewsMu.RUnlock()
715 }719 }
716- c.webViewsMu.RUnlock()720+ if !ok || paneID == "" {
717-
718- if paneID == "" {
719 c.handlePopupOAuthClose(ctx, popupID)721 c.handlePopupOAuthClose(ctx, popupID)
720 c.clearReusableNamedPopupByWebViewID(popupID)722 c.clearReusableNamedPopupByWebViewID(popupID)
721 log.Warn().Msg("popup close: could not find pane for webview")723 log.Warn().Msg("popup close: could not find pane for webview")

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