detached 34fd6d3

style(ui): polish touchpad navigation overlay

commit
34fd6d36ebfc084bc973fa672017975016c3c1e8
author
brice · · 2026-06-18 08:45 UTC
parent
167c9b2
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
3 files · +119 -32
M internal/infrastructure/cef/touchpad_navigation.go +1 -1
@@ -132,7 +132,7 @@ func (r *touchpadNavigationRecognizer) finishIndicator(input touchpadNavigationI
132132 }
133133 action, ok := r.indicatorAction(input)
134134 if !ok {
135- action, ok = r.lastIndicatorAction, true
135+ action = r.lastIndicatorAction
136136 }
137137 threshold := normalizedTouchpadNavigationMinDelta(input.Config.TouchpadNavigationMinDelta)
138138 return touchpadNavigationResult{
@@ -132,7 +132,7 @@ func (r *touchpadNavigationRecognizer) finishIndicator(input touchpadNavigationI
132 }132 }
133 action, ok := r.indicatorAction(input)133 action, ok := r.indicatorAction(input)
134 if !ok {134 if !ok {
135- action, ok = r.lastIndicatorAction, true135+ action = r.lastIndicatorAction
136 }136 }
137 threshold := normalizedTouchpadNavigationMinDelta(input.Config.TouchpadNavigationMinDelta)137 threshold := normalizedTouchpadNavigationMinDelta(input.Config.TouchpadNavigationMinDelta)
138 return touchpadNavigationResult{138 return touchpadNavigationResult{
M internal/ui/component/touchpad_navigation_indicator.go +68 -17
@@ -9,11 +9,14 @@ import (
99 )
1010
1111 const (
12- touchpadNavigationIndicatorHideMs = 220
13- touchpadNavigationIndicatorSpacing = 6
14- touchpadNavigationIndicatorMargin = 28
15- touchpadNavigationIndicatorWidth = 96
16- touchpadNavigationIndicatorBarHeight = 4
12+ touchpadNavigationIndicatorHideMs = 220
13+ touchpadNavigationIndicatorSpacing = 8
14+ touchpadNavigationIndicatorHeaderGap = 10
15+ touchpadNavigationIndicatorMargin = 32
16+ touchpadNavigationIndicatorWidth = 156
17+ touchpadNavigationIndicatorBarHeight = 5
18+ touchpadNavigationIndicatorIconMinChars = 2
19+ touchpadNavigationIndicatorIconXAlign = 0.5
1720 )
1821
1922 // TouchpadNavigationIndicator is a lightweight overlay that makes CEF
@@ -21,6 +24,7 @@ const (
2124 // fills toward the commit threshold, and briefly shows the triggered state.
2225 type TouchpadNavigationIndicator struct {
2326 container *gtk.Box
27+ icon *gtk.Label
2428 label *gtk.Label
2529 bar *gtk.ProgressBar
2630
@@ -44,16 +48,11 @@ func NewTouchpadNavigationIndicator() *TouchpadNavigationIndicator {
4448 container.SetCanFocus(false)
4549 container.SetVisible(false)
4650
47- text := ""
48- label := gtk.NewLabel(&text)
49- if label == nil {
51+ icon, label, ok := appendTouchpadNavigationHeader(container)
52+ if !ok {
5053 container.Unref()
5154 return nil
5255 }
53- label.AddCssClass("touchpad-navigation-label")
54- label.SetCanTarget(false)
55- label.SetCanFocus(false)
56- container.Append(&label.Widget)
5756
5857 bar := gtk.NewProgressBar()
5958 if bar == nil {
@@ -69,11 +68,48 @@ func NewTouchpadNavigationIndicator() *TouchpadNavigationIndicator {
6968
7069 return &TouchpadNavigationIndicator{
7170 container: container,
71+ icon: icon,
7272 label: label,
7373 bar: bar,
7474 }
7575 }
7676
77+func appendTouchpadNavigationHeader(container *gtk.Box) (*gtk.Label, *gtk.Label, bool) {
78+ header := gtk.NewBox(gtk.OrientationHorizontalValue, touchpadNavigationIndicatorHeaderGap)
79+ if header == nil {
80+ return nil, nil, false
81+ }
82+ header.AddCssClass("touchpad-navigation-header")
83+ header.SetCanTarget(false)
84+ header.SetCanFocus(false)
85+ container.Append(&header.Widget)
86+
87+ iconText := "←"
88+ icon := gtk.NewLabel(&iconText)
89+ if icon == nil {
90+ return nil, nil, false
91+ }
92+ icon.AddCssClass("touchpad-navigation-icon")
93+ icon.SetWidthChars(touchpadNavigationIndicatorIconMinChars)
94+ icon.SetXalign(touchpadNavigationIndicatorIconXAlign)
95+ icon.SetCanTarget(false)
96+ icon.SetCanFocus(false)
97+ header.Append(&icon.Widget)
98+
99+ text := "Slide Back"
100+ label := gtk.NewLabel(&text)
101+ if label == nil {
102+ return nil, nil, false
103+ }
104+ label.AddCssClass("touchpad-navigation-label")
105+ label.SetXalign(0)
106+ label.SetCanTarget(false)
107+ label.SetCanFocus(false)
108+ header.Append(&label.Widget)
109+
110+ return icon, label, true
111+}
112+
77113 func (i *TouchpadNavigationIndicator) Widget() *gtk.Widget {
78114 if i == nil || i.container == nil {
79115 return nil
@@ -82,17 +118,21 @@ func (i *TouchpadNavigationIndicator) Widget() *gtk.Widget {
82118 }
83119
84120 func (i *TouchpadNavigationIndicator) ShowGesture(gesture port.TouchpadNavigationGesture) {
85- if i == nil || i.container == nil || i.label == nil || i.bar == nil {
121+ if i == nil || i.container == nil || i.icon == nil || i.label == nil || i.bar == nil {
86122 return
87123 }
88124 i.mu.Lock()
89125 defer i.mu.Unlock()
90126 i.cancelHideTimerLocked()
91127
128+ i.container.RemoveCssClass("back")
129+ i.container.RemoveCssClass("forward")
92130 if gesture.Action == port.TouchpadNavigationForward {
131+ i.container.AddCssClass("forward")
93132 i.container.SetHalign(gtk.AlignEndValue)
94133 i.bar.SetInverted(true)
95134 } else {
135+ i.container.AddCssClass("back")
96136 i.container.SetHalign(gtk.AlignStartValue)
97137 i.bar.SetInverted(false)
98138 }
@@ -103,6 +143,7 @@ func (i *TouchpadNavigationIndicator) ShowGesture(gesture port.TouchpadNavigatio
103143 i.container.RemoveCssClass("threshold-reached")
104144 }
105145
146+ i.icon.SetLabel(touchpadNavigationIndicatorIcon(gesture))
106147 i.label.SetLabel(touchpadNavigationIndicatorLabel(gesture))
107148 i.bar.SetFraction(clampIndicatorProgress(gesture.Progress))
108149 i.container.SetVisible(true)
@@ -133,6 +174,7 @@ func (i *TouchpadNavigationIndicator) Destroy() {
133174 i.container.Unparent()
134175 }
135176 i.container = nil
177+ i.icon = nil
136178 i.label = nil
137179 i.bar = nil
138180 }
@@ -160,6 +202,8 @@ func (i *TouchpadNavigationIndicator) hideLocked() {
160202 i.cancelHideTimerLocked()
161203 if i.container != nil {
162204 i.container.RemoveCssClass("threshold-reached")
205+ i.container.RemoveCssClass("back")
206+ i.container.RemoveCssClass("forward")
163207 i.container.SetVisible(false)
164208 }
165209 if i.bar != nil {
@@ -174,15 +218,22 @@ func (i *TouchpadNavigationIndicator) cancelHideTimerLocked() {
174218 }
175219 }
176220
221+func touchpadNavigationIndicatorIcon(gesture port.TouchpadNavigationGesture) string {
222+ if gesture.Action == port.TouchpadNavigationForward {
223+ return "→"
224+ }
225+ return "←"
226+}
227+
177228 func touchpadNavigationIndicatorLabel(gesture port.TouchpadNavigationGesture) string {
178- suffix := "back"
229+ suffix := "Back"
179230 if gesture.Action == port.TouchpadNavigationForward {
180- suffix = "forward"
231+ suffix = "Forward"
181232 }
182233 if gesture.ThresholdReached {
183- return "Release to go " + suffix
234+ return "Release for " + suffix
184235 }
185- return "Slide to go " + suffix
236+ return "Slide " + suffix
186237 }
187238
188239 func clampIndicatorProgress(progress float64) float64 {
@@ -9,11 +9,14 @@ import (
9 )9 )
10 10
11 const (11 const (
12- touchpadNavigationIndicatorHideMs = 22012+ touchpadNavigationIndicatorHideMs = 220
13- touchpadNavigationIndicatorSpacing = 613+ touchpadNavigationIndicatorSpacing = 8
14- touchpadNavigationIndicatorMargin = 2814+ touchpadNavigationIndicatorHeaderGap = 10
15- touchpadNavigationIndicatorWidth = 9615+ touchpadNavigationIndicatorMargin = 32
16- touchpadNavigationIndicatorBarHeight = 416+ touchpadNavigationIndicatorWidth = 156
17+ touchpadNavigationIndicatorBarHeight = 5
18+ touchpadNavigationIndicatorIconMinChars = 2
19+ touchpadNavigationIndicatorIconXAlign = 0.5
17 )20 )
18 21
19 // TouchpadNavigationIndicator is a lightweight overlay that makes CEF22 // TouchpadNavigationIndicator is a lightweight overlay that makes CEF
@@ -21,6 +24,7 @@ const (
21 // fills toward the commit threshold, and briefly shows the triggered state.24 // fills toward the commit threshold, and briefly shows the triggered state.
22 type TouchpadNavigationIndicator struct {25 type TouchpadNavigationIndicator struct {
23 container *gtk.Box26 container *gtk.Box
27+ icon *gtk.Label
24 label *gtk.Label28 label *gtk.Label
25 bar *gtk.ProgressBar29 bar *gtk.ProgressBar
26 30
@@ -44,16 +48,11 @@ func NewTouchpadNavigationIndicator() *TouchpadNavigationIndicator {
44 container.SetCanFocus(false)48 container.SetCanFocus(false)
45 container.SetVisible(false)49 container.SetVisible(false)
46 50
47- text := ""51+ icon, label, ok := appendTouchpadNavigationHeader(container)
48- label := gtk.NewLabel(&text)52+ if !ok {
49- if label == nil {
50 container.Unref()53 container.Unref()
51 return nil54 return nil
52 }55 }
53- label.AddCssClass("touchpad-navigation-label")
54- label.SetCanTarget(false)
55- label.SetCanFocus(false)
56- container.Append(&label.Widget)
57 56
58 bar := gtk.NewProgressBar()57 bar := gtk.NewProgressBar()
59 if bar == nil {58 if bar == nil {
@@ -69,11 +68,48 @@ func NewTouchpadNavigationIndicator() *TouchpadNavigationIndicator {
69 68
70 return &TouchpadNavigationIndicator{69 return &TouchpadNavigationIndicator{
71 container: container,70 container: container,
71+ icon: icon,
72 label: label,72 label: label,
73 bar: bar,73 bar: bar,
74 }74 }
75 }75 }
76 76
77+func appendTouchpadNavigationHeader(container *gtk.Box) (*gtk.Label, *gtk.Label, bool) {
78+ header := gtk.NewBox(gtk.OrientationHorizontalValue, touchpadNavigationIndicatorHeaderGap)
79+ if header == nil {
80+ return nil, nil, false
81+ }
82+ header.AddCssClass("touchpad-navigation-header")
83+ header.SetCanTarget(false)
84+ header.SetCanFocus(false)
85+ container.Append(&header.Widget)
86+
87+ iconText := "←"
88+ icon := gtk.NewLabel(&iconText)
89+ if icon == nil {
90+ return nil, nil, false
91+ }
92+ icon.AddCssClass("touchpad-navigation-icon")
93+ icon.SetWidthChars(touchpadNavigationIndicatorIconMinChars)
94+ icon.SetXalign(touchpadNavigationIndicatorIconXAlign)
95+ icon.SetCanTarget(false)
96+ icon.SetCanFocus(false)
97+ header.Append(&icon.Widget)
98+
99+ text := "Slide Back"
100+ label := gtk.NewLabel(&text)
101+ if label == nil {
102+ return nil, nil, false
103+ }
104+ label.AddCssClass("touchpad-navigation-label")
105+ label.SetXalign(0)
106+ label.SetCanTarget(false)
107+ label.SetCanFocus(false)
108+ header.Append(&label.Widget)
109+
110+ return icon, label, true
111+}
112+
77 func (i *TouchpadNavigationIndicator) Widget() *gtk.Widget {113 func (i *TouchpadNavigationIndicator) Widget() *gtk.Widget {
78 if i == nil || i.container == nil {114 if i == nil || i.container == nil {
79 return nil115 return nil
@@ -82,17 +118,21 @@ func (i *TouchpadNavigationIndicator) Widget() *gtk.Widget {
82 }118 }
83 119
84 func (i *TouchpadNavigationIndicator) ShowGesture(gesture port.TouchpadNavigationGesture) {120 func (i *TouchpadNavigationIndicator) ShowGesture(gesture port.TouchpadNavigationGesture) {
85- if i == nil || i.container == nil || i.label == nil || i.bar == nil {121+ if i == nil || i.container == nil || i.icon == nil || i.label == nil || i.bar == nil {
86 return122 return
87 }123 }
88 i.mu.Lock()124 i.mu.Lock()
89 defer i.mu.Unlock()125 defer i.mu.Unlock()
90 i.cancelHideTimerLocked()126 i.cancelHideTimerLocked()
91 127
128+ i.container.RemoveCssClass("back")
129+ i.container.RemoveCssClass("forward")
92 if gesture.Action == port.TouchpadNavigationForward {130 if gesture.Action == port.TouchpadNavigationForward {
131+ i.container.AddCssClass("forward")
93 i.container.SetHalign(gtk.AlignEndValue)132 i.container.SetHalign(gtk.AlignEndValue)
94 i.bar.SetInverted(true)133 i.bar.SetInverted(true)
95 } else {134 } else {
135+ i.container.AddCssClass("back")
96 i.container.SetHalign(gtk.AlignStartValue)136 i.container.SetHalign(gtk.AlignStartValue)
97 i.bar.SetInverted(false)137 i.bar.SetInverted(false)
98 }138 }
@@ -103,6 +143,7 @@ func (i *TouchpadNavigationIndicator) ShowGesture(gesture port.TouchpadNavigatio
103 i.container.RemoveCssClass("threshold-reached")143 i.container.RemoveCssClass("threshold-reached")
104 }144 }
105 145
146+ i.icon.SetLabel(touchpadNavigationIndicatorIcon(gesture))
106 i.label.SetLabel(touchpadNavigationIndicatorLabel(gesture))147 i.label.SetLabel(touchpadNavigationIndicatorLabel(gesture))
107 i.bar.SetFraction(clampIndicatorProgress(gesture.Progress))148 i.bar.SetFraction(clampIndicatorProgress(gesture.Progress))
108 i.container.SetVisible(true)149 i.container.SetVisible(true)
@@ -133,6 +174,7 @@ func (i *TouchpadNavigationIndicator) Destroy() {
133 i.container.Unparent()174 i.container.Unparent()
134 }175 }
135 i.container = nil176 i.container = nil
177+ i.icon = nil
136 i.label = nil178 i.label = nil
137 i.bar = nil179 i.bar = nil
138 }180 }
@@ -160,6 +202,8 @@ func (i *TouchpadNavigationIndicator) hideLocked() {
160 i.cancelHideTimerLocked()202 i.cancelHideTimerLocked()
161 if i.container != nil {203 if i.container != nil {
162 i.container.RemoveCssClass("threshold-reached")204 i.container.RemoveCssClass("threshold-reached")
205+ i.container.RemoveCssClass("back")
206+ i.container.RemoveCssClass("forward")
163 i.container.SetVisible(false)207 i.container.SetVisible(false)
164 }208 }
165 if i.bar != nil {209 if i.bar != nil {
@@ -174,15 +218,22 @@ func (i *TouchpadNavigationIndicator) cancelHideTimerLocked() {
174 }218 }
175 }219 }
176 220
221+func touchpadNavigationIndicatorIcon(gesture port.TouchpadNavigationGesture) string {
222+ if gesture.Action == port.TouchpadNavigationForward {
223+ return "→"
224+ }
225+ return "←"
226+}
227+
177 func touchpadNavigationIndicatorLabel(gesture port.TouchpadNavigationGesture) string {228 func touchpadNavigationIndicatorLabel(gesture port.TouchpadNavigationGesture) string {
178- suffix := "back"229+ suffix := "Back"
179 if gesture.Action == port.TouchpadNavigationForward {230 if gesture.Action == port.TouchpadNavigationForward {
180- suffix = "forward"231+ suffix = "Forward"
181 }232 }
182 if gesture.ThresholdReached {233 if gesture.ThresholdReached {
183- return "Release to go " + suffix234+ return "Release for " + suffix
184 }235 }
185- return "Slide to go " + suffix236+ return "Slide " + suffix
186 }237 }
187 238
188 func clampIndicatorProgress(progress float64) float64 {239 func clampIndicatorProgress(progress float64) float64 {
M internal/ui/theme/css.go +50 -14
@@ -770,39 +770,75 @@ func generateTouchpadNavigationIndicatorCSS(p Palette) string {
770770 return `/* ===== Touchpad Navigation Indicator Styling ===== */
771771
772772 .touchpad-navigation-indicator {
773- background-color: alpha(var(--surface-variant), 0.92);
773+ background-color: alpha(var(--surface), 0.94);
774774 color: var(--text);
775- border: 1px solid alpha(var(--accent), 0.45);
776- border-radius: 999px;
777- padding: 10px 12px;
778- box-shadow: 0 10px 24px alpha(#000000, 0.28);
775+ border: 0.0625em solid alpha(var(--border), 0.72);
776+ border-radius: 1.125em;
777+ padding: 0.75em 0.875em 0.8125em;
778+ box-shadow: 0 1em 2.25em alpha(black, 0.30),
779+ 0 0.0625em 0 alpha(white, 0.06) inset;
780+ transition: background-color 140ms ease-out,
781+ border-color 140ms ease-out,
782+ box-shadow 140ms ease-out;
783+}
784+
785+.touchpad-navigation-indicator.back {
786+ border-left: 0.1875em solid alpha(var(--accent), 0.82);
787+}
788+
789+.touchpad-navigation-indicator.forward {
790+ border-right: 0.1875em solid alpha(var(--accent), 0.82);
779791 }
780792
781793 .touchpad-navigation-indicator.threshold-reached {
782- border-color: var(--accent);
783- background-color: alpha(var(--accent), 0.20);
794+ border-color: alpha(var(--accent), 0.95);
795+ background-color: alpha(var(--accent), 0.18);
796+ box-shadow: 0 1.125em 2.5em alpha(black, 0.34),
797+ 0 0 0 0.0625em alpha(var(--accent), 0.24) inset;
798+}
799+
800+.touchpad-navigation-header {
801+ min-width: 9.75em;
802+}
803+
804+.touchpad-navigation-icon {
805+ background-color: alpha(var(--accent), 0.16);
806+ color: var(--accent);
807+ border-radius: 999px;
808+ font-size: 0.95em;
809+ font-weight: 700;
810+ padding: 0.125em 0.375em;
811+}
812+
813+.touchpad-navigation-indicator.threshold-reached .touchpad-navigation-icon {
814+ background-color: var(--accent);
815+ color: var(--bg);
784816 }
785817
786818 .touchpad-navigation-label {
787- font-weight: 600;
788- font-size: 0.9em;
819+ color: var(--text);
820+ font-weight: 700;
821+ font-size: 0.92em;
822+ letter-spacing: 0.01em;
789823 }
790824
791825 progressbar.touchpad-navigation-progress {
792- min-height: 4px;
826+ min-height: 0.3125em;
793827 }
794828
795829 progressbar.touchpad-navigation-progress trough {
796- min-height: 4px;
797- min-width: 96px;
830+ min-height: 0.3125em;
831+ min-width: 9.75em;
798832 border-radius: 999px;
799- background-color: alpha(var(--text), 0.16);
833+ background-color: alpha(var(--muted), 0.24);
834+ box-shadow: 0 0.0625em 0 alpha(white, 0.06) inset;
800835 }
801836
802837 progressbar.touchpad-navigation-progress progress {
803- min-height: 4px;
838+ min-height: 0.3125em;
804839 border-radius: 999px;
805840 background-color: var(--accent);
841+ box-shadow: 0 0 0.35em alpha(var(--accent), 0.35);
806842 }
807843 `
808844 }
@@ -770,39 +770,75 @@ func generateTouchpadNavigationIndicatorCSS(p Palette) string {
770 return `/* ===== Touchpad Navigation Indicator Styling ===== */770 return `/* ===== Touchpad Navigation Indicator Styling ===== */
771 771
772 .touchpad-navigation-indicator {772 .touchpad-navigation-indicator {
773- background-color: alpha(var(--surface-variant), 0.92);773+ background-color: alpha(var(--surface), 0.94);
774 color: var(--text);774 color: var(--text);
775- border: 1px solid alpha(var(--accent), 0.45);775+ border: 0.0625em solid alpha(var(--border), 0.72);
776- border-radius: 999px;776+ border-radius: 1.125em;
777- padding: 10px 12px;777+ padding: 0.75em 0.875em 0.8125em;
778- box-shadow: 0 10px 24px alpha(#000000, 0.28);778+ box-shadow: 0 1em 2.25em alpha(black, 0.30),
779+ 0 0.0625em 0 alpha(white, 0.06) inset;
780+ transition: background-color 140ms ease-out,
781+ border-color 140ms ease-out,
782+ box-shadow 140ms ease-out;
783+}
784+
785+.touchpad-navigation-indicator.back {
786+ border-left: 0.1875em solid alpha(var(--accent), 0.82);
787+}
788+
789+.touchpad-navigation-indicator.forward {
790+ border-right: 0.1875em solid alpha(var(--accent), 0.82);
779 }791 }
780 792
781 .touchpad-navigation-indicator.threshold-reached {793 .touchpad-navigation-indicator.threshold-reached {
782- border-color: var(--accent);794+ border-color: alpha(var(--accent), 0.95);
783- background-color: alpha(var(--accent), 0.20);795+ background-color: alpha(var(--accent), 0.18);
796+ box-shadow: 0 1.125em 2.5em alpha(black, 0.34),
797+ 0 0 0 0.0625em alpha(var(--accent), 0.24) inset;
798+}
799+
800+.touchpad-navigation-header {
801+ min-width: 9.75em;
802+}
803+
804+.touchpad-navigation-icon {
805+ background-color: alpha(var(--accent), 0.16);
806+ color: var(--accent);
807+ border-radius: 999px;
808+ font-size: 0.95em;
809+ font-weight: 700;
810+ padding: 0.125em 0.375em;
811+}
812+
813+.touchpad-navigation-indicator.threshold-reached .touchpad-navigation-icon {
814+ background-color: var(--accent);
815+ color: var(--bg);
784 }816 }
785 817
786 .touchpad-navigation-label {818 .touchpad-navigation-label {
787- font-weight: 600;819+ color: var(--text);
788- font-size: 0.9em;820+ font-weight: 700;
821+ font-size: 0.92em;
822+ letter-spacing: 0.01em;
789 }823 }
790 824
791 progressbar.touchpad-navigation-progress {825 progressbar.touchpad-navigation-progress {
792- min-height: 4px;826+ min-height: 0.3125em;
793 }827 }
794 828
795 progressbar.touchpad-navigation-progress trough {829 progressbar.touchpad-navigation-progress trough {
796- min-height: 4px;830+ min-height: 0.3125em;
797- min-width: 96px;831+ min-width: 9.75em;
798 border-radius: 999px;832 border-radius: 999px;
799- background-color: alpha(var(--text), 0.16);833+ background-color: alpha(var(--muted), 0.24);
834+ box-shadow: 0 0.0625em 0 alpha(white, 0.06) inset;
800 }835 }
801 836
802 progressbar.touchpad-navigation-progress progress {837 progressbar.touchpad-navigation-progress progress {
803- min-height: 4px;838+ min-height: 0.3125em;
804 border-radius: 999px;839 border-radius: 999px;
805 background-color: var(--accent);840 background-color: var(--accent);
841+ box-shadow: 0 0 0.35em alpha(var(--accent), 0.35);
806 }842 }
807 `843 `
808 }844 }

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