detached 635dc69

fix(cef): honor invisible cursor requests from web content (#424)

Map CEF CursorTypeCtNone to the "none" cursor name so sites that hide
their cursor (e.g. video players during idle playback) propagate through
the GTK bridge instead of falling back to "default".

Add TestCEFCursorToGDKName covering none, pointer, hand, ibeam, and the
unknown-type fallback.
commit
635dc6958e3383a9d60ec686653d4c2ff97688d4
author
bnema · · 2026-09-10 04:09 UTC
committer
GitHub · · 2026-09-10 04:09 UTC
parent
69f40e2
signature
GPG UnverifiedB5690EEEBB952194 The key is unknown
2 files · +21 -0
M internal/infrastructure/cef/webview_handlers.go +1 -0
@@ -1392,6 +1392,7 @@ var cefCursorNames = map[purecef.CursorType]string{
13921392 purecef.CursorTypeCtGrabbing: "grabbing",
13931393 purecef.CursorTypeCtZoomin: "zoom-in",
13941394 purecef.CursorTypeCtZoomout: "zoom-out",
1395+ purecef.CursorTypeCtNone: "none",
13951396 }
13961397
13971398 // cefCursorToGDKName maps a CEF cursor type to a GDK/CSS cursor name.
@@ -1392,6 +1392,7 @@ var cefCursorNames = map[purecef.CursorType]string{
1392 purecef.CursorTypeCtGrabbing: "grabbing",1392 purecef.CursorTypeCtGrabbing: "grabbing",
1393 purecef.CursorTypeCtZoomin: "zoom-in",1393 purecef.CursorTypeCtZoomin: "zoom-in",
1394 purecef.CursorTypeCtZoomout: "zoom-out",1394 purecef.CursorTypeCtZoomout: "zoom-out",
1395+ purecef.CursorTypeCtNone: "none",
1395 }1396 }
1396 1397
1397 // cefCursorToGDKName maps a CEF cursor type to a GDK/CSS cursor name.1398 // cefCursorToGDKName maps a CEF cursor type to a GDK/CSS cursor name.
M internal/infrastructure/cef/webview_handlers_test.go +20 -0
@@ -679,3 +679,23 @@ type selectionRenderHandlerForTest struct{ wv *WebView }
679679 func (h *selectionRenderHandlerForTest) OnTextSelectionChanged(_ purecef.Browser, selectedText string, _ *purecef.Range) {
680680 handleRenderTextSelectionChanged(h.wv, selectedText)
681681 }
682+
683+func TestCEFCursorToGDKName(t *testing.T) {
684+ tests := []struct {
685+ name string
686+ cursor purecef.CursorType
687+ expected string
688+ }{
689+ {name: "none maps to invisible cursor", cursor: purecef.CursorTypeCtNone, expected: "none"},
690+ {name: "pointer maps to default", cursor: purecef.CursorTypeCtPointer, expected: "default"},
691+ {name: "hand maps to pointer", cursor: purecef.CursorTypeCtHand, expected: "pointer"},
692+ {name: "ibeam maps to text", cursor: purecef.CursorTypeCtIbeam, expected: "text"},
693+ {name: "unknown falls back to default", cursor: purecef.CursorType(-1), expected: "default"},
694+ }
695+
696+ for _, tt := range tests {
697+ t.Run(tt.name, func(t *testing.T) {
698+ require.Equal(t, tt.expected, cefCursorToGDKName(tt.cursor))
699+ })
700+ }
701+}
@@ -679,3 +679,23 @@ type selectionRenderHandlerForTest struct{ wv *WebView }
679 func (h *selectionRenderHandlerForTest) OnTextSelectionChanged(_ purecef.Browser, selectedText string, _ *purecef.Range) {679 func (h *selectionRenderHandlerForTest) OnTextSelectionChanged(_ purecef.Browser, selectedText string, _ *purecef.Range) {
680 handleRenderTextSelectionChanged(h.wv, selectedText)680 handleRenderTextSelectionChanged(h.wv, selectedText)
681 }681 }
682+
683+func TestCEFCursorToGDKName(t *testing.T) {
684+ tests := []struct {
685+ name string
686+ cursor purecef.CursorType
687+ expected string
688+ }{
689+ {name: "none maps to invisible cursor", cursor: purecef.CursorTypeCtNone, expected: "none"},
690+ {name: "pointer maps to default", cursor: purecef.CursorTypeCtPointer, expected: "default"},
691+ {name: "hand maps to pointer", cursor: purecef.CursorTypeCtHand, expected: "pointer"},
692+ {name: "ibeam maps to text", cursor: purecef.CursorTypeCtIbeam, expected: "text"},
693+ {name: "unknown falls back to default", cursor: purecef.CursorType(-1), expected: "default"},
694+ }
695+
696+ for _, tt := range tests {
697+ t.Run(tt.name, func(t *testing.T) {
698+ require.Equal(t, tt.expected, cefCursorToGDKName(tt.cursor))
699+ })
700+ }
701+}

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