detached db3364d

feat(overview): darken the background behind overview previews (#110)

* feat(overview): darken the background behind overview previews

* fix(overview): set DimBehind only with a veil and update the plane veil comment
commit
db3364d27d9cd464bc798048ab67c07d600315d1
author
bnema · · 2026-10-03 17:28 UTC
committer
GitHub · · 2026-10-03 17:28 UTC
parent
1c6cce9
signature
GPG UnverifiedB5690EEEBB952194 The key is unknown
7 files · +77 -8
M docs/config.md +2 -2
@@ -22,7 +22,7 @@ NeferWL reads `$XDG_CONFIG_HOME/neferwl/config` (or `~/.config/neferwl/config`).
2222 | `xwayland` | `xwayland-satellite` | X11 support; `off` disables it |
2323 | `background` | `#111111` | Solid background color |
2424 | `border.width` | `2` | Lines between visible neighboring tiles and around non-fullscreen floats, in logical pixels; `0` hides them |
25-| `floating.dim` | `0.3` | Black veil over tiles and lower layers when a native floating window above the tiles or the stash is visible; opacity 0 to 1 |
25+| `floating.dim` | `0.3` | Black veil over tiles and lower layers when a native floating window above the tiles or the stash is visible, and over the background and lower layers behind the overview; opacity 0 to 1 |
2626 | `stash.width` | `80` | Width of the selected stashed window, over the tiles, in percent of the usable width; 10 to 90 (`80%` works too). The rest is split between both sides, where the neighbors show. Its height is 80% of the usable height |
2727 | `stash.gap` | `2` | Space between the selected stashed window and its neighbors, in percent of the usable width; 0 to 10. The neighbors show from there to the screen edge: with the defaults, 8% of each. A gap as wide as the side margin hides them |
2828 | `stash.dim` | `0.5` | Black veil over the stash neighbors, borders included; opacity 0 to 1 |
@@ -236,7 +236,7 @@ The drop can be on another monitor; focus follows the window. There, the middle
236236
237237 ### Overview
238238
239-`toggle-overview` shows scaled previews of the current workspace, including over fullscreen windows. Windows keep their size and previews show their last frames. Covering floats and windows hidden behind a maximized column in `fixed` overflow appear as cards: the on-screen item is in front, hidden columns share one spiral-layout card, up to two cards behind peek above it, and passed cards peek below. Neighbor workspaces show dimmed stacks. Each monitor has one vertical overview. A named workspace appears directly below the numbered workspace from which its `workspace <name>` bind was invoked. Invoking it from another numbered workspace updates this placement; browsing the overview does not. Several named workspaces attached to the same numbered workspace follow configuration order, with horizontal rules separating their group from numbered rows. Named-to-named binds reuse the source's numbered attachment. Unattached named workspaces follow the last occupied numbered row, or the first row if all numbered workspaces are empty. The selected workspace stays centred and its immediate neighbors appear above and below; rows and stash previews keep their full width. Empty workspaces are skipped unless currently selected or serving as the invocation anchor of a visible named row. Named workspaces remain outside the numbered list and normal workspace up/down navigation. Smaller floats, such as dialogs, stay hidden. A fullscreen floating window, or any fullscreen window with `fixed` overflow, is its workspace's front card, with the windows it hides behind it; `scroll` overflow keeps its columns selectable in one card. Choosing the fullscreen window keeps it fullscreen; choosing another window leaves fullscreen.
239+`toggle-overview` shows scaled previews of the current workspace, including over fullscreen windows, over a background darkened by `floating.dim`. Windows keep their size and previews show their last frames. Covering floats and windows hidden behind a maximized column in `fixed` overflow appear as cards: the on-screen item is in front, hidden columns share one spiral-layout card, up to two cards behind peek above it, and passed cards peek below. Neighbor workspaces show dimmed stacks. Each monitor has one vertical overview. A named workspace appears directly below the numbered workspace from which its `workspace <name>` bind was invoked. Invoking it from another numbered workspace updates this placement; browsing the overview does not. Several named workspaces attached to the same numbered workspace follow configuration order, with horizontal rules separating their group from numbered rows. Named-to-named binds reuse the source's numbered attachment. Unattached named workspaces follow the last occupied numbered row, or the first row if all numbered workspaces are empty. The selected workspace stays centred and its immediate neighbors appear above and below; rows and stash previews keep their full width. Empty workspaces are skipped unless currently selected or serving as the invocation anchor of a visible named row. Named workspaces remain outside the numbered list and normal workspace up/down navigation. Smaller floats, such as dialogs, stay hidden. A fullscreen floating window, or any fullscreen window with `fixed` overflow, is its workspace's front card, with the windows it hides behind it; `scroll` overflow keeps its columns selectable in one card. Choosing the fullscreen window keeps it fullscreen; choosing another window leaves fullscreen.
240240
241241 | Key | Action |
242242 | --- | --- |
@@ -22,7 +22,7 @@ NeferWL reads `$XDG_CONFIG_HOME/neferwl/config` (or `~/.config/neferwl/config`).
22 | `xwayland` | `xwayland-satellite` | X11 support; `off` disables it |22 | `xwayland` | `xwayland-satellite` | X11 support; `off` disables it |
23 | `background` | `#111111` | Solid background color |23 | `background` | `#111111` | Solid background color |
24 | `border.width` | `2` | Lines between visible neighboring tiles and around non-fullscreen floats, in logical pixels; `0` hides them |24 | `border.width` | `2` | Lines between visible neighboring tiles and around non-fullscreen floats, in logical pixels; `0` hides them |
25-| `floating.dim` | `0.3` | Black veil over tiles and lower layers when a native floating window above the tiles or the stash is visible; opacity 0 to 1 |25+| `floating.dim` | `0.3` | Black veil over tiles and lower layers when a native floating window above the tiles or the stash is visible, and over the background and lower layers behind the overview; opacity 0 to 1 |
26 | `stash.width` | `80` | Width of the selected stashed window, over the tiles, in percent of the usable width; 10 to 90 (`80%` works too). The rest is split between both sides, where the neighbors show. Its height is 80% of the usable height |26 | `stash.width` | `80` | Width of the selected stashed window, over the tiles, in percent of the usable width; 10 to 90 (`80%` works too). The rest is split between both sides, where the neighbors show. Its height is 80% of the usable height |
27 | `stash.gap` | `2` | Space between the selected stashed window and its neighbors, in percent of the usable width; 0 to 10. The neighbors show from there to the screen edge: with the defaults, 8% of each. A gap as wide as the side margin hides them |27 | `stash.gap` | `2` | Space between the selected stashed window and its neighbors, in percent of the usable width; 0 to 10. The neighbors show from there to the screen edge: with the defaults, 8% of each. A gap as wide as the side margin hides them |
28 | `stash.dim` | `0.5` | Black veil over the stash neighbors, borders included; opacity 0 to 1 |28 | `stash.dim` | `0.5` | Black veil over the stash neighbors, borders included; opacity 0 to 1 |
@@ -236,7 +236,7 @@ The drop can be on another monitor; focus follows the window. There, the middle
236 236
237 ### Overview237 ### Overview
238 238
239-`toggle-overview` shows scaled previews of the current workspace, including over fullscreen windows. Windows keep their size and previews show their last frames. Covering floats and windows hidden behind a maximized column in `fixed` overflow appear as cards: the on-screen item is in front, hidden columns share one spiral-layout card, up to two cards behind peek above it, and passed cards peek below. Neighbor workspaces show dimmed stacks. Each monitor has one vertical overview. A named workspace appears directly below the numbered workspace from which its `workspace <name>` bind was invoked. Invoking it from another numbered workspace updates this placement; browsing the overview does not. Several named workspaces attached to the same numbered workspace follow configuration order, with horizontal rules separating their group from numbered rows. Named-to-named binds reuse the source's numbered attachment. Unattached named workspaces follow the last occupied numbered row, or the first row if all numbered workspaces are empty. The selected workspace stays centred and its immediate neighbors appear above and below; rows and stash previews keep their full width. Empty workspaces are skipped unless currently selected or serving as the invocation anchor of a visible named row. Named workspaces remain outside the numbered list and normal workspace up/down navigation. Smaller floats, such as dialogs, stay hidden. A fullscreen floating window, or any fullscreen window with `fixed` overflow, is its workspace's front card, with the windows it hides behind it; `scroll` overflow keeps its columns selectable in one card. Choosing the fullscreen window keeps it fullscreen; choosing another window leaves fullscreen.239+`toggle-overview` shows scaled previews of the current workspace, including over fullscreen windows, over a background darkened by `floating.dim`. Windows keep their size and previews show their last frames. Covering floats and windows hidden behind a maximized column in `fixed` overflow appear as cards: the on-screen item is in front, hidden columns share one spiral-layout card, up to two cards behind peek above it, and passed cards peek below. Neighbor workspaces show dimmed stacks. Each monitor has one vertical overview. A named workspace appears directly below the numbered workspace from which its `workspace <name>` bind was invoked. Invoking it from another numbered workspace updates this placement; browsing the overview does not. Several named workspaces attached to the same numbered workspace follow configuration order, with horizontal rules separating their group from numbered rows. Named-to-named binds reuse the source's numbered attachment. Unattached named workspaces follow the last occupied numbered row, or the first row if all numbered workspaces are empty. The selected workspace stays centred and its immediate neighbors appear above and below; rows and stash previews keep their full width. Empty workspaces are skipped unless currently selected or serving as the invocation anchor of a visible named row. Named workspaces remain outside the numbered list and normal workspace up/down navigation. Smaller floats, such as dialogs, stay hidden. A fullscreen floating window, or any fullscreen window with `fixed` overflow, is its workspace's front card, with the windows it hides behind it; `scroll` overflow keeps its columns selectable in one card. Choosing the fullscreen window keeps it fullscreen; choosing another window leaves fullscreen.
240 240
241 | Key | Action |241 | Key | Action |
242 | --- | --- |242 | --- | --- |
M internal/adapters/drm/overlay.go +2 -2
@@ -91,8 +91,8 @@ func overlayCandidate(s ports.Scene, surfaces map[ports.WindowID]ports.SurfaceCo
9191 return ports.SceneWindow{}, ports.SurfaceContent{}, "no_candidate"
9292 }
9393 if s.Dim > 0 {
94- // The renderer draws the veil under the first float it composes: a
95- // float on the plane would take the veil with it.
94+ // The renderer composes the veil (under the first float, or under
95+ // every window with DimBehind): a window on the plane would skip it.
9696 return ports.SceneWindow{}, ports.SurfaceContent{}, "dim"
9797 }
9898 if len(s.DropHints) > 0 {
@@ -91,8 +91,8 @@ func overlayCandidate(s ports.Scene, surfaces map[ports.WindowID]ports.SurfaceCo
91 return ports.SceneWindow{}, ports.SurfaceContent{}, "no_candidate"91 return ports.SceneWindow{}, ports.SurfaceContent{}, "no_candidate"
92 }92 }
93 if s.Dim > 0 {93 if s.Dim > 0 {
94- // The renderer draws the veil under the first float it composes: a94+ // The renderer composes the veil (under the first float, or under
95- // float on the plane would take the veil with it.95+ // every window with DimBehind): a window on the plane would skip it.
96 return ports.SceneWindow{}, ports.SurfaceContent{}, "dim"96 return ports.SceneWindow{}, ports.SurfaceContent{}, "dim"
97 }97 }
98 if len(s.DropHints) > 0 {98 if len(s.DropHints) > 0 {
M internal/adapters/vulkan/scene.go +6 -2
@@ -160,8 +160,12 @@ func (w *sceneWalk) layers(afterWindows bool) {
160160
161161 // windows draws ordered placements with borders. Tile lines go after
162162 // the column group, before the first float above it; an overview preview
163-// of a float is not one.
163+// of a float is not one. The veil goes under that float, or under every
164+// window with DimBehind.
164165 func (w *sceneWalk) windows() {
166+ if w.s.DimBehind && w.s.Dim > 0 {
167+ w.dim(w.bounds, w.s.Dim)
168+ }
165169 tileLines := false
166170 for _, win := range w.s.Windows {
167171 if win.Hidden || win.Popup || win.Rect.W <= 0 || win.Rect.H <= 0 {
@@ -170,7 +174,7 @@ func (w *sceneWalk) windows() {
170174 if !win.Below && win.Floating && win.Preview == 0 && !tileLines {
171175 w.separators(0)
172176 tileLines = true
173- if w.s.Dim > 0 {
177+ if w.s.Dim > 0 && !w.s.DimBehind {
174178 w.dim(w.bounds, w.s.Dim)
175179 }
176180 }
@@ -160,8 +160,12 @@ func (w *sceneWalk) layers(afterWindows bool) {
160 160
161 // windows draws ordered placements with borders. Tile lines go after161 // windows draws ordered placements with borders. Tile lines go after
162 // the column group, before the first float above it; an overview preview162 // the column group, before the first float above it; an overview preview
163-// of a float is not one.163+// of a float is not one. The veil goes under that float, or under every
164+// window with DimBehind.
164 func (w *sceneWalk) windows() {165 func (w *sceneWalk) windows() {
166+ if w.s.DimBehind && w.s.Dim > 0 {
167+ w.dim(w.bounds, w.s.Dim)
168+ }
165 tileLines := false169 tileLines := false
166 for _, win := range w.s.Windows {170 for _, win := range w.s.Windows {
167 if win.Hidden || win.Popup || win.Rect.W <= 0 || win.Rect.H <= 0 {171 if win.Hidden || win.Popup || win.Rect.W <= 0 || win.Rect.H <= 0 {
@@ -170,7 +174,7 @@ func (w *sceneWalk) windows() {
170 if !win.Below && win.Floating && win.Preview == 0 && !tileLines {174 if !win.Below && win.Floating && win.Preview == 0 && !tileLines {
171 w.separators(0)175 w.separators(0)
172 tileLines = true176 tileLines = true
173- if w.s.Dim > 0 {177+ if w.s.Dim > 0 && !w.s.DimBehind {
174 w.dim(w.bounds, w.s.Dim)178 w.dim(w.bounds, w.s.Dim)
175 }179 }
176 }180 }
M internal/adapters/vulkan/scene_dim_test.go +20 -0
@@ -64,6 +64,26 @@ func TestSceneDimOrder(t *testing.T) {
6464 }
6565 }
6666
67+// DimBehind puts the veil first, under every window, floats included, and
68+// only once.
69+func TestSceneDimBehind(t *testing.T) {
70+ r := &Renderer{width: 80, height: 60}
71+ s := ports.Scene{Dim: 0.3, DimBehind: true, Windows: []ports.SceneWindow{
72+ {ID: 1, Preview: 0.5, Rect: ports.Rect{X: 10, W: 20, H: 20}},
73+ {ID: 2, Floating: true, Rect: ports.Rect{X: 40, W: 20, H: 20}},
74+ }}
75+ ds := r.draws(s, nil, newDamage(&target{}, s, image.Rect(0, 0, 80, 60)))
76+ if len(ds) != 3 { // veil, two windows
77+ t.Fatalf("draws = %d, want 3", len(ds))
78+ }
79+ if v := ds[0].pc; v.rect != [4]int32{0, 0, 80, 60} || v.color != [4]float32{0, 0, 0, 0.3} {
80+ t.Fatalf("veil = %+v", v)
81+ }
82+ if ds[1].pc.rect[0] != 10 || ds[2].pc.rect[0] != 40 {
83+ t.Fatalf("windows not over the veil: %+v", ds)
84+ }
85+}
86+
6787 // A dimmed window gets a veil over its own rect, border included, right
6888 // after it: windows drawn later stay bright.
6989 func TestSceneWindowDim(t *testing.T) {
@@ -64,6 +64,26 @@ func TestSceneDimOrder(t *testing.T) {
64 }64 }
65 }65 }
66 66
67+// DimBehind puts the veil first, under every window, floats included, and
68+// only once.
69+func TestSceneDimBehind(t *testing.T) {
70+ r := &Renderer{width: 80, height: 60}
71+ s := ports.Scene{Dim: 0.3, DimBehind: true, Windows: []ports.SceneWindow{
72+ {ID: 1, Preview: 0.5, Rect: ports.Rect{X: 10, W: 20, H: 20}},
73+ {ID: 2, Floating: true, Rect: ports.Rect{X: 40, W: 20, H: 20}},
74+ }}
75+ ds := r.draws(s, nil, newDamage(&target{}, s, image.Rect(0, 0, 80, 60)))
76+ if len(ds) != 3 { // veil, two windows
77+ t.Fatalf("draws = %d, want 3", len(ds))
78+ }
79+ if v := ds[0].pc; v.rect != [4]int32{0, 0, 80, 60} || v.color != [4]float32{0, 0, 0, 0.3} {
80+ t.Fatalf("veil = %+v", v)
81+ }
82+ if ds[1].pc.rect[0] != 10 || ds[2].pc.rect[0] != 40 {
83+ t.Fatalf("windows not over the veil: %+v", ds)
84+ }
85+}
86+
67 // A dimmed window gets a veil over its own rect, border included, right87 // A dimmed window gets a veil over its own rect, border included, right
68 // after it: windows drawn later stay bright.88 // after it: windows drawn later stay bright.
69 func TestSceneWindowDim(t *testing.T) {89 func TestSceneWindowDim(t *testing.T) {
M internal/core/core.go +5 -0
@@ -561,6 +561,11 @@ func (c *Core) publish(ctx context.Context) error {
561561 }
562562 }
563563 scene.Dim = floatDim(layout, frame, c.cfg.Floating.Dim)
564+ if sc.mon.ov.open {
565+ // Darken the wallpaper around the previews.
566+ scene.Dim = c.cfg.Floating.Dim
567+ scene.DimBehind = scene.Dim > 0
568+ }
564569 if d := c.drag; d != nil && d.target.screen == sc && d.target.kind != dropNone {
565570 scene.DropHints = slices.Clone(d.target.hints)
566571 }
@@ -561,6 +561,11 @@ func (c *Core) publish(ctx context.Context) error {
561 }561 }
562 }562 }
563 scene.Dim = floatDim(layout, frame, c.cfg.Floating.Dim)563 scene.Dim = floatDim(layout, frame, c.cfg.Floating.Dim)
564+ if sc.mon.ov.open {
565+ // Darken the wallpaper around the previews.
566+ scene.Dim = c.cfg.Floating.Dim
567+ scene.DimBehind = scene.Dim > 0
568+ }
564 if d := c.drag; d != nil && d.target.screen == sc && d.target.kind != dropNone {569 if d := c.drag; d != nil && d.target.screen == sc && d.target.kind != dropNone {
565 scene.DropHints = slices.Clone(d.target.hints)570 scene.DropHints = slices.Clone(d.target.hints)
566 }571 }
M internal/core/float_core_test.go +37 -0
@@ -60,4 +60,41 @@ func TestCoreCoveringFloatSceneAndHit(t *testing.T) {
6060 }
6161 w.FocusID(2)
6262 check(0.3, 1, 2, 3)
63+ m.ToggleOverview()
64+ if err := c.publish(ctx); err != nil {
65+ t.Fatal(err)
66+ }
67+ if s := (<-scenes)[0]; s.Dim != 0.3 || !s.DimBehind {
68+ t.Fatalf("overview dim %v behind %v", s.Dim, s.DimBehind)
69+ }
70+}
71+
72+// The overview darkens the wallpaper behind its previews, tiles only too.
73+func TestCoreOverviewDimsBehind(t *testing.T) {
74+ cfg := ports.Config{}
75+ cfg.Keyboard.CmdKey = "super"
76+ cfg.Layout.MaxColumns = 2
77+ cfg.Floating.Dim = 0.3
78+ scenes := make(chan []ports.Scene, 1)
79+ c, err := New(cfg, Channels{Scenes: scenes, Commands: make(chan ports.ClientCommand, 128)})
80+ if err != nil {
81+ t.Fatal(err)
82+ }
83+ c.cur().info = ports.OutputInfo{Name: "OUT", Width: 100, Height: 80}
84+ m := c.cur().mon
85+ m.SetOutput(100, 80)
86+ m.Current().AddWindow(1)
87+ ctx := context.Background()
88+ for _, tc := range []struct {
89+ dim float64
90+ behind bool
91+ }{{0, false}, {0.3, true}, {0, false}} {
92+ if err := c.publish(ctx); err != nil {
93+ t.Fatal(err)
94+ }
95+ if s := (<-scenes)[0]; s.Dim != tc.dim || s.DimBehind != tc.behind {
96+ t.Fatalf("dim %v behind %v, want %v %v", s.Dim, s.DimBehind, tc.dim, tc.behind)
97+ }
98+ m.ToggleOverview()
99+ }
63100 }
@@ -60,4 +60,41 @@ func TestCoreCoveringFloatSceneAndHit(t *testing.T) {
60 }60 }
61 w.FocusID(2)61 w.FocusID(2)
62 check(0.3, 1, 2, 3)62 check(0.3, 1, 2, 3)
63+ m.ToggleOverview()
64+ if err := c.publish(ctx); err != nil {
65+ t.Fatal(err)
66+ }
67+ if s := (<-scenes)[0]; s.Dim != 0.3 || !s.DimBehind {
68+ t.Fatalf("overview dim %v behind %v", s.Dim, s.DimBehind)
69+ }
70+}
71+
72+// The overview darkens the wallpaper behind its previews, tiles only too.
73+func TestCoreOverviewDimsBehind(t *testing.T) {
74+ cfg := ports.Config{}
75+ cfg.Keyboard.CmdKey = "super"
76+ cfg.Layout.MaxColumns = 2
77+ cfg.Floating.Dim = 0.3
78+ scenes := make(chan []ports.Scene, 1)
79+ c, err := New(cfg, Channels{Scenes: scenes, Commands: make(chan ports.ClientCommand, 128)})
80+ if err != nil {
81+ t.Fatal(err)
82+ }
83+ c.cur().info = ports.OutputInfo{Name: "OUT", Width: 100, Height: 80}
84+ m := c.cur().mon
85+ m.SetOutput(100, 80)
86+ m.Current().AddWindow(1)
87+ ctx := context.Background()
88+ for _, tc := range []struct {
89+ dim float64
90+ behind bool
91+ }{{0, false}, {0.3, true}, {0, false}} {
92+ if err := c.publish(ctx); err != nil {
93+ t.Fatal(err)
94+ }
95+ if s := (<-scenes)[0]; s.Dim != tc.dim || s.DimBehind != tc.behind {
96+ t.Fatalf("dim %v behind %v, want %v %v", s.Dim, s.DimBehind, tc.dim, tc.behind)
97+ }
98+ m.ToggleOverview()
99+ }
63 }100 }
M internal/ports/scene.go +5 -2
@@ -27,8 +27,11 @@ type Scene struct {
2727 // included) inside WorkspaceClip when set, with black at this opacity,
2828 // 0 to 1, under the first
2929 // visible float. 0 draws nothing.
30- Dim float64
31- Windows []SceneWindow
30+ Dim float64
31+ // DimBehind draws the Dim veil under every window instead, right over
32+ // the background and bottom layers (the overview).
33+ DimBehind bool
34+ Windows []SceneWindow
3235 // Separators are the lines between windows, drawn in slice order with
3336 // the Border colors: tile lines over the tiles, under the floats; a
3437 // float's border right after the float.
@@ -27,8 +27,11 @@ type Scene struct {
27 // included) inside WorkspaceClip when set, with black at this opacity,27 // included) inside WorkspaceClip when set, with black at this opacity,
28 // 0 to 1, under the first28 // 0 to 1, under the first
29 // visible float. 0 draws nothing.29 // visible float. 0 draws nothing.
30- Dim float6430+ Dim float64
31- Windows []SceneWindow31+ // DimBehind draws the Dim veil under every window instead, right over
32+ // the background and bottom layers (the overview).
33+ DimBehind bool
34+ Windows []SceneWindow
32 // Separators are the lines between windows, drawn in slice order with35 // Separators are the lines between windows, drawn in slice order with
33 // the Border colors: tile lines over the tiles, under the floats; a36 // the Border colors: tile lines over the tiles, under the floats; a
34 // float's border right after the float.37 // float's border right after the float.

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 25

Tags 10

↑↓ moveenter openesc close