Repository: jesseduffield/lazygit (language: go)

Issue:
Allow focusing the main view
Currently the main view can't be focused. It can be scrolled with pgup/pgdn or with c-d/c-u, but that's very non-standard and hard to discover. Also, it goes only line by line; it would be useful to be able to scroll by pages.

It would be nice if there was a way to focus the main view, including having a selection that can be moved up/down like in all other views (up/down for line by line, `,`/`.` by page, `<`/`>` for beginning/end). This would also allow searching the main view.

And, together with #3986, it would make it possible to select a line in the main view, and then either hit `e` to edit the file at that line, or `enter` to go to the staging view.

Challenges/open questions:
- Clicking in the main view. Right now, clicking in the diff of a commit does nothing, so this can easily focus the main view and select the clicked line. However, if a file is selected in the files panel, then clicking in the diff currently enters staging (with all the caveats mentioned in #3986), so selecting the line would be a behavior change that might be annoying for existing users; you'd need an extra `enter` key press to enter staging.
- Focusing the main view with the keyboard. This needs a new key, since `enter` is already taken. Maybe `0` would make sense (in analogy to `1`-`5` for the side panels).
  - One detail consideration: which line do we select when hitting the key? Users might already have scrolled with the wheel, in which case it would be annoying to select the first line (and jump back to the top). Maybe we should always select the line that is in the middle of the screen?
- Hitting escape to go "out". In some cases it will be possible to jump from the main view to another view, e.g. from a diff to the staging view. When hitting escape, should we put the focus back to the main view that we came from, or focus the side panel like we do today? I suppose the latter would be easier to implement, since we may have to rerender the main view after coming back from staging.


Discussion / hints:

Fix patch:
diff --git a/docs/Config.md b/docs/Config.md
index a569418e8f4..9eb146d922f 100644
--- a/docs/Config.md
+++ b/docs/Config.md
@@ -501,6 +501,7 @@ keybinding:
       - "3"
       - "4"
       - "5"
+    focusMainView: "0"
     nextMatch: "n"
     prevMatch: "N"
     startSearch: /
diff --git a/docs/keybindings/Keybindings_en.md b/docs/keybindings/Keybindings_en.md
index 0c328580a27..396459478d5 100644
--- a/docs/keybindings/Keybindings_en.md
+++ b/docs/keybindings/Keybindings_en.md
@@ -68,6 +68,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
 | `` ` `` | Toggle file tree view | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory. |
 | `` - `` | Collapse all files | Collapse all directories in the files tree |
 | `` = `` | Expand all files | Expand all directories in the file tree |
+| `` 0 `` | Focus main view |  |
 | `` / `` | Search the current view by text |  |
 
 ## Commit summary
@@ -112,6 +113,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` C `` | Copy (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
 | `` <c-t> `` | Open external diff tool (git difftool) |  |
 | `` * `` | Select commits of current branch |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | View files |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Search the current view by text |  |
@@ -153,6 +155,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` f `` | Fetch | Fetch changes from remote. |
 | `` - `` | Collapse all files | Collapse all directories in the files tree |
 | `` = `` | Expand all files | Expand all directories in the file tree |
+| `` 0 `` | Focus main view |  |
 | `` / `` | Search the current view by text |  |
 
 ## Local branches
@@ -178,6 +181,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` R `` | Rename branch |  |
 | `` u `` | View upstream options | View options relating to the branch's upstream e.g. setting/unsetting the upstream and resetting to the upstream. |
 | `` <c-t> `` | Open external diff tool (git difftool) |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | View commits |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Filter the current view by text |  |
@@ -204,6 +208,9 @@ If you would instead like to start an interactive rebase from the selected commi
 |-----|--------|-------------|
 | `` mouse wheel down (fn+up) `` | Scroll down |  |
 | `` mouse wheel up (fn+down) `` | Scroll up |  |
+| `` <tab> `` | Switch view | Switch to other view (staged/unstaged changes). |
+| `` <esc> `` | Exit back to side panel |  |
+| `` / `` | Search the current view by text |  |
 
 ## Main panel (patch building)
 
@@ -264,6 +271,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` <c-r> `` | Reset copied (cherry-picked) commits selection |  |
 | `` <c-t> `` | Open external diff tool (git difftool) |  |
 | `` * `` | Select commits of current branch |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | View commits |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Filter the current view by text |  |
@@ -282,6 +290,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` s `` | Sort order |  |
 | `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
 | `` <c-t> `` | Open external diff tool (git difftool) |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | View commits |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Filter the current view by text |  |
@@ -297,6 +306,14 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` f `` | Fetch | Fetch updates from the remote repository. This retrieves new commits and branches without merging them into your local branches. |
 | `` / `` | Filter the current view by text |  |
 
+## Secondary
+
+| Key | Action | Info |
+|-----|--------|-------------|
+| `` <tab> `` | Switch view | Switch to other view (staged/unstaged changes). |
+| `` <esc> `` | Exit back to side panel |  |
+| `` / `` | Search the current view by text |  |
+
 ## Stash
 
 | Key | Action | Info |
@@ -306,6 +323,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` d `` | Drop | Remove the stash entry from the stash list. |
 | `` n `` | New branch | Create a new branch from the selected stash entry. This works by git checking out the commit that the stash entry was created from, creating a new branch from that commit, then applying the stash entry to the new branch as an additional commit. |
 | `` r `` | Rename stash |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | View files |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Filter the current view by text |  |
@@ -334,6 +352,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` <c-r> `` | Reset copied (cherry-picked) commits selection |  |
 | `` <c-t> `` | Open external diff tool (git difftool) |  |
 | `` * `` | Select commits of current branch |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | View files |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Search the current view by text |  |
@@ -363,6 +382,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` P `` | Push tag | Push the selected tag to a remote. You'll be prompted to select a remote. |
 | `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
 | `` <c-t> `` | Open external diff tool (git difftool) |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | View commits |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Filter the current view by text |  |
diff --git a/docs/keybindings/Keybindings_ja.md b/docs/keybindings/Keybindings_ja.md
index 612fd18183a..eff44cbb2c5 100644
--- a/docs/keybindings/Keybindings_ja.md
+++ b/docs/keybindings/Keybindings_ja.md
@@ -51,6 +51,14 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
 | `` ] `` | 次のタブ |  |
 | `` [ `` | 前のタブ |  |
 
+## Secondary
+
+| Key | Action | Info |
+|-----|--------|-------------|
+| `` <tab> `` | パネルを切り替え | Switch to other view (staged/unstaged changes). |
+| `` <esc> `` | Exit back to side panel |  |
+| `` / `` | 検索を開始 |  |
+
 ## Stash
 
 | Key | Action | Info |
@@ -60,6 +68,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
 | `` d `` | Drop | Remove the stash entry from the stash list. |
 | `` n `` | 新しいブランチを作成 | Create a new branch from the selected stash entry. This works by git checking out the commit that the stash entry was created from, creating a new branch from that commit, then applying the stash entry to the new branch as an additional commit. |
 | `` r `` | Stashを変更 |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | View files |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Filter the current view by text |  |
@@ -78,6 +87,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
 | `` <c-r> `` | Reset copied (cherry-picked) commits selection |  |
 | `` <c-t> `` | Open external diff tool (git difftool) |  |
 | `` * `` | Select commits of current branch |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | View files |  |
 | `` w `` | View worktree options |  |
 | `` / `` | 検索を開始 |  |
@@ -127,6 +137,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` C `` | コミットをコピー (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
 | `` <c-t> `` | Open external diff tool (git difftool) |  |
 | `` * `` | Select commits of current branch |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | View files |  |
 | `` w `` | View worktree options |  |
 | `` / `` | 検索を開始 |  |
@@ -148,6 +159,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` ` `` | ファイルツリーの表示を切り替え | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory. |
 | `` - `` | Collapse all files | Collapse all directories in the files tree |
 | `` = `` | Expand all files | Expand all directories in the file tree |
+| `` 0 `` | Focus main view |  |
 | `` / `` | 検索を開始 |  |
 
 ## コミットメッセージ
@@ -192,6 +204,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` P `` | タグをpush | Push the selected tag to a remote. You'll be prompted to select a remote. |
 | `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
 | `` <c-t> `` | Open external diff tool (git difftool) |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | コミットを閲覧 |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Filter the current view by text |  |
@@ -226,6 +239,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` f `` | Fetch | Fetch changes from remote. |
 | `` - `` | Collapse all files | Collapse all directories in the files tree |
 | `` = `` | Expand all files | Expand all directories in the file tree |
+| `` 0 `` | Focus main view |  |
 | `` / `` | 検索を開始 |  |
 
 ## ブランチ
@@ -251,6 +265,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` R `` | ブランチ名を変更 |  |
 | `` u `` | View upstream options | View options relating to the branch's upstream e.g. setting/unsetting the upstream and resetting to the upstream. |
 | `` <c-t> `` | Open external diff tool (git difftool) |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | コミットを閲覧 |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Filter the current view by text |  |
@@ -277,6 +292,9 @@ If you would instead like to start an interactive rebase from the selected commi
 |-----|--------|-------------|
 | `` mouse wheel down (fn+up) `` | 下にスクロール |  |
 | `` mouse wheel up (fn+down) `` | 上にスクロール |  |
+| `` <tab> `` | パネルを切り替え | Switch to other view (staged/unstaged changes). |
+| `` <esc> `` | Exit back to side panel |  |
+| `` / `` | 検索を開始 |  |
 
 ## メインパネル (Patch Building)
 
@@ -348,6 +366,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` s `` | 並び替え |  |
 | `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
 | `` <c-t> `` | Open external diff tool (git difftool) |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | コミットを閲覧 |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Filter the current view by text |  |
@@ -366,6 +385,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` <c-r> `` | Reset copied (cherry-picked) commits selection |  |
 | `` <c-t> `` | Open external diff tool (git difftool) |  |
 | `` * `` | Select commits of current branch |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | コミットを閲覧 |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Filter the current view by text |  |
diff --git a/docs/keybindings/Keybindings_ko.md b/docs/keybindings/Keybindings_ko.md
index 8b2f1c01687..c1561441b14 100644
--- a/docs/keybindings/Keybindings_ko.md
+++ b/docs/keybindings/Keybindings_ko.md
@@ -65,10 +65,19 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
 | `` <c-r> `` | Reset cherry-picked (copied) commits selection |  |
 | `` <c-t> `` | Open external diff tool (git difftool) |  |
 | `` * `` | Select commits of current branch |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | 커밋 보기 |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Filter the current view by text |  |
 
+## Secondary
+
+| Key | Action | Info |
+|-----|--------|-------------|
+| `` <tab> `` | 패널 전환 | Switch to other view (staged/unstaged changes). |
+| `` <esc> `` | Exit back to side panel |  |
+| `` / `` | 검색 시작 |  |
+
 ## Stash
 
 | Key | Action | Info |
@@ -78,6 +87,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
 | `` d `` | Drop | Remove the stash entry from the stash list. |
 | `` n `` | 새 브랜치 생성 | Create a new branch from the selected stash entry. This works by git checking out the commit that the stash entry was created from, creating a new branch from that commit, then applying the stash entry to the new branch as an additional commit. |
 | `` r `` | Rename stash |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | View selected item's files |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Filter the current view by text |  |
@@ -96,6 +106,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
 | `` <c-r> `` | Reset cherry-picked (copied) commits selection |  |
 | `` <c-t> `` | Open external diff tool (git difftool) |  |
 | `` * `` | Select commits of current branch |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | View selected item's files |  |
 | `` w `` | View worktree options |  |
 | `` / `` | 검색 시작 |  |
@@ -140,6 +151,9 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
 |-----|--------|-------------|
 | `` mouse wheel down (fn+up) `` | 아래로 스크롤 |  |
 | `` mouse wheel up (fn+down) `` | 위로 스크롤 |  |
+| `` <tab> `` | 패널 전환 | Switch to other view (staged/unstaged changes). |
+| `` <esc> `` | Exit back to side panel |  |
+| `` / `` | 검색 시작 |  |
 
 ## 메인 패널 (Patch Building)
 
@@ -201,6 +215,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
 | `` R `` | 브랜치 이름 변경 |  |
 | `` u `` | View upstream options | View options relating to the branch's upstream e.g. setting/unsetting the upstream and resetting to the upstream. |
 | `` <c-t> `` | Open external diff tool (git difftool) |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | 커밋 보기 |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Filter the current view by text |  |
@@ -254,6 +269,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
 | `` s `` | Sort order |  |
 | `` g `` | View reset options | View reset options (soft/mixed/hard) for resetting onto selected item. |
 | `` <c-t> `` | Open external diff tool (git difftool) |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | 커밋 보기 |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Filter the current view by text |  |
@@ -293,6 +309,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` C `` | 커밋을 복사 (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
 | `` <c-t> `` | Open external diff tool (git difftool) |  |
 | `` * `` | Select commits of current branch |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | View selected item's files |  |
 | `` w `` | View worktree options |  |
 | `` / `` | 검색 시작 |  |
@@ -314,6 +331,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` ` `` | 파일 트리뷰로 전환 | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory. |
 | `` - `` | Collapse all files | Collapse all directories in the files tree |
 | `` = `` | Expand all files | Expand all directories in the file tree |
+| `` 0 `` | Focus main view |  |
 | `` / `` | 검색 시작 |  |
 
 ## 커밋메시지
@@ -334,6 +352,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` P `` | 태그를 push | Push the selected tag to a remote. You'll be prompted to select a remote. |
 | `` g `` | 초기화 | View reset options (soft/mixed/hard) for resetting onto selected item. |
 | `` <c-t> `` | Open external diff tool (git difftool) |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | 커밋 보기 |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Filter the current view by text |  |
@@ -368,6 +387,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` f `` | Fetch | Fetch changes from remote. |
 | `` - `` | Collapse all files | Collapse all directories in the files tree |
 | `` = `` | Expand all files | Expand all directories in the file tree |
+| `` 0 `` | Focus main view |  |
 | `` / `` | 검색 시작 |  |
 
 ## 확인 패널
diff --git a/docs/keybindings/Keybindings_nl.md b/docs/keybindings/Keybindings_nl.md
index 469ac8db8e7..1752329e71c 100644
--- a/docs/keybindings/Keybindings_nl.md
+++ b/docs/keybindings/Keybindings_nl.md
@@ -81,6 +81,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
 | `` f `` | Fetch | Fetch changes from remote. |
 | `` - `` | Collapse all files | Collapse all directories in the files tree |
 | `` = `` | Expand all files | Expand all directories in the file tree |
+| `` 0 `` | Focus main view |  |
 | `` / `` | Start met zoeken |  |
 
 ## Bevestigingspaneel
@@ -113,6 +114,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
 | `` R `` | Hernoem branch |  |
 | `` u `` | View upstream options | View options relating to the branch's upstream e.g. setting/unsetting the upstream and resetting to the upstream. |
 | `` <c-t> `` | Open external diff tool (git difftool) |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | Bekijk commits |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Filter the current view by text |  |
@@ -141,6 +143,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
 | `` ` `` | Toggle bestandsboom weergave | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory. |
 | `` - `` | Collapse all files | Collapse all directories in the files tree |
 | `` = `` | Expand all files | Expand all directories in the file tree |
+| `` 0 `` | Focus main view |  |
 | `` / `` | Start met zoeken |  |
 
 ## Commits
@@ -178,6 +181,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` C `` | Kopieer commit (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
 | `` <c-t> `` | Open external diff tool (git difftool) |  |
 | `` * `` | Select commits of current branch |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | Bekijk gecommite bestanden |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Start met zoeken |  |
@@ -212,6 +216,9 @@ If you would instead like to start an interactive rebase from the selected commi
 |-----|--------|-------------|
 | `` mouse wheel down (fn+up) `` | Scroll omlaag |  |
 | `` mouse wheel up (fn+down) `` | Scroll omhoog |  |
+| `` <tab> `` | Ga naar een ander paneel | Switch to other view (staged/unstaged changes). |
+| `` <esc> `` | Exit back to side panel |  |
+| `` / `` | Start met zoeken |  |
 
 ## Patch bouwen
 
@@ -242,6 +249,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` <c-r> `` | Reset cherry-picked (gekopieerde) commits selectie |  |
 | `` <c-t> `` | Open external diff tool (git difftool) |  |
 | `` * `` | Select commits of current branch |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | Bekijk commits |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Filter the current view by text |  |
@@ -260,6 +268,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` s `` | Sort order |  |
 | `` g `` | Bekijk reset opties | View reset options (soft/mixed/hard) for resetting onto selected item. |
 | `` <c-t> `` | Open external diff tool (git difftool) |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | Bekijk commits |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Filter the current view by text |  |
@@ -275,6 +284,14 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` f `` | Fetch | Fetch remote |
 | `` / `` | Filter the current view by text |  |
 
+## Secondary
+
+| Key | Action | Info |
+|-----|--------|-------------|
+| `` <tab> `` | Ga naar een ander paneel | Switch to other view (staged/unstaged changes). |
+| `` <esc> `` | Exit back to side panel |  |
+| `` / `` | Start met zoeken |  |
+
 ## Staging
 
 | Key | Action | Info |
@@ -306,6 +323,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` d `` | Laten vallen | Remove the stash entry from the stash list. |
 | `` n `` | Nieuwe branch | Create a new branch from the selected stash entry. This works by git checking out the commit that the stash entry was created from, creating a new branch from that commit, then applying the stash entry to the new branch as an additional commit. |
 | `` r `` | Rename stash |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | Bekijk gecommite bestanden |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Filter the current view by text |  |
@@ -334,6 +352,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` <c-r> `` | Reset cherry-picked (gekopieerde) commits selectie |  |
 | `` <c-t> `` | Open external diff tool (git difftool) |  |
 | `` * `` | Select commits of current branch |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | Bekijk gecommite bestanden |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Start met zoeken |  |
@@ -363,6 +382,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` P `` | Push tag | Push the selected tag to a remote. You'll be prompted to select a remote. |
 | `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
 | `` <c-t> `` | Open external diff tool (git difftool) |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | Bekijk commits |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Filter the current view by text |  |
diff --git a/docs/keybindings/Keybindings_pl.md b/docs/keybindings/Keybindings_pl.md
index f940f5ad47c..67a4ba27797 100644
--- a/docs/keybindings/Keybindings_pl.md
+++ b/docs/keybindings/Keybindings_pl.md
@@ -86,10 +86,19 @@ Jeśli chcesz zamiast tego rozpocząć interaktywny rebase od wybranego commita,
 | `` C `` | Kopiuj (cherry-pick) | Oznacz commit jako skopiowany. Następnie, w widoku lokalnych commitów, możesz nacisnąć `V`, aby wkleić (cherry-pick) skopiowane commity do sprawdzonej gałęzi. W dowolnym momencie możesz nacisnąć `<esc>`, aby anulować zaznaczenie. |
 | `` <c-t> `` | Otwórz zewnętrzne narzędzie różnic (git difftool) |  |
 | `` * `` | Select commits of current branch |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | Wyświetl pliki |  |
 | `` w `` | Zobacz opcje drzewa pracy |  |
 | `` / `` | Szukaj w bieżącym widoku po tekście |  |
 
+## Dodatkowy
+
+| Key | Action | Info |
+|-----|--------|-------------|
+| `` <tab> `` | Przełącz widok | Przełącz na inny widok (zatwierdzone/niezatwierdzone zmiany). |
+| `` <esc> `` | Exit back to side panel |  |
+| `` / `` | Szukaj w bieżącym widoku po tekście |  |
+
 ## Drzewa pracy
 
 | Key | Action | Info |
@@ -138,6 +147,7 @@ Jeśli chcesz zamiast tego rozpocząć interaktywny rebase od wybranego commita,
 | `` R `` | Zmień nazwę gałęzi |  |
 | `` u `` | Pokaż opcje upstream | Pokaż opcje dotyczące upstream gałęzi, np. ustawianie/usuwanie upstream i resetowanie do upstream. |
 | `` <c-t> `` | Otwórz zewnętrzne narzędzie różnic (git difftool) |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | Pokaż commity |  |
 | `` w `` | Zobacz opcje drzewa pracy |  |
 | `` / `` | Filtruj bieżący widok po tekście |  |
@@ -156,6 +166,9 @@ Jeśli chcesz zamiast tego rozpocząć interaktywny rebase od wybranego commita,
 |-----|--------|-------------|
 | `` mouse wheel down (fn+up) `` | Przewiń w dół |  |
 | `` mouse wheel up (fn+down) `` | Przewiń w górę |  |
+| `` <tab> `` | Przełącz widok | Przełącz na inny widok (zatwierdzone/niezatwierdzone zmiany). |
+| `` <esc> `` | Exit back to side panel |  |
+| `` / `` | Szukaj w bieżącym widoku po tekście |  |
 
 ## Panel główny (scalanie)
 
@@ -232,6 +245,7 @@ Jeśli chcesz zamiast tego rozpocząć interaktywny rebase od wybranego commita,
 | `` f `` | Pobierz | Pobierz zmiany ze zdalnego serwera. |
 | `` - `` | Collapse all files | Collapse all directories in the files tree |
 | `` = `` | Expand all files | Expand all directories in the file tree |
+| `` 0 `` | Focus main view |  |
 | `` / `` | Szukaj w bieżącym widoku po tekście |  |
 
 ## Pliki commita
@@ -251,6 +265,7 @@ Jeśli chcesz zamiast tego rozpocząć interaktywny rebase od wybranego commita,
 | `` ` `` | Przełącz widok drzewa plików | Przełącz widok plików między płaskim a drzewem. Płaski układ pokazuje wszystkie ścieżki plików na jednej liście, układ drzewa grupuje pliki według katalogów. |
 | `` - `` | Collapse all files | Collapse all directories in the files tree |
 | `` = `` | Expand all files | Expand all directories in the file tree |
+| `` 0 `` | Focus main view |  |
 | `` / `` | Szukaj w bieżącym widoku po tekście |  |
 
 ## Podsumowanie commita
@@ -274,6 +289,7 @@ Jeśli chcesz zamiast tego rozpocząć interaktywny rebase od wybranego commita,
 | `` <c-r> `` | Resetuj wybrane (cherry-picked) commity |  |
 | `` <c-t> `` | Otwórz zewnętrzne narzędzie różnic (git difftool) |  |
 | `` * `` | Select commits of current branch |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | Pokaż commity |  |
 | `` w `` | Zobacz opcje drzewa pracy |  |
 | `` / `` | Filtruj bieżący widok po tekście |  |
@@ -287,6 +303,7 @@ Jeśli chcesz zamiast tego rozpocząć interaktywny rebase od wybranego commita,
 | `` d `` | Usuń | Usuń wpis schowka z listy schowka. |
 | `` n `` | Nowa gałąź | Utwórz nową gałąź z wybranego wpisu schowka. Działa poprzez przełączenie git na commit, na którym wpis schowka został utworzony, tworzenie nowej gałęzi z tego commita, a następnie zastosowanie wpisu schowka do nowej gałęzi jako dodatkowego commita. |
 | `` r `` | Zmień nazwę schowka |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | Wyświetl pliki |  |
 | `` w `` | Zobacz opcje drzewa pracy |  |
 | `` / `` | Filtruj bieżący widok po tekście |  |
@@ -315,6 +332,7 @@ Jeśli chcesz zamiast tego rozpocząć interaktywny rebase od wybranego commita,
 | `` <c-r> `` | Resetuj wybrane (cherry-picked) commity |  |
 | `` <c-t> `` | Otwórz zewnętrzne narzędzie różnic (git difftool) |  |
 | `` * `` | Select commits of current branch |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | Wyświetl pliki |  |
 | `` w `` | Zobacz opcje drzewa pracy |  |
 | `` / `` | Szukaj w bieżącym widoku po tekście |  |
@@ -344,6 +362,7 @@ Jeśli chcesz zamiast tego rozpocząć interaktywny rebase od wybranego commita,
 | `` P `` | Wyślij tag | Wyślij wybrany tag do zdalnego. Zostaniesz poproszony o wybranie zdalnego. |
 | `` g `` | Reset | Wyświetl opcje resetu (miękki/mieszany/twardy) do wybranego elementu. |
 | `` <c-t> `` | Otwórz zewnętrzne narzędzie różnic (git difftool) |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | Pokaż commity |  |
 | `` w `` | Zobacz opcje drzewa pracy |  |
 | `` / `` | Filtruj bieżący widok po tekście |  |
@@ -373,6 +392,7 @@ Jeśli chcesz zamiast tego rozpocząć interaktywny rebase od wybranego commita,
 | `` s `` | Kolejność sortowania |  |
 | `` g `` | Reset | Wyświetl opcje resetu (miękki/mieszany/twardy) do wybranego elementu. |
 | `` <c-t> `` | Otwórz zewnętrzne narzędzie różnic (git difftool) |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | Pokaż commity |  |
 | `` w `` | Zobacz opcje drzewa pracy |  |
 | `` / `` | Filtruj bieżący widok po tekście |  |
diff --git a/docs/keybindings/Keybindings_pt.md b/docs/keybindings/Keybindings_pt.md
index 6ea4cb215a5..ccf5524b060 100644
--- a/docs/keybindings/Keybindings_pt.md
+++ b/docs/keybindings/Keybindings_pt.md
@@ -83,6 +83,7 @@ Veja a documentação:
 | `` f `` | Buscar | Buscar alterações do controle remoto. |
 | `` - `` | Recolher todos os arquivos | Recolher todos os diretórios na árvore de arquivos |
 | `` = `` | Expandir todos os arquivos | Expandir todos os diretórios na árvore do arquivo |
+| `` 0 `` | Focus main view |  |
 | `` / `` | Search the current view by text |  |
 
 ## Branches locais
@@ -108,6 +109,7 @@ Veja a documentação:
 | `` R `` | Rename branch |  |
 | `` u `` | View upstream options | View options relating to the branch's upstream e.g. setting/unsetting the upstream and resetting to the upstream. |
 | `` <c-t> `` | Abrir ferramenta de diff externa (git difftool) |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | View commits |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Filter the current view by text |  |
@@ -126,6 +128,7 @@ Veja a documentação:
 | `` s `` | Sort order |  |
 | `` g `` | Restaurar | Ver opções de redefinição (soft/mixed/hard) para redefinir para o item selecionado. |
 | `` <c-t> `` | Abrir ferramenta de diff externa (git difftool) |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | View commits |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Filter the current view by text |  |
@@ -147,6 +150,7 @@ Veja a documentação:
 | `` ` `` | Alternar exibição de árvore de arquivo | Alternar a visualização de arquivo entre layout plano e layout de árvore. Layout plano mostra todos os caminhos de arquivo em uma única lista, layout de árvore agrupa arquivos por diretório. |
 | `` - `` | Recolher todos os arquivos | Recolher todos os diretórios na árvore de arquivos |
 | `` = `` | Expandir todos os arquivos | Expandir todos os diretórios na árvore do arquivo |
+| `` 0 `` | Focus main view |  |
 | `` / `` | Search the current view by text |  |
 
 ## Commits
@@ -184,6 +188,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` C `` | Copiar (cherry-pick) | Marcar commit como copiado. Então, dentro da visualização local de commits, você pode pressionar `V` para colar (cherry-pick) o(s) commit(s) copiado(s) em seu branch de check-out. A qualquer momento você pode pressionar `<esc>` para cancelar a seleção. |
 | `` <c-t> `` | Abrir ferramenta de diff externa (git difftool) |  |
 | `` * `` | Select commits of current branch |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | Ver arquivos |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Search the current view by text |  |
@@ -206,6 +211,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` P `` | Push tag | Push the selected tag to a remote. You'll be prompted to select a remote. |
 | `` g `` | Restaurar | Ver opções de redefinição (soft/mixed/hard) para redefinir para o item selecionado. |
 | `` <c-t> `` | Abrir ferramenta de diff externa (git difftool) |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | View commits |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Filter the current view by text |  |
@@ -224,6 +230,9 @@ If you would instead like to start an interactive rebase from the selected commi
 |-----|--------|-------------|
 | `` mouse wheel down (fn+up) `` | Rolar para baixo |  |
 | `` mouse wheel up (fn+down) `` | Rolar para cima |  |
+| `` <tab> `` | Mudar de visão | Alternar para outra visão (staged/não processadas alterações). |
+| `` <esc> `` | Exit back to side panel |  |
+| `` / `` | Search the current view by text |  |
 
 ## Painel Principal (preparação)
 
@@ -294,6 +303,7 @@ Veja a documentação:
 | `` <c-r> `` | Reset copied (cherry-picked) commits selection |  |
 | `` <c-t> `` | Abrir ferramenta de diff externa (git difftool) |  |
 | `` * `` | Select commits of current branch |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | View commits |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Filter the current view by text |  |
@@ -309,6 +319,14 @@ Veja a documentação:
 | `` f `` | Buscar | Fetch updates from the remote repository. This retrieves new commits and branches without merging them into your local branches. |
 | `` / `` | Filter the current view by text |  |
 
+## Secundário
+
+| Key | Action | Info |
+|-----|--------|-------------|
+| `` <tab> `` | Mudar de visão | Alternar para outra visão (staged/não processadas alterações). |
+| `` <esc> `` | Exit back to side panel |  |
+| `` / `` | Search the current view by text |  |
+
 ## Stash
 
 | Key | Action | Info |
@@ -318,6 +336,7 @@ Veja a documentação:
 | `` d `` | Descartar | Remova a entrada do stash da lista de armazenamento. |
 | `` n `` | Nova branch | Criar um novo ramo a partir da entrada de lixo selecionada. Isso funciona verificando o commit do qual a entrada de lixo foi criada, criar um novo branch a partir desse commit e, em seguida, aplicar a entrada de lixo ao novo branch como um commit adicional. |
 | `` r `` | Renomear o stasj |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | Ver arquivos |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Filter the current view by text |  |
@@ -346,6 +365,7 @@ Veja a documentação:
 | `` <c-r> `` | Reset copied (cherry-picked) commits selection |  |
 | `` <c-t> `` | Abrir ferramenta de diff externa (git difftool) |  |
 | `` * `` | Select commits of current branch |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | Ver arquivos |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Search the current view by text |  |
diff --git a/docs/keybindings/Keybindings_ru.md b/docs/keybindings/Keybindings_ru.md
index 28c6a087256..b75fc0d80c2 100644
--- a/docs/keybindings/Keybindings_ru.md
+++ b/docs/keybindings/Keybindings_ru.md
@@ -61,6 +61,14 @@ _Связки клавиш_
 | `` d `` | Remove | Remove the selected worktree. This will both delete the worktree's directory, as well as metadata about the worktree in the .git directory. |
 | `` / `` | Filter the current view by text |  |
 
+## Вторичный
+
+| Key | Action | Info |
+|-----|--------|-------------|
+| `` <tab> `` | Переключиться на другую панель (проиндексированные/непроиндексированные изменения) | Switch to other view (staged/unstaged changes). |
+| `` <esc> `` | Exit back to side panel |  |
+| `` / `` | Найти |  |
+
 ## Главная панель (Индексирование)
 
 | Key | Action | Info |
@@ -89,6 +97,9 @@ _Связки клавиш_
 |-----|--------|-------------|
 | `` mouse wheel down (fn+up) `` | Прокрутить вниз |  |
 | `` mouse wheel up (fn+down) `` | Прокрутить вверх |  |
+| `` <tab> `` | Переключиться на другую панель (проиндексированные/непроиндексированные изменения) | Switch to other view (staged/unstaged changes). |
+| `` <esc> `` | Exit back to side panel |  |
+| `` / `` | Найти |  |
 
 ## Главная панель (Слияние)
 
@@ -135,6 +146,7 @@ _Связки клавиш_
 | `` <c-r> `` | Сбросить отобранную (скопированную | cherry-picked) выборку коммитов |  |
 | `` <c-t> `` | Open external diff tool (git difftool) |  |
 | `` * `` | Select commits of current branch |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | Просмотреть коммиты |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Filter the current view by text |  |
@@ -174,6 +186,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` C `` | Скопировать отобранные коммит (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
 | `` <c-t> `` | Open external diff tool (git difftool) |  |
 | `` * `` | Select commits of current branch |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | Просмотреть файлы выбранного элемента |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Найти |  |
@@ -201,6 +214,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` R `` | Переименовать ветку |  |
 | `` u `` | View upstream options | View options relating to the branch's upstream e.g. setting/unsetting the upstream and resetting to the upstream. |
 | `` <c-t> `` | Open external diff tool (git difftool) |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | Просмотреть коммиты |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Filter the current view by text |  |
@@ -234,6 +248,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` <c-r> `` | Сбросить отобранную (скопированную | cherry-picked) выборку коммитов |  |
 | `` <c-t> `` | Open external diff tool (git difftool) |  |
 | `` * `` | Select commits of current branch |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | Просмотреть файлы выбранного элемента |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Найти |  |
@@ -276,6 +291,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` ` `` | Переключить вид дерева файлов | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory. |
 | `` - `` | Collapse all files | Collapse all directories in the files tree |
 | `` = `` | Expand all files | Expand all directories in the file tree |
+| `` 0 `` | Focus main view |  |
 | `` / `` | Найти |  |
 
 ## Статус
@@ -299,6 +315,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` P `` | Отправить тег | Push the selected tag to a remote. You'll be prompted to select a remote. |
 | `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
 | `` <c-t> `` | Open external diff tool (git difftool) |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | Просмотреть коммиты |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Filter the current view by text |  |
@@ -317,6 +334,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` s `` | Порядок сортировки |  |
 | `` g `` | Просмотреть параметры сброса | View reset options (soft/mixed/hard) for resetting onto selected item. |
 | `` <c-t> `` | Open external diff tool (git difftool) |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | Просмотреть коммиты |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Filter the current view by text |  |
@@ -362,6 +380,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` f `` | Получить изменения | Fetch changes from remote. |
 | `` - `` | Collapse all files | Collapse all directories in the files tree |
 | `` = `` | Expand all files | Expand all directories in the file tree |
+| `` 0 `` | Focus main view |  |
 | `` / `` | Найти |  |
 
 ## Хранилище
@@ -373,6 +392,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` d `` | Удалить припрятанные изменения из хранилища | Remove the stash entry from the stash list. |
 | `` n `` | Новая ветка | Create a new branch from the selected stash entry. This works by git checking out the commit that the stash entry was created from, creating a new branch from that commit, then applying the stash entry to the new branch as an additional commit. |
 | `` r `` | Переименовать хранилище |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | Просмотреть файлы выбранного элемента |  |
 | `` w `` | View worktree options |  |
 | `` / `` | Filter the current view by text |  |
diff --git a/docs/keybindings/Keybindings_zh-CN.md b/docs/keybindings/Keybindings_zh-CN.md
index bea70be973c..4cdda3dd094 100644
--- a/docs/keybindings/Keybindings_zh-CN.md
+++ b/docs/keybindings/Keybindings_zh-CN.md
@@ -65,6 +65,7 @@ _图例：`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_
 | `` <c-r> `` | 重置已拣选(复制)的提交 |  |
 | `` <c-t> `` | 使用外部差异比较工具(git difftool) |  |
 | `` * `` | Select commits of current branch |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | 查看提交 |  |
 | `` w `` | 查看工作区选项 |  |
 | `` / `` | 通过文本过滤当前视图 |  |
@@ -83,6 +84,7 @@ _图例：`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_
 | `` <c-r> `` | 重置已拣选(复制)的提交 |  |
 | `` <c-t> `` | 使用外部差异比较工具(git difftool) |  |
 | `` * `` | Select commits of current branch |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | 查看提交的文件 |  |
 | `` w `` | 查看工作区选项 |  |
 | `` / `` | 开始搜索 |  |
@@ -146,6 +148,7 @@ _图例：`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_
 | `` C `` | 复制提交(拣选) | 标记提交为已复制。然后，在本地提交视图中，你可以按 `V` (Cherry-Pick) 将已复制的提交粘贴到已检出的分支中。任何时候都可以按 `<esc>` 来取消选择。 |
 | `` <c-t> `` | 使用外部差异比较工具(git difftool) |  |
 | `` * `` | Select commits of current branch |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | 查看提交的文件 |  |
 | `` w `` | 查看工作区选项 |  |
 | `` / `` | 开始搜索 |  |
@@ -174,6 +177,7 @@ _图例：`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_
 | `` ` `` | 切换文件树视图 | 在平铺部署与树布局之间切换文件视图。平铺布局在一个列表中展示所有文件路径，树布局则根据目录分组展示。 |
 | `` - `` | 折叠全部文件 | 折叠文件树中的全部目录 |
 | `` = `` | 展开全部文件 | 展开文件树中的全部目录 |
+| `` 0 `` | Focus main view |  |
 | `` / `` | 开始搜索 |  |
 
 ## 文件
@@ -206,6 +210,7 @@ _图例：`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_
 | `` f `` | 抓取 | 从远程获取变更 |
 | `` - `` | 折叠全部文件 | 折叠文件树中的全部目录 |
 | `` = `` | 展开全部文件 | 展开文件树中的全部目录 |
+| `` 0 `` | Focus main view |  |
 | `` / `` | 开始搜索 |  |
 
 ## 本地分支
@@ -231,6 +236,7 @@ _图例：`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_
 | `` R `` | 重命名分支 |  |
 | `` u `` | 查看上游选项 | 查看与分支上游相关的选项，例如设置/取消设置上游和重置为上游。 |
 | `` <c-t> `` | 使用外部差异比较工具(git difftool) |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | 查看提交 |  |
 | `` w `` | 查看工作区选项 |  |
 | `` / `` | 通过文本过滤当前视图 |  |
@@ -261,10 +267,19 @@ _图例：`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_
 | `` P `` | 推送标签 | 推送选择的标签到远端。你将在弹窗中选择一个远端。 |
 | `` g `` | 重置 | 查看重置选项 (soft/mixed/hard) 用于重置到选择项 |
 | `` <c-t> `` | 使用外部差异比较工具(git difftool) |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | 查看提交 |  |
 | `` w `` | 查看工作区选项 |  |
 | `` / `` | 通过文本过滤当前视图 |  |
 
+## 次要
+
+| Key | Action | Info |
+|-----|--------|-------------|
+| `` <tab> `` | 切换到其他面板 | 切换到其他视图（已暂存/未暂存的变更） |
+| `` <esc> `` | Exit back to side panel |  |
+| `` / `` | 开始搜索 |  |
+
 ## 正在合并
 
 | Key | Action | Info |
@@ -309,6 +324,9 @@ _图例：`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_
 |-----|--------|-------------|
 | `` mouse wheel down (fn+up) `` | 向下滚动 |  |
 | `` mouse wheel up (fn+down) `` | 向上滚动 |  |
+| `` <tab> `` | 切换到其他面板 | 切换到其他视图（已暂存/未暂存的变更） |
+| `` <esc> `` | Exit back to side panel |  |
+| `` / `` | 开始搜索 |  |
 
 ## 状态
 
@@ -344,6 +362,7 @@ _图例：`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_
 | `` d `` | 删除 | 从贮藏列表中删除该贮藏项 |
 | `` n `` | 新分支 | 从选定的贮藏项创建一个新分支。这是通过 git 检查创建贮藏项的提交，从该提交创建一个新分支，然后将贮藏项作为附加提交应用到新分支来实现的。 |
 | `` r `` | 重命名贮藏 |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | 查看提交的文件 |  |
 | `` w `` | 查看工作区选项 |  |
 | `` / `` | 通过文本过滤当前视图 |  |
@@ -373,6 +392,7 @@ _图例：`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_
 | `` s `` | 排序 |  |
 | `` g `` | 查看重置选项 | 查看重置选项 (soft/mixed/hard) 用于重置到选择项 |
 | `` <c-t> `` | 使用外部差异比较工具(git difftool) |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | 查看提交 |  |
 | `` w `` | 查看工作区选项 |  |
 | `` / `` | 通过文本过滤当前视图 |  |
diff --git a/docs/keybindings/Keybindings_zh-TW.md b/docs/keybindings/Keybindings_zh-TW.md
index 913a76a7c82..8312ca98bce 100644
--- a/docs/keybindings/Keybindings_zh-TW.md
+++ b/docs/keybindings/Keybindings_zh-TW.md
@@ -72,6 +72,9 @@ _說明：`<c-b>` 表示 Ctrl＋B、`<a-b>` 表示 Alt＋B，`B`表示 Shift＋B
 |-----|--------|-------------|
 | `` mouse wheel down (fn+up) `` | 向下捲動 |  |
 | `` mouse wheel up (fn+down) `` | 向上捲動 |  |
+| `` <tab> `` | 切換至另一個面板 (已預存/未預存更改) | Switch to other view (staged/unstaged changes). |
+| `` <esc> `` | Exit back to side panel |  |
+| `` / `` | 搜尋 |  |
 
 ## 主面板（合併）
 
@@ -133,6 +136,7 @@ _說明：`<c-b>` 表示 Ctrl＋B、`<a-b>` 表示 Alt＋B，`B`表示 Shift＋B
 | `` <c-r> `` | 重設選定的揀選 (複製) 提交 |  |
 | `` <c-t> `` | 開啟外部差異工具 (git difftool) |  |
 | `` * `` | Select commits of current branch |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | 檢視所選項目的檔案 |  |
 | `` w `` | 檢視工作目錄選項 |  |
 | `` / `` | 搜尋 |  |
@@ -196,6 +200,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` C `` | 複製提交 (揀選) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
 | `` <c-t> `` | 開啟外部差異工具 (git difftool) |  |
 | `` * `` | Select commits of current branch |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | 檢視所選項目的檔案 |  |
 | `` w `` | 檢視工作目錄選項 |  |
 | `` / `` | 搜尋 |  |
@@ -224,6 +229,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` ` `` | 顯示檔案樹狀視圖 | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory. |
 | `` - `` | Collapse all files | Collapse all directories in the files tree |
 | `` = `` | Expand all files | Expand all directories in the file tree |
+| `` 0 `` | Focus main view |  |
 | `` / `` | 搜尋 |  |
 
 ## 收藏 (Stash)
@@ -235,6 +241,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` d `` | 捨棄 | Remove the stash entry from the stash list. |
 | `` n `` | 新分支 | Create a new branch from the selected stash entry. This works by git checking out the commit that the stash entry was created from, creating a new branch from that commit, then applying the stash entry to the new branch as an additional commit. |
 | `` r `` | 重新命名收藏 |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | 檢視所選項目的檔案 |  |
 | `` w `` | 檢視工作目錄選項 |  |
 | `` / `` | 搜尋 |  |
@@ -253,6 +260,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` <c-r> `` | 重設選定的揀選 (複製) 提交 |  |
 | `` <c-t> `` | 開啟外部差異工具 (git difftool) |  |
 | `` * `` | Select commits of current branch |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | 檢視提交 |  |
 | `` w `` | 檢視工作目錄選項 |  |
 | `` / `` | 搜尋 |  |
@@ -280,6 +288,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` R `` | 重新命名分支 |  |
 | `` u `` | 檢視遠端設定 | 檢視有關遠端分支的設定（例如重設至遠端） |
 | `` <c-t> `` | 開啟外部差異工具 (git difftool) |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | 檢視提交 |  |
 | `` w `` | 檢視工作目錄選項 |  |
 | `` / `` | 搜尋 |  |
@@ -295,6 +304,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` P `` | 推送標籤 | Push the selected tag to a remote. You'll be prompted to select a remote. |
 | `` g `` | 重設 | View reset options (soft/mixed/hard) for resetting onto selected item. |
 | `` <c-t> `` | 開啟外部差異工具 (git difftool) |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | 檢視提交 |  |
 | `` w `` | 檢視工作目錄選項 |  |
 | `` / `` | 搜尋 |  |
@@ -329,6 +339,15 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` f `` | 擷取 | 同步遠端異動 |
 | `` - `` | Collapse all files | Collapse all directories in the files tree |
 | `` = `` | Expand all files | Expand all directories in the file tree |
+| `` 0 `` | Focus main view |  |
+| `` / `` | 搜尋 |  |
+
+## 次要
+
+| Key | Action | Info |
+|-----|--------|-------------|
+| `` <tab> `` | 切換至另一個面板 (已預存/未預存更改) | Switch to other view (staged/unstaged changes). |
+| `` <esc> `` | Exit back to side panel |  |
 | `` / `` | 搜尋 |  |
 
 ## 狀態
@@ -373,6 +392,7 @@ If you would instead like to start an interactive rebase from the selected commi
 | `` s `` | 排序規則 |  |
 | `` g `` | 檢視重設選項 | View reset options (soft/mixed/hard) for resetting onto selected item. |
 | `` <c-t> `` | 開啟外部差異工具 (git difftool) |  |
+| `` 0 `` | Focus main view |  |
 | `` <enter> `` | 檢視提交 |  |
 | `` w `` | 檢視工作目錄選項 |  |
 | `` / `` | 搜尋 |  |
diff --git a/go.mod b/go.mod
index d854d07c92a..ae9990519bb 100644
--- a/go.mod
+++ b/go.mod
@@ -15,7 +15,7 @@ require (
 	github.com/integrii/flaggy v1.4.0
 	github.com/jesseduffield/generics v0.0.0-20250406224309-4f541cb84918
 	github.com/jesseduffield/go-git/v5 v5.14.1-0.20250407170251-e1a013310ccd
-	github.com/jesseduffield/gocui v0.3.1-0.20250408140206-7f1bb9232647
+	github.com/jesseduffield/gocui v0.3.1-0.20250421160159-82c9aaeba2b9
 	github.com/jesseduffield/kill v0.0.0-20250101124109-e216ddbe133a
 	github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5
 	github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e
diff --git a/go.sum b/go.sum
index e62940c3fe2..98389ca8175 100644
--- a/go.sum
+++ b/go.sum
@@ -194,8 +194,8 @@ github.com/jesseduffield/generics v0.0.0-20250406224309-4f541cb84918 h1:meoUDZGF
 github.com/jesseduffield/generics v0.0.0-20250406224309-4f541cb84918/go.mod h1:+LLj9/WUPAP8LqCchs7P+7X0R98HiFujVFANdNaxhGk=
 github.com/jesseduffield/go-git/v5 v5.14.1-0.20250407170251-e1a013310ccd h1:ViKj6qth8FgcIWizn9KiACWwPemWSymx62OPN0tHT+Q=
 github.com/jesseduffield/go-git/v5 v5.14.1-0.20250407170251-e1a013310ccd/go.mod h1:lRhCiBr6XjQrvcQVa+UYsy/99d3wMXn/a0nSQlhnhlA=
-github.com/jesseduffield/gocui v0.3.1-0.20250408140206-7f1bb9232647 h1:XpoXCyLGCB4mczPc1q/8dJefr6OB1xa7jyhH5a5eJqY=
-github.com/jesseduffield/gocui v0.3.1-0.20250408140206-7f1bb9232647/go.mod h1:sLIyZ2J42R6idGdtemZzsiR3xY5EF0KsvYEGh3dQv3s=
+github.com/jesseduffield/gocui v0.3.1-0.20250421160159-82c9aaeba2b9 h1:k23sCKHCNpAvwJP8Yr16CBUItuarmUHBGH7FaAm2glc=
+github.com/jesseduffield/gocui v0.3.1-0.20250421160159-82c9aaeba2b9/go.mod h1:sLIyZ2J42R6idGdtemZzsiR3xY5EF0KsvYEGh3dQv3s=
 github.com/jesseduffield/kill v0.0.0-20250101124109-e216ddbe133a h1:UDeJ3EBk04bXDLOPvuqM3on8HvyJfISw0+UMqW+0a4g=
 github.com/jesseduffield/kill v0.0.0-20250101124109-e216ddbe133a/go.mod h1:FSWDLKT0NQpntbDd1H3lbz51fhCVlMzy/J0S6nM727Q=
 github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5 h1:CDuQmfOjAtb1Gms6a1p5L2P8RhbLUq5t8aL7PiQd2uY=
diff --git a/pkg/commands/patch/patch.go b/pkg/commands/patch/patch.go
index d785fe49e27..5e4f9a84665 100644
--- a/pkg/commands/patch/patch.go
+++ b/pkg/commands/patch/patch.go
@@ -1,7 +1,6 @@
 package patch
 
 import (
-	"github.com/jesseduffield/lazygit/pkg/utils"
 	"github.com/samber/lo"
 )
 
@@ -54,7 +53,7 @@ func (self *Patch) Lines() []*PatchLine {
 
 // Returns the patch line index of the first line in the given hunk
 func (self *Patch) HunkStartIdx(hunkIndex int) int {
-	hunkIndex = utils.Clamp(hunkIndex, 0, len(self.hunks)-1)
+	hunkIndex = lo.Clamp(hunkIndex, 0, len(self.hunks)-1)
 
 	result := len(self.header)
 	for i := 0; i < hunkIndex; i++ {
@@ -65,7 +64,7 @@ func (self *Patch) HunkStartIdx(hunkIndex int) int {
 
 // Returns the patch line index of the last line in the given hunk
 func (self *Patch) HunkEndIdx(hunkIndex int) int {
-	hunkIndex = utils.Clamp(hunkIndex, 0, len(self.hunks)-1)
+	hunkIndex = lo.Clamp(hunkIndex, 0, len(self.hunks)-1)
 
 	return self.HunkStartIdx(hunkIndex) + self.hunks[hunkIndex].lineCount() - 1
 }
@@ -118,7 +117,7 @@ func (self *Patch) HunkContainingLine(idx int) int {
 
 // Returns the patch line index of the next change (i.e. addition or deletion).
 func (self *Patch) GetNextChangeIdx(idx int) int {
-	idx = utils.Clamp(idx, 0, self.LineCount()-1)
+	idx = lo.Clamp(idx, 0, self.LineCount()-1)
 
 	lines := self.Lines()
 
diff --git a/pkg/config/user_config.go b/pkg/config/user_config.go
index 165de3e3427..49e8e13c8e2 100644
--- a/pkg/config/user_config.go
+++ b/pkg/config/user_config.go
@@ -395,6 +395,7 @@ type KeybindingUniversalConfig struct {
 	NextBlockAlt2                     string   `yaml:"nextBlock-alt2"`
 	PrevBlockAlt2                     string   `yaml:"prevBlock-alt2"`
 	JumpToBlock                       []string `yaml:"jumpToBlock"`
+	FocusMainView                     string   `yaml:"focusMainView"`
 	NextMatch                         string   `yaml:"nextMatch"`
 	PrevMatch                         string   `yaml:"prevMatch"`
 	StartSearch                       string   `yaml:"startSearch"`
@@ -876,6 +877,7 @@ func GetDefaultConfig() *UserConfig {
 				PrevBlockAlt2:                     "<backtab>",
 				NextBlockAlt2:                     "<tab>",
 				JumpToBlock:                       []string{"1", "2", "3", "4", "5"},
+				FocusMainView:                     "0",
 				NextMatch:                         "n",
 				PrevMatch:                         "N",
 				StartSearch:                       "/",
diff --git a/pkg/gui/context/base_context.go b/pkg/gui/context/base_context.go
index 76482ad0f35..b72374392d9 100644
--- a/pkg/gui/context/base_context.go
+++ b/pkg/gui/context/base_context.go
@@ -13,12 +13,13 @@ type BaseContext struct {
 	windowName      string
 	onGetOptionsMap func() map[string]string
 
-	keybindingsFns      []types.KeybindingsFn
-	mouseKeybindingsFns []types.MouseKeybindingsFn
-	onClickFn           func() error
-	onRenderToMainFn    func()
-	onFocusFn           onFocusFn
-	onFocusLostFn       onFocusLostFn
+	keybindingsFns           []types.KeybindingsFn
+	mouseKeybindingsFns      []types.MouseKeybindingsFn
+	onClickFn                func() error
+	onClickFocusedMainViewFn onClickFocusedMainViewFn
+	onRenderToMainFn         func()
+	onFocusFn                onFocusFn
+	onFocusLostFn            onFocusLostFn
 
 	focusable                   bool
 	transient                   bool
@@ -31,8 +32,9 @@ type BaseContext struct {
 }
 
 type (
-	onFocusFn     = func(types.OnFocusOpts)
-	onFocusLostFn = func(types.OnFocusLostOpts)
+	onFocusFn                = func(types.OnFocusOpts)
+	onFocusLostFn            = func(types.OnFocusLostOpts)
+	onClickFocusedMainViewFn = func(mainViewName string, clickedLineIdx int) error
 )
 
 var _ types.IBaseContext = &BaseContext{}
@@ -144,10 +146,20 @@ func (self *BaseContext) AddOnClickFn(fn func() error) {
 	}
 }
 
+func (self *BaseContext) AddOnClickFocusedMainViewFn(fn onClickFocusedMainViewFn) {
+	if fn != nil {
+		self.onClickFocusedMainViewFn = fn
+	}
+}
+
 func (self *BaseContext) GetOnClick() func() error {
 	return self.onClickFn
 }
 
+func (self *BaseContext) GetOnClickFocusedMainView() onClickFocusedMainViewFn {
+	return self.onClickFocusedMainViewFn
+}
+
 func (self *BaseContext) AddOnRenderToMainFn(fn func()) {
 	if fn != nil {
 		self.onRenderToMainFn = fn
diff --git a/pkg/gui/context/context.go b/pkg/gui/context/context.go
index e3584a8af11..a993cf253bc 100644
--- a/pkg/gui/context/context.go
+++ b/pkg/gui/context/context.go
@@ -98,8 +98,8 @@ type ContextTree struct {
 	SubCommits                  *SubCommitsContext
 	Stash                       *StashContext
 	Suggestions                 *SuggestionsContext
-	Normal                      types.Context
-	NormalSecondary             types.Context
+	Normal                      *MainContext
+	NormalSecondary             *MainContext
 	Staging                     *PatchExplorerContext
 	StagingSecondary            *PatchExplorerContext
 	CustomPatchBuilder          *PatchExplorerContext
diff --git a/pkg/gui/context/list_renderer.go b/pkg/gui/context/list_renderer.go
index 06fc69b0c0e..9bde5fb4b22 100644
--- a/pkg/gui/context/list_renderer.go
+++ b/pkg/gui/context/list_renderer.go
@@ -52,7 +52,7 @@ func (self *ListRenderer) ModelIndexToViewIndex(modelIndex int) int {
 }
 
 func (self *ListRenderer) ViewIndexToModelIndex(viewIndex int) int {
-	viewIndex = utils.Clamp(viewIndex, 0, self.list.Len()+self.numNonModelItems)
+	viewIndex = lo.Clamp(viewIndex, 0, self.list.Len()+self.numNonModelItems)
 	if self.modelIndicesByViewIndex != nil {
 		return self.modelIndicesByViewIndex[viewIndex]
 	}
diff --git a/pkg/gui/context/main_context.go b/pkg/gui/context/main_context.go
new file mode 100644
index 00000000000..66babac0362
--- /dev/null
+++ b/pkg/gui/context/main_context.go
@@ -0,0 +1,41 @@
+package context
+
+import (
+	"github.com/jesseduffield/gocui"
+	"github.com/jesseduffield/lazygit/pkg/gui/types"
+)
+
+type MainContext struct {
+	*SimpleContext
+	*SearchTrait
+}
+
+var _ types.ISearchableContext = (*MainContext)(nil)
+
+func NewMainContext(
+	view *gocui.View,
+	windowName string,
+	key types.ContextKey,
+	c *ContextCommon,
+) *MainContext {
+	ctx := &MainContext{
+		SimpleContext: NewSimpleContext(
+			NewBaseContext(NewBaseContextOpts{
+				Kind:             types.MAIN_CONTEXT,
+				View:             view,
+				WindowName:       windowName,
+				Key:              key,
+				Focusable:        true,
+				HighlightOnFocus: false,
+			})),
+		SearchTrait: NewSearchTrait(c),
+	}
+
+	ctx.GetView().SetOnSelectItem(ctx.SearchTrait.onSelectItemWrapper(func(int) error { return nil }))
+
+	return ctx
+}
+
+func (self *MainContext) ModelSearchResults(searchStr string, caseSensitive bool) []gocui.SearchPosition {
+	return nil
+}
diff --git a/pkg/gui/context/setup.go b/pkg/gui/context/setup.go
index ec14a502898..3a87c100a7e 100644
--- a/pkg/gui/context/setup.go
+++ b/pkg/gui/context/setup.go
@@ -25,38 +25,22 @@ func NewContextTree(c *ContextCommon) *ContextTree {
 				Focusable:  true,
 			}),
 		),
-		Files:          NewWorkingTreeContext(c),
-		Submodules:     NewSubmodulesContext(c),
-		Menu:           NewMenuContext(c),
-		Remotes:        NewRemotesContext(c),
-		Worktrees:      NewWorktreesContext(c),
-		RemoteBranches: NewRemoteBranchesContext(c),
-		LocalCommits:   NewLocalCommitsContext(c),
-		CommitFiles:    commitFilesContext,
-		ReflogCommits:  NewReflogCommitsContext(c),
-		SubCommits:     NewSubCommitsContext(c),
-		Branches:       NewBranchesContext(c),
-		Tags:           NewTagsContext(c),
-		Stash:          NewStashContext(c),
-		Suggestions:    NewSuggestionsContext(c),
-		Normal: NewSimpleContext(
-			NewBaseContext(NewBaseContextOpts{
-				Kind:       types.MAIN_CONTEXT,
-				View:       c.Views().Main,
-				WindowName: "main",
-				Key:        NORMAL_MAIN_CONTEXT_KEY,
-				Focusable:  false,
-			}),
-		),
-		NormalSecondary: NewSimpleContext(
-			NewBaseContext(NewBaseContextOpts{
-				Kind:       types.MAIN_CONTEXT,
-				View:       c.Views().Secondary,
-				WindowName: "secondary",
-				Key:        NORMAL_SECONDARY_CONTEXT_KEY,
-				Focusable:  false,
-			}),
-		),
+		Files:           NewWorkingTreeContext(c),
+		Submodules:      NewSubmodulesContext(c),
+		Menu:            NewMenuContext(c),
+		Remotes:         NewRemotesContext(c),
+		Worktrees:       NewWorktreesContext(c),
+		RemoteBranches:  NewRemoteBranchesContext(c),
+		LocalCommits:    NewLocalCommitsContext(c),
+		CommitFiles:     commitFilesContext,
+		ReflogCommits:   NewReflogCommitsContext(c),
+		SubCommits:      NewSubCommitsContext(c),
+		Branches:        NewBranchesContext(c),
+		Tags:            NewTagsContext(c),
+		Stash:           NewStashContext(c),
+		Suggestions:     NewSuggestionsContext(c),
+		Normal:          NewMainContext(c.Views().Main, "main", NORMAL_MAIN_CONTEXT_KEY, c),
+		NormalSecondary: NewMainContext(c.Views().Secondary, "secondary", NORMAL_SECONDARY_CONTEXT_KEY, c),
 		Staging: NewPatchExplorerContext(
 			c.Views().Staging,
 			"main",
diff --git a/pkg/gui/context/traits/list_cursor.go b/pkg/gui/context/traits/list_cursor.go
index 31f3de7a40f..a8ff8971638 100644
--- a/pkg/gui/context/traits/list_cursor.go
+++ b/pkg/gui/context/traits/list_cursor.go
@@ -3,6 +3,7 @@ package traits
 import (
 	"github.com/jesseduffield/lazygit/pkg/gui/types"
 	"github.com/jesseduffield/lazygit/pkg/utils"
+	"github.com/samber/lo"
 )
 
 type RangeSelectMode int
@@ -85,7 +86,7 @@ func (self *ListCursor) clampValue(value int) int {
 	clampedValue := -1
 	length := self.getLength()
 	if length > 0 {
-		clampedValue = utils.Clamp(value, 0, length-1)
+		clampedValue = lo.Clamp(value, 0, length-1)
 	}
 
 	return clampedValue
diff --git a/pkg/gui/controllers.go b/pkg/gui/controllers.go
index a808d368f51..e6f6bbb4ee7 100644
--- a/pkg/gui/controllers.go
+++ b/pkg/gui/controllers.go
@@ -180,7 +180,8 @@ func (gui *Gui) resetHelpersAndControllers() {
 	globalController := controllers.NewGlobalController(common)
 	contextLinesController := controllers.NewContextLinesController(common)
 	renameSimilarityThresholdController := controllers.NewRenameSimilarityThresholdController(common)
-	verticalScrollControllerFactory := controllers.NewVerticalScrollControllerFactory(common, &gui.viewBufferManagerMap)
+	verticalScrollControllerFactory := controllers.NewVerticalScrollControllerFactory(common)
+	viewSelectionControllerFactory := controllers.NewViewSelectionControllerFactory(common)
 
 	branchesController := controllers.NewBranchesController(common)
 	gitFlowController := controllers.NewGitFlowController(common)
@@ -189,6 +190,8 @@ func (gui *Gui) resetHelpersAndControllers() {
 	patchExplorerControllerFactory := controllers.NewPatchExplorerControllerFactory(common)
 	stagingController := controllers.NewStagingController(common, gui.State.Contexts.Staging, gui.State.Contexts.StagingSecondary, false)
 	stagingSecondaryController := controllers.NewStagingController(common, gui.State.Contexts.StagingSecondary, gui.State.Contexts.Staging, true)
+	mainViewController := controllers.NewMainViewController(common, gui.State.Contexts.Normal, gui.State.Contexts.NormalSecondary)
+	secondaryViewController := controllers.NewMainViewController(common, gui.State.Contexts.NormalSecondary, gui.State.Contexts.Normal)
 	patchBuildingController := controllers.NewPatchBuildingController(common)
 	snakeController := controllers.NewSnakeController(common)
 	reflogCommitsController := controllers.NewReflogCommitsController(common)
@@ -263,6 +266,22 @@ func (gui *Gui) resetHelpersAndControllers() {
 		))
 	}
 
+	for _, context := range []types.Context{
+		gui.State.Contexts.Files,
+		gui.State.Contexts.Branches,
+		gui.State.Contexts.RemoteBranches,
+		gui.State.Contexts.Tags,
+		gui.State.Contexts.LocalCommits,
+		gui.State.Contexts.ReflogCommits,
+		gui.State.Contexts.SubCommits,
+		gui.State.Contexts.CommitFiles,
+		gui.State.Contexts.Stash,
+	} {
+		controllers.AttachControllers(context, controllers.NewSwitchToFocusedMainViewController(
+			common, context,
+		))
+	}
+
 	for _, context := range []controllers.ContainsCommits{
 		gui.State.Contexts.LocalCommits,
 		gui.State.Contexts.ReflogCommits,
@@ -306,6 +325,18 @@ func (gui *Gui) resetHelpersAndControllers() {
 		mergeConflictsController,
 	)
 
+	controllers.AttachControllers(gui.State.Contexts.Normal,
+		mainViewController,
+		verticalScrollControllerFactory.Create(gui.State.Contexts.Normal),
+		viewSelectionControllerFactory.Create(gui.State.Contexts.Normal),
+	)
+
+	controllers.AttachControllers(gui.State.Contexts.NormalSecondary,
+		secondaryViewController,
+		verticalScrollControllerFactory.Create(gui.State.Contexts.NormalSecondary),
+		viewSelectionControllerFactory.Create(gui.State.Contexts.NormalSecondary),
+	)
+
 	controllers.AttachControllers(gui.State.Contexts.Files,
 		filesController,
 	)
diff --git a/pkg/gui/controllers/attach.go b/pkg/gui/controllers/attach.go
index 6a24fd7d317..8c1dbb91e75 100644
--- a/pkg/gui/controllers/attach.go
+++ b/pkg/gui/controllers/attach.go
@@ -7,6 +7,7 @@ func AttachControllers(context types.Context, controllers ...types.IController)
 		context.AddKeybindingsFn(controller.GetKeybindings)
 		context.AddMouseKeybindingsFn(controller.GetMouseKeybindings)
 		context.AddOnClickFn(controller.GetOnClick())
+		context.AddOnClickFocusedMainViewFn(controller.GetOnClickFocusedMainView())
 		context.AddOnRenderToMainFn(controller.GetOnRenderToMain())
 		context.AddOnFocusFn(controller.GetOnFocus())
 		context.AddOnFocusLostFn(controller.GetOnFocusLost())
diff --git a/pkg/gui/controllers/base_controller.go b/pkg/gui/controllers/base_controller.go
index adff1927aa2..0e122922289 100644
--- a/pkg/gui/controllers/base_controller.go
+++ b/pkg/gui/controllers/base_controller.go
@@ -19,6 +19,10 @@ func (self *baseController) GetOnClick() func() error {
 	return nil
 }
 
+func (self *baseController) GetOnClickFocusedMainView() func(mainViewName string, clickedLineIdx int) error {
+	return nil
+}
+
 func (self *baseController) GetOnRenderToMain() func() {
 	return nil
 }
diff --git a/pkg/gui/controllers/commits_files_controller.go b/pkg/gui/controllers/commits_files_controller.go
index 77037513e1a..e1818789cda 100644
--- a/pkg/gui/controllers/commits_files_controller.go
+++ b/pkg/gui/controllers/commits_files_controller.go
@@ -135,17 +135,6 @@ func (self *CommitFilesController) GetKeybindings(opts types.KeybindingsOpts) []
 	return bindings
 }
 
-func (self *CommitFilesController) GetMouseKeybindings(opts types.KeybindingsOpts) []*gocui.ViewMouseBinding {
-	return []*gocui.ViewMouseBinding{
-		{
-			ViewName:    "patchBuilding",
-			Key:         gocui.MouseLeft,
-			Handler:     self.onClickMain,
-			FocusedView: self.context().GetViewName(),
-		},
-	}
-}
-
 func (self *CommitFilesController) context() *context.CommitFilesContext {
 	return self.c.Contexts().CommitFiles
 }
@@ -163,13 +152,8 @@ func (self *CommitFilesController) GetOnRenderToMain() func() {
 		cmdObj := self.c.Git().WorkingTree.ShowFileDiffCmdObj(from, to, reverse, node.GetPath(), false)
 		task := types.NewRunPtyTask(cmdObj.GetCmd())
 
-		pair := self.c.MainViewPairs().Normal
-		if node.File != nil {
-			pair = self.c.MainViewPairs().PatchBuilding
-		}
-
 		self.c.RenderToMainViews(types.RefreshMainOpts{
-			Pair: pair,
+			Pair: self.c.MainViewPairs().Normal,
 			Main: &types.ViewUpdateOpts{
 				Title:    self.c.Tr.Patch,
 				SubTitle: self.c.Helpers().Diff.IgnoringWhitespaceSubTitle(),
@@ -180,14 +164,6 @@ func (self *CommitFilesController) GetOnRenderToMain() func() {
 	}
 }
 
-func (self *CommitFilesController) onClickMain(opts gocui.ViewMouseBindingOpts) error {
-	node := self.context().GetSelected()
-	if node == nil {
-		return nil
-	}
-	return self.enterCommitFile(node, types.OnFocusOpts{ClickedWindowName: "main", ClickedViewLineIdx: opts.Y})
-}
-
 func (self *CommitFilesController) copyDiffToClipboard(path string, toastMessage string) error {
 	from, to := self.context().GetFromAndToForDiff()
 	from, reverse := self.c.Modes().Diffing.GetFromAndReverseArgsForDiff(from)
@@ -555,6 +531,16 @@ func (self *CommitFilesController) expandAll() error {
 	return nil
 }
 
+func (self *CommitFilesController) GetOnClickFocusedMainView() func(mainViewName string, clickedLineIdx int) error {
+	return func(mainViewName string, clickedLineIdx int) error {
+		node := self.getSelectedItem()
+		if node != nil && node.File != nil {
+			return self.enterCommitFile(node, types.OnFocusOpts{ClickedWindowName: mainViewName, ClickedViewLineIdx: clickedLineIdx})
+		}
+		return nil
+	}
+}
+
 // NOTE: these functions are identical to those in files_controller.go (except for types) and
 // could also be cleaned up with some generics
 func normalisedSelectedCommitFileNodes(selectedNodes []*filetree.CommitFileNode) []*filetree.CommitFileNode {
diff --git a/pkg/gui/controllers/files_controller.go b/pkg/gui/controllers/files_controller.go
index 3614e232e86..1240e3cf7f7 100644
--- a/pkg/gui/controllers/files_controller.go
+++ b/pkg/gui/controllers/files_controller.go
@@ -208,36 +208,12 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types
 
 func (self *FilesController) GetMouseKeybindings(opts types.KeybindingsOpts) []*gocui.ViewMouseBinding {
 	return []*gocui.ViewMouseBinding{
-		{
-			ViewName:    "main",
-			Key:         gocui.MouseLeft,
-			Handler:     self.onClickMain,
-			FocusedView: self.context().GetViewName(),
-		},
-		{
-			ViewName:    "patchBuilding",
-			Key:         gocui.MouseLeft,
-			Handler:     self.onClickMain,
-			FocusedView: self.context().GetViewName(),
-		},
 		{
 			ViewName:    "mergeConflicts",
 			Key:         gocui.MouseLeft,
 			Handler:     self.onClickMain,
 			FocusedView: self.context().GetViewName(),
 		},
-		{
-			ViewName:    "secondary",
-			Key:         gocui.MouseLeft,
-			Handler:     self.onClickSecondary,
-			FocusedView: self.context().GetViewName(),
-		},
-		{
-			ViewName:    "patchBuildingSecondary",
-			Key:         gocui.MouseLeft,
-			Handler:     self.onClickSecondary,
-			FocusedView: self.context().GetViewName(),
-		},
 	}
 }
 
@@ -304,11 +280,6 @@ func (self *FilesController) GetOnRenderToMain() func() {
 
 			self.c.Helpers().MergeConflicts.ResetMergeState()
 
-			pair := self.c.MainViewPairs().Normal
-			if node.File != nil {
-				pair = self.c.MainViewPairs().Staging
-			}
-
 			split := self.c.UserConfig().Gui.SplitDiff == "always" || (node.GetHasUnstagedChanges() && node.GetHasStagedChanges())
 			mainShowsStaged := !split && node.GetHasStagedChanges()
 
@@ -318,7 +289,7 @@ func (self *FilesController) GetOnRenderToMain() func() {
 				title = self.c.Tr.StagedChanges
 			}
 			refreshOpts := types.RefreshMainOpts{
-				Pair: pair,
+				Pair: self.c.MainViewPairs().Normal,
 				Main: &types.ViewUpdateOpts{
 					Task:     types.NewRunPtyTask(cmdObj.GetCmd()),
 					SubTitle: self.c.Helpers().Diff.IgnoringWhitespaceSubTitle(),
@@ -352,6 +323,16 @@ func (self *FilesController) GetOnClick() func() error {
 	})
 }
 
+func (self *FilesController) GetOnClickFocusedMainView() func(mainViewName string, clickedLineIdx int) error {
+	return func(mainViewName string, clickedLineIdx int) error {
+		node := self.getSelectedItem()
+		if node != nil && node.File != nil {
+			return self.EnterFile(types.OnFocusOpts{ClickedWindowName: mainViewName, ClickedViewLineIdx: clickedLineIdx})
+		}
+		return nil
+	}
+}
+
 // if we are dealing with a status for which there is no key in this map,
 // then we won't optimistically render: we'll just let `git status` tell
 // us what the new status is.
@@ -574,7 +555,8 @@ func (self *FilesController) EnterFile(opts types.OnFocusOpts) error {
 		return self.handleNonInlineConflict(file)
 	}
 
-	self.c.Context().Push(self.c.Contexts().Staging, opts)
+	context := lo.Ternary(opts.ClickedWindowName == "secondary", self.c.Contexts().StagingSecondary, self.c.Contexts().Staging)
+	self.c.Context().Push(context, opts)
 	return nil
 }
 
@@ -1205,10 +1187,6 @@ func (self *FilesController) onClickMain(opts gocui.ViewMouseBindingOpts) error
 	return self.EnterFile(types.OnFocusOpts{ClickedWindowName: "main", ClickedViewLineIdx: opts.Y})
 }
 
-func (self *FilesController) onClickSecondary(opts gocui.ViewMouseBindingOpts) error {
-	return self.EnterFile(types.OnFocusOpts{ClickedWindowName: "secondary", ClickedViewLineIdx: opts.Y})
-}
-
 func (self *FilesController) fetch() error {
 	return self.c.WithWaitingStatus(self.c.Tr.FetchingStatus, func(task gocui.Task) error {
 		if err := self.fetchAux(task); err != nil {
diff --git a/pkg/gui/controllers/main_view_controller.go b/pkg/gui/controllers/main_view_controller.go
new file mode 100644
index 00000000000..e6b209286ba
--- /dev/null
+++ b/pkg/gui/controllers/main_view_controller.go
@@ -0,0 +1,118 @@
+package controllers
+
+import (
+	"github.com/jesseduffield/gocui"
+	"github.com/jesseduffield/lazygit/pkg/gui/context"
+	"github.com/jesseduffield/lazygit/pkg/gui/types"
+)
+
+type MainViewController struct {
+	baseController
+	c *ControllerCommon
+
+	context      *context.MainContext
+	otherContext *context.MainContext
+}
+
+var _ types.IController = &MainViewController{}
+
+func NewMainViewController(
+	c *ControllerCommon,
+	context *context.MainContext,
+	otherContext *context.MainContext,
+) *MainViewController {
+	return &MainViewController{
+		baseController: baseController{},
+		c:              c,
+		context:        context,
+		otherContext:   otherContext,
+	}
+}
+
+func (self *MainViewController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
+	return []*types.Binding{
+		{
+			Key:             opts.GetKey(opts.Config.Universal.TogglePanel),
+			Handler:         self.togglePanel,
+			Description:     self.c.Tr.ToggleStagingView,
+			Tooltip:         self.c.Tr.ToggleStagingViewTooltip,
+			DisplayOnScreen: true,
+		},
+		{
+			Key:         opts.GetKey(opts.Config.Universal.Return),
+			Handler:     self.escape,
+			Description: self.c.Tr.ExitFocusedMainView,
+		},
+		{
+			// overriding this because we want to read all of the task's output before we start searching
+			Key:         opts.GetKey(opts.Config.Universal.StartSearch),
+			Handler:     self.openSearch,
+			Description: self.c.Tr.StartSearch,
+			Tag:         "navigation",
+		},
+	}
+}
+
+func (self *MainViewController) GetMouseKeybindings(opts types.KeybindingsOpts) []*gocui.ViewMouseBinding {
+	return []*gocui.ViewMouseBinding{
+		{
+			ViewName: self.context.GetViewName(),
+			Key:      gocui.MouseLeft,
+			Handler: func(opts gocui.ViewMouseBindingOpts) error {
+				if self.isFocused() {
+					return self.onClick(opts)
+				}
+
+				self.context.SetParentContext(self.otherContext.GetParentContext())
+				self.c.Context().Push(self.context, types.OnFocusOpts{
+					ClickedWindowName:  self.context.GetWindowName(),
+					ClickedViewLineIdx: opts.Y,
+				})
+
+				return nil
+			},
+		},
+	}
+}
+
+func (self *MainViewController) Context() types.Context {
+	return self.context
+}
+
+func (self *MainViewController) togglePanel() error {
+	if self.otherContext.GetView().Visible {
+		self.otherContext.SetParentContext(self.context.GetParentContext())
+		self.c.Context().Push(self.otherContext, types.OnFocusOpts{})
+	}
+
+	return nil
+}
+
+func (self *MainViewController) escape() error {
+	self.c.Context().Pop()
+	return nil
+}
+
+func (self *MainViewController) onClick(opts gocui.ViewMouseBindingOpts) error {
+	parentCtx := self.context.GetParentContext()
+	if parentCtx.GetOnClickFocusedMainView() != nil {
+		return parentCtx.GetOnClickFocusedMainView()(self.context.GetViewName(), opts.Y)
+	}
+	return nil
+}
+
+func (self *MainViewController) openSearch() error {
+	if manager := self.c.GetViewBufferManagerForView(self.context.GetView()); manager != nil {
+		manager.ReadToEnd(func() {
+			self.c.OnUIThread(func() error {
+				return self.c.Helpers().Search.OpenSearchPrompt(self.context)
+			})
+		})
+	}
+
+	return nil
+}
+
+func (self *MainViewController) isFocused() bool {
+	return self.c.Context().Current().GetKey() == self.context.GetKey()
+}
diff --git a/pkg/gui/controllers/switch_to_focused_main_view_controller.go b/pkg/gui/controllers/switch_to_focused_main_view_controller.go
new file mode 100644
index 00000000000..cb03f5e1516
--- /dev/null
+++ b/pkg/gui/controllers/switch_to_focused_main_view_controller.go
@@ -0,0 +1,82 @@
+package controllers
+
+import (
+	"github.com/jesseduffield/gocui"
+	"github.com/jesseduffield/lazygit/pkg/gui/types"
+)
+
+// This controller is for all contexts that can focus their main view.
+
+var _ types.IController = &SwitchToFocusedMainViewController{}
+
+type SwitchToFocusedMainViewController struct {
+	baseController
+	c       *ControllerCommon
+	context types.Context
+}
+
+func NewSwitchToFocusedMainViewController(
+	c *ControllerCommon,
+	context types.Context,
+) *SwitchToFocusedMainViewController {
+	return &SwitchToFocusedMainViewController{
+		baseController: baseController{},
+		c:              c,
+		context:        context,
+	}
+}
+
+func (self *SwitchToFocusedMainViewController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
+	bindings := []*types.Binding{
+		{
+			Key:         opts.GetKey(opts.Config.Universal.FocusMainView),
+			Handler:     self.handleFocusMainView,
+			Description: self.c.Tr.FocusMainView,
+		},
+	}
+
+	return bindings
+}
+
+func (self *SwitchToFocusedMainViewController) GetMouseKeybindings(opts types.KeybindingsOpts) []*gocui.ViewMouseBinding {
+	return []*gocui.ViewMouseBinding{
+		{
+			ViewName:    "main",
+			Key:         gocui.MouseLeft,
+			Handler:     self.onClickMain,
+			FocusedView: self.context.GetViewName(),
+		},
+		{
+			ViewName:    "secondary",
+			Key:         gocui.MouseLeft,
+			Handler:     self.onClickSecondary,
+			FocusedView: self.context.GetViewName(),
+		},
+	}
+}
+
+func (self *SwitchToFocusedMainViewController) Context() types.Context {
+	return self.context
+}
+
+func (self *SwitchToFocusedMainViewController) onClickMain(opts gocui.ViewMouseBindingOpts) error {
+	return self.focusMainView("main")
+}
+
+func (self *SwitchToFocusedMainViewController) onClickSecondary(opts gocui.ViewMouseBindingOpts) error {
+	return self.focusMainView("secondary")
+}
+
+func (self *SwitchToFocusedMainViewController) handleFocusMainView() error {
+	return self.focusMainView("main")
+}
+
+func (self *SwitchToFocusedMainViewController) focusMainView(mainViewName string) error {
+	mainViewContext := self.c.Helpers().Window.GetContextForWindow(mainViewName)
+	mainViewContext.SetParentContext(self.context)
+	if context, ok := mainViewContext.(types.ISearchableContext); ok {
+		context.ClearSearchString()
+	}
+	self.c.Context().Push(mainViewContext, types.OnFocusOpts{})
+	return nil
+}
diff --git a/pkg/gui/controllers/vertical_scroll_controller.go b/pkg/gui/controllers/vertical_scroll_controller.go
index b168e86a8b9..b88574451cb 100644
--- a/pkg/gui/controllers/vertical_scroll_controller.go
+++ b/pkg/gui/controllers/vertical_scroll_controller.go
@@ -3,29 +3,25 @@ package controllers
 import (
 	"github.com/jesseduffield/gocui"
 	"github.com/jesseduffield/lazygit/pkg/gui/types"
-	"github.com/jesseduffield/lazygit/pkg/tasks"
 )
 
 // given we have no fields here, arguably we shouldn't even need this factory
 // struct, but we're maintaining consistency with the other files.
 type VerticalScrollControllerFactory struct {
-	c                    *ControllerCommon
-	viewBufferManagerMap *map[string]*tasks.ViewBufferManager
+	c *ControllerCommon
 }
 
-func NewVerticalScrollControllerFactory(c *ControllerCommon, viewBufferManagerMap *map[string]*tasks.ViewBufferManager) *VerticalScrollControllerFactory {
+func NewVerticalScrollControllerFactory(c *ControllerCommon) *VerticalScrollControllerFactory {
 	return &VerticalScrollControllerFactory{
-		c:                    c,
-		viewBufferManagerMap: viewBufferManagerMap,
+		c: c,
 	}
 }
 
 func (self *VerticalScrollControllerFactory) Create(context types.Context) types.IController {
 	return &VerticalScrollController{
-		baseController:       baseController{},
-		c:                    self.c,
-		context:              context,
-		viewBufferManagerMap: self.viewBufferManagerMap,
+		baseController: baseController{},
+		c:              self.c,
+		context:        context,
 	}
 }
 
@@ -33,8 +29,7 @@ type VerticalScrollController struct {
 	baseController
 	c *ControllerCommon
 
-	context              types.Context
-	viewBufferManagerMap *map[string]*tasks.ViewBufferManager
+	context types.Context
 }
 
 func (self *VerticalScrollController) Context() types.Context {
@@ -74,7 +69,7 @@ func (self *VerticalScrollController) HandleScrollDown() error {
 	scrollHeight := self.c.UserConfig().Gui.ScrollHeight
 	self.context.GetViewTrait().ScrollDown(scrollHeight)
 
-	if manager, ok := (*self.viewBufferManagerMap)[self.context.GetViewName()]; ok {
+	if manager := self.c.GetViewBufferManagerForView(self.context.GetView()); manager != nil {
 		manager.ReadLines(scrollHeight)
 	}
 
diff --git a/pkg/gui/controllers/view_selection_controller.go b/pkg/gui/controllers/view_selection_controller.go
new file mode 100644
index 00000000000..638c46ba608
--- /dev/null
+++ b/pkg/gui/controllers/view_selection_controller.go
@@ -0,0 +1,109 @@
+package controllers
+
+import (
+	"github.com/jesseduffield/gocui"
+	"github.com/jesseduffield/lazygit/pkg/gui/types"
+)
+
+type ViewSelectionControllerFactory struct {
+	c *ControllerCommon
+}
+
+func NewViewSelectionControllerFactory(c *ControllerCommon) *ViewSelectionControllerFactory {
+	return &ViewSelectionControllerFactory{
+		c: c,
+	}
+}
+
+func (self *ViewSelectionControllerFactory) Create(context types.Context) types.IController {
+	return &ViewSelectionController{
+		baseController: baseController{},
+		c:              self.c,
+		context:        context,
+	}
+}
+
+type ViewSelectionController struct {
+	baseController
+	c *ControllerCommon
+
+	context types.Context
+}
+
+func (self *ViewSelectionController) Context() types.Context {
+	return self.context
+}
+
+func (self *ViewSelectionController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
+	return []*types.Binding{
+		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.PrevItem), Handler: self.handlePrevLine},
+		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.PrevItemAlt), Handler: self.handlePrevLine},
+		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.NextItem), Handler: self.handleNextLine},
+		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.NextItemAlt), Handler: self.handleNextLine},
+		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.PrevPage), Handler: self.handlePrevPage, Description: self.c.Tr.PrevPage},
+		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.NextPage), Handler: self.handleNextPage, Description: self.c.Tr.NextPage},
+		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.GotoTop), Handler: self.handleGotoTop, Description: self.c.Tr.GotoTop, Alternative: "<home>"},
+		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.GotoBottom), Handler: self.handleGotoBottom, Description: self.c.Tr.GotoBottom, Alternative: "<end>"},
+		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.GotoTopAlt), Handler: self.handleGotoTop},
+		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.GotoBottomAlt), Handler: self.handleGotoBottom},
+	}
+}
+
+func (self *ViewSelectionController) GetMouseKeybindings(opts types.KeybindingsOpts) []*gocui.ViewMouseBinding {
+	return []*gocui.ViewMouseBinding{}
+}
+
+func (self *ViewSelectionController) handleLineChange(delta int) {
+	if delta > 0 {
+		if manager := self.c.GetViewBufferManagerForView(self.context.GetView()); manager != nil {
+			manager.ReadLines(delta)
+		}
+	}
+
+	v := self.Context().GetView()
+	if delta < 0 {
+		v.ScrollUp(-delta)
+	} else {
+		v.ScrollDown(delta)
+	}
+}
+
+func (self *ViewSelectionController) handlePrevLine() error {
+	self.handleLineChange(-1)
+	return nil
+}
+
+func (self *ViewSelectionController) handleNextLine() error {
+	self.handleLineChange(1)
+	return nil
+}
+
+func (self *ViewSelectionController) handlePrevPage() error {
+	self.handleLineChange(-self.context.GetViewTrait().PageDelta())
+	return nil
+}
+
+func (self *ViewSelectionController) handleNextPage() error {
+	self.handleLineChange(self.context.GetViewTrait().PageDelta())
+	return nil
+}
+
+func (self *ViewSelectionController) handleGotoTop() error {
+	v := self.Context().GetView()
+	self.handleLineChange(-v.ViewLinesHeight())
+	return nil
+}
+
+func (self *ViewSelectionController) handleGotoBottom() error {
+	if manager := self.c.GetViewBufferManagerForView(self.context.GetView()); manager != nil {
+		manager.ReadToEnd(func() {
+			self.c.OnUIThread(func() error {
+				v := self.Context().GetView()
+				self.handleLineChange(v.ViewLinesHeight())
+				return nil
+			})
+		})
+	}
+
+	return nil
+}
diff --git a/pkg/gui/global_handlers.go b/pkg/gui/global_handlers.go
index 0301cf63edb..a5aabb3610e 100644
--- a/pkg/gui/global_handlers.go
+++ b/pkg/gui/global_handlers.go
@@ -20,7 +20,7 @@ func (gui *Gui) scrollDownView(view *gocui.View) {
 	scrollHeight := gui.c.UserConfig().Gui.ScrollHeight
 	view.ScrollDown(scrollHeight)
 
-	if manager, ok := gui.viewBufferManagerMap[view.Name()]; ok {
+	if manager := gui.getViewBufferManagerForView(view); manager != nil {
 		manager.ReadLines(scrollHeight)
 	}
 }
diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go
index 42922611a42..90a2a64d48f 100644
--- a/pkg/gui/gui.go
+++ b/pkg/gui/gui.go
@@ -578,6 +578,15 @@ func (gui *Gui) resetState(startArgs appTypes.StartArgs) types.Context {
 	return initialContext(contextTree, startArgs)
 }
 
+func (self *Gui) getViewBufferManagerForView(view *gocui.View) *tasks.ViewBufferManager {
+	manager, ok := self.viewBufferManagerMap[view.Name()]
+	if !ok {
+		return nil
+	}
+
+	return manager
+}
+
 func initialWindowViewNameMap(contextTree *context.ContextTree) *utils.ThreadSafeMap[string, string] {
 	result := utils.NewThreadSafeMap[string, string]()
 
diff --git a/pkg/gui/gui_common.go b/pkg/gui/gui_common.go
index dfa4be52f3d..1465a1ce301 100644
--- a/pkg/gui/gui_common.go
+++ b/pkg/gui/gui_common.go
@@ -7,6 +7,7 @@ import (
 	"github.com/jesseduffield/lazygit/pkg/config"
 	"github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers"
 	"github.com/jesseduffield/lazygit/pkg/gui/types"
+	"github.com/jesseduffield/lazygit/pkg/tasks"
 )
 
 // hacking this by including the gui struct for now until we split more things out
@@ -128,6 +129,10 @@ func (self *guiCommon) MainViewPairs() types.MainViewPairs {
 	}
 }
 
+func (self *guiCommon) GetViewBufferManagerForView(view *gocui.View) *tasks.ViewBufferManager {
+	return self.gui.getViewBufferManagerForView(view)
+}
+
 func (self *guiCommon) State() types.IStateAccessor {
 	return self.gui.stateAccessor
 }
diff --git a/pkg/gui/layout.go b/pkg/gui/layout.go
index f2e48c674d1..53e8247783c 100644
--- a/pkg/gui/layout.go
+++ b/pkg/gui/layout.go
@@ -32,10 +32,10 @@ func (gui *Gui) layout(g *gocui.Gui) error {
 		newMainHeight := viewDimensions["main"].Y1 - viewDimensions["main"].Y0 + 1
 		heightDiff := newMainHeight - prevMainHeight
 		if heightDiff > 0 {
-			if manager, ok := gui.viewBufferManagerMap["main"]; ok {
+			if manager := gui.getViewBufferManagerForView(gui.Views.Main); manager != nil {
 				manager.ReadLines(heightDiff)
 			}
-			if manager, ok := gui.viewBufferManagerMap["secondary"]; ok {
+			if manager := gui.getViewBufferManagerForView(gui.Views.Secondary); manager != nil {
 				manager.ReadLines(heightDiff)
 			}
 		}
diff --git a/pkg/gui/mergeconflicts/state.go b/pkg/gui/mergeconflicts/state.go
index a3dbcff3abd..047241353e4 100644
--- a/pkg/gui/mergeconflicts/state.go
+++ b/pkg/gui/mergeconflicts/state.go
@@ -4,6 +4,7 @@ import (
 	"strings"
 
 	"github.com/jesseduffield/lazygit/pkg/utils"
+	"github.com/samber/lo"
 )
 
 // State represents the selection state of the merge conflict context.
@@ -37,14 +38,14 @@ func (s *State) setConflictIndex(index int) {
 	if len(s.conflicts) == 0 {
 		s.conflictIndex = 0
 	} else {
-		s.conflictIndex = utils.Clamp(index, 0, len(s.conflicts)-1)
+		s.conflictIndex = lo.Clamp(index, 0, len(s.conflicts)-1)
 	}
 	s.setSelectionIndex(s.selectionIndex)
 }
 
 func (s *State) setSelectionIndex(index int) {
 	if selections := s.availableSelections(); len(selections) != 0 {
-		s.selectionIndex = utils.Clamp(index, 0, len(selections)-1)
+		s.selectionIndex = lo.Clamp(index, 0, len(selections)-1)
 	}
 }
 
diff --git a/pkg/gui/types/common.go b/pkg/gui/types/common.go
index e01c664230b..863d7c2004e 100644
--- a/pkg/gui/types/common.go
+++ b/pkg/gui/types/common.go
@@ -8,6 +8,7 @@ import (
 	"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
 	"github.com/jesseduffield/lazygit/pkg/common"
 	"github.com/jesseduffield/lazygit/pkg/config"
+	"github.com/jesseduffield/lazygit/pkg/tasks"
 	"github.com/jesseduffield/lazygit/pkg/utils"
 	"github.com/sasha-s/go-deadlock"
 	"gopkg.in/ozeidan/fuzzy-patricia.v3/patricia"
@@ -48,6 +49,9 @@ type IGuiCommon interface {
 	// used purely for the sake of RenderToMainViews to provide the pair of main views we want to render to
 	MainViewPairs() MainViewPairs
 
+	// return the view buffer manager for the given view, or nil if it doesn't have one
+	GetViewBufferManagerForView(view *gocui.View) *tasks.ViewBufferManager
+
 	// returns true if command completed successfully
 	RunSubprocess(cmdObj oscommands.ICmdObj) (bool, error)
 	RunSubprocessAndRefresh(oscommands.ICmdObj) error
diff --git a/pkg/gui/types/context.go b/pkg/gui/types/context.go
index 8a9bbf25b4a..f76bedeb661 100644
--- a/pkg/gui/types/context.go
+++ b/pkg/gui/types/context.go
@@ -94,6 +94,9 @@ type IBaseContext interface {
 	// our list controller can come along and wrap it in a list-specific click handler.
 	// We'll need to think of a better way to do this.
 	AddOnClickFn(func() error)
+	// Likewise for the focused main view: we need this to communicate between a
+	// side panel controller and the focused main view controller.
+	AddOnClickFocusedMainViewFn(func(mainViewName string, clickedLineIdx int) error)
 
 	AddOnRenderToMainFn(func())
 	AddOnFocusFn(func(OnFocusOpts))
@@ -240,6 +243,7 @@ type HasKeybindings interface {
 	GetKeybindings(opts KeybindingsOpts) []*Binding
 	GetMouseKeybindings(opts KeybindingsOpts) []*gocui.ViewMouseBinding
 	GetOnClick() func() error
+	GetOnClickFocusedMainView() func(mainViewName string, clickedLineIdx int) error
 	GetOnRenderToMain() func()
 	GetOnFocus() func(OnFocusOpts)
 	GetOnFocusLost() func(OnFocusLostOpts)
diff --git a/pkg/gui/view_helpers.go b/pkg/gui/view_helpers.go
index bf83fd7f355..706f5863bcb 100644
--- a/pkg/gui/view_helpers.go
+++ b/pkg/gui/view_helpers.go
@@ -4,6 +4,7 @@ import (
 	"time"
 
 	"github.com/jesseduffield/gocui"
+	"github.com/jesseduffield/lazygit/pkg/gui/context"
 	"github.com/jesseduffield/lazygit/pkg/gui/types"
 	"github.com/jesseduffield/lazygit/pkg/tasks"
 	"github.com/jesseduffield/lazygit/pkg/utils"
@@ -143,5 +144,17 @@ func (gui *Gui) postRefreshUpdate(c types.Context) {
 		// correctly, and that integration tests see the up to date selection
 		// state.
 		c.FocusLine()
+
+		currentCtx := gui.State.ContextMgr.Current()
+		if currentCtx.GetKey() == context.NORMAL_MAIN_CONTEXT_KEY || currentCtx.GetKey() == context.NORMAL_SECONDARY_CONTEXT_KEY {
+			// Searching can't cope well with the view being updated while it is being searched.
+			// We might be able to fix the problems with this, but it doesn't seem easy, so for now
+			// just don't rerender the view while searching, on the assumption that users will probably
+			// either search or change their data, but not both at the same time.
+			if !currentCtx.GetView().IsSearching() {
+				parentCtx := currentCtx.GetParentContext()
+				parentCtx.HandleRenderToMain()
+			}
+		}
 	}
 }
diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go
index ab9f7cdf4cb..53c376293e5 100644
--- a/pkg/i18n/english.go
+++ b/pkg/i18n/english.go
@@ -264,6 +264,7 @@ type TranslationSet struct {
 	IgnoreFile                            string
 	ExcludeFile                           string
 	RefreshFiles                          string
+	FocusMainView                         string
 	Merge                                 string
 	RegularMerge                          string
 	MergeBranchTooltip                    string
@@ -508,6 +509,7 @@ type TranslationSet struct {
 	EnterCommitFile                       string
 	EnterCommitFileTooltip                string
 	ExitCustomPatchBuilder                string
+	ExitFocusedMainView                   string
 	EnterUpstream                         string
 	InvalidUpstream                       string
 	ReturnToRemotesList                   string
@@ -1328,6 +1330,7 @@ func EnglishTranslationSet() *TranslationSet {
 		IgnoreFile:                           `Add to .gitignore`,
 		ExcludeFile:                          `Add to .git/info/exclude`,
 		RefreshFiles:                         `Refresh files`,
+		FocusMainView:                        "Focus main view",
 		Merge:                                `Merge`,
 		RegularMerge:                         "Regular merge",
 		MergeBranchTooltip:                   "View options for merging the selected item into the current branch (regular merge, squash merge)",
@@ -1581,6 +1584,7 @@ func EnglishTranslationSet() *TranslationSet {
 		EnterCommitFile:                      "Enter file / Toggle directory collapsed",
 		EnterCommitFileTooltip:               "If a file is selected, enter the file so that you can add/remove individual lines to the custom patch. If a directory is selected, toggle the directory.",
 		ExitCustomPatchBuilder:               `Exit custom patch builder`,
+		ExitFocusedMainView:                  "Exit back to side panel",
 		EnterUpstream:                        `Enter upstream as '<remote> <branchname>'`,
 		InvalidUpstream:                      "Invalid upstream. Must be in the format '<remote> <branchname>'",
 		ReturnToRemotesList:                  `Return to remotes list`,
diff --git a/pkg/tasks/tasks.go b/pkg/tasks/tasks.go
index 9658753495a..e20e5804533 100644
--- a/pkg/tasks/tasks.go
+++ b/pkg/tasks/tasks.go
@@ -70,6 +70,9 @@ type LinesToRead struct {
 	// do an initial refresh. Only set for the initial read request; -1 for
 	// subsequent requests.
 	InitialRefreshAfter int
+
+	// Function to call after reading the lines is done
+	Then func()
 }
 
 func (m *ViewBufferManager) GetTaskKey() string {
@@ -91,16 +94,28 @@ func NewViewBufferManager(
 		beforeStart:  beforeStart,
 		refreshView:  refreshView,
 		onEndOfInput: onEndOfInput,
-		readLines:    make(chan LinesToRead, 1024),
+		readLines:    nil,
 		onNewKey:     onNewKey,
 		newGocuiTask: newGocuiTask,
 	}
 }
 
 func (self *ViewBufferManager) ReadLines(n int) {
-	go utils.Safe(func() {
-		self.readLines <- LinesToRead{Total: n, InitialRefreshAfter: -1}
-	})
+	if self.readLines != nil {
+		go utils.Safe(func() {
+			self.readLines <- LinesToRead{Total: n, InitialRefreshAfter: -1}
+		})
+	}
+}
+
+func (self *ViewBufferManager) ReadToEnd(then func()) {
+	if self.readLines != nil {
+		go utils.Safe(func() {
+			self.readLines <- LinesToRead{Total: -1, InitialRefreshAfter: -1, Then: then}
+		})
+	} else if then != nil {
+		then()
+	}
 }
 
 func (self *ViewBufferManager) NewCmdTask(start func() (*exec.Cmd, io.Reader), prefix string, linesToRead LinesToRead, onDoneFn func()) func(TaskOpts) error {
@@ -166,7 +181,6 @@ func (self *ViewBufferManager) NewCmdTask(start func() (*exec.Cmd, io.Reader), p
 
 		loadingMutex := deadlock.Mutex{}
 
-		// not sure if it's the right move to redefine this or not
 		self.readLines = make(chan LinesToRead, 1024)
 
 		scanner := bufio.NewScanner(r)
@@ -233,11 +247,17 @@ func (self *ViewBufferManager) NewCmdTask(start func() (*exec.Cmd, io.Reader), p
 				case <-opts.Stop:
 					break outer
 				case linesToRead := <-self.readLines:
-					for i := 0; i < linesToRead.Total; i++ {
+					callThen := func() {
+						if linesToRead.Then != nil {
+							linesToRead.Then()
+						}
+					}
+					for i := 0; linesToRead.Total == -1 || i < linesToRead.Total; i++ {
 						var ok bool
 						var line []byte
 						select {
 						case <-opts.Stop:
+							callThen()
 							break outer
 						case line, ok = <-lineChan:
 							break
@@ -257,6 +277,7 @@ func (self *ViewBufferManager) NewCmdTask(start func() (*exec.Cmd, io.Reader), p
 							// if we're here then there's nothing left to scan from the source
 							// so we're at the EOF and can flush the stale content
 							self.onEndOfInput()
+							callThen()
 							break outer
 						}
 						writeToView(append(line, '\n'))
@@ -271,9 +292,12 @@ func (self *ViewBufferManager) NewCmdTask(start func() (*exec.Cmd, io.Reader), p
 					}
 					refreshViewIfStale()
 					onFirstPageShown()
+					callThen()
 				}
 			}
 
+			self.readLines = nil
+
 			refreshViewIfStale()
 
 			if err := cmd.Wait(); err != nil {
@@ -349,6 +373,8 @@ func (self *ViewBufferManager) NewTask(f func(TaskOpts) error, key string) error
 	go utils.Safe(func() {
 		defer completeGocuiTask()
 
+		self.readLines = nil
+
 		self.taskIDMutex.Lock()
 		self.newTaskID++
 		taskID := self.newTaskID
diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go
index ceb8f0dacd4..c7118ff5c20 100644
--- a/pkg/utils/utils.go
+++ b/pkg/utils/utils.go
@@ -39,15 +39,6 @@ func SortRange(x int, y int) (int, int) {
 	return y, x
 }
 
-func Clamp(x int, min int, max int) int {
-	if x < min {
-		return min
-	} else if x > max {
-		return max
-	}
-	return x
-}
-
 func AsJson(i interface{}) string {
 	bytes, _ := json.MarshalIndent(i, "", "    ")
 	return string(bytes)
diff --git a/schema/config.json b/schema/config.json
index 1f207af1d7b..7b912a85c9b 100644
--- a/schema/config.json
+++ b/schema/config.json
@@ -1259,6 +1259,10 @@
             "5"
           ]
         },
+        "focusMainView": {
+          "type": "string",
+          "default": "0"
+        },
         "nextMatch": {
           "type": "string",
           "default": "n"
diff --git a/vendor/github.com/jesseduffield/gocui/view.go b/vendor/github.com/jesseduffield/gocui/view.go
index 2662ae6aceb..8c95bbe351c 100644
--- a/vendor/github.com/jesseduffield/gocui/view.go
+++ b/vendor/github.com/jesseduffield/gocui/view.go
@@ -519,9 +519,9 @@ func (v *View) setRune(x, y int, ch rune, fgColor, bgColor Attribute) {
 			}
 			fgColor = fgColor | AttrBold
 			if v.HighlightInactive {
-				bgColor = bgColor | v.InactiveViewSelBgColor
+				bgColor = (bgColor & AttrStyleBits) | v.InactiveViewSelBgColor
 			} else {
-				bgColor = bgColor | v.SelBgColor
+				bgColor = (bgColor & AttrStyleBits) | v.SelBgColor
 			}
 		}
 	}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index cc9c61b1adf..e2ec62f924f 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -226,7 +226,7 @@ github.com/jesseduffield/go-git/v5/utils/merkletrie/internal/frame
 github.com/jesseduffield/go-git/v5/utils/merkletrie/noder
 github.com/jesseduffield/go-git/v5/utils/sync
 github.com/jesseduffield/go-git/v5/utils/trace
-# github.com/jesseduffield/gocui v0.3.1-0.20250408140206-7f1bb9232647
+# github.com/jesseduffield/gocui v0.3.1-0.20250421160159-82c9aaeba2b9
 ## explicit; go 1.12
 github.com/jesseduffield/gocui
 # github.com/jesseduffield/kill v0.0.0-20250101124109-e216ddbe133a


Test patch:
diff --git a/pkg/integration/tests/patch_building/apply.go b/pkg/integration/tests/patch_building/apply.go
index 8774f07d71f..533ad1e23a0 100644
--- a/pkg/integration/tests/patch_building/apply.go
+++ b/pkg/integration/tests/patch_building/apply.go
@@ -48,7 +48,7 @@ var Apply = NewIntegrationTest(NewIntegrationTestArgs{
 
 		t.Views().Information().Content(Contains("Building patch"))
 
-		t.Views().PatchBuildingSecondary().Content(Contains("second line"))
+		t.Views().Secondary().Content(Contains("second line"))
 
 		t.Common().SelectPatchOption(MatchesRegexp(`Apply patch$`))
 
diff --git a/pkg/integration/tests/patch_building/apply_in_reverse.go b/pkg/integration/tests/patch_building/apply_in_reverse.go
index f15d4c35c0d..f2aa6b3a83f 100644
--- a/pkg/integration/tests/patch_building/apply_in_reverse.go
+++ b/pkg/integration/tests/patch_building/apply_in_reverse.go
@@ -35,7 +35,7 @@ var ApplyInReverse = NewIntegrationTest(NewIntegrationTestArgs{
 
 		t.Views().Information().Content(Contains("Building patch"))
 
-		t.Views().PatchBuildingSecondary().Content(Contains("+file1 content"))
+		t.Views().Secondary().Content(Contains("+file1 content"))
 
 		t.Common().SelectPatchOption(Contains("Apply patch in reverse"))
 
diff --git a/pkg/integration/tests/patch_building/apply_in_reverse_with_conflict.go b/pkg/integration/tests/patch_building/apply_in_reverse_with_conflict.go
index 78367cc84bc..1a09cea7af9 100644
--- a/pkg/integration/tests/patch_building/apply_in_reverse_with_conflict.go
+++ b/pkg/integration/tests/patch_building/apply_in_reverse_with_conflict.go
@@ -44,13 +44,13 @@ var ApplyInReverseWithConflict = NewIntegrationTest(NewIntegrationTestArgs{
 			Tap(func() {
 				t.Views().Information().Content(Contains("Building patch"))
 
-				t.Views().PatchBuildingSecondary().Content(
+				t.Views().Secondary().Content(
 					Contains("+more file1 content"))
 			}).
 			SelectNextItem().
 			PressPrimaryAction()
 
-		t.Views().PatchBuildingSecondary().Content(
+		t.Views().Secondary().Content(
 			Contains("+more file1 content").Contains("+more file2 content"))
 
 		t.Common().SelectPatchOption(Contains("Apply patch in reverse"))
diff --git a/pkg/integration/tests/patch_building/move_range_to_index.go b/pkg/integration/tests/patch_building/move_range_to_index.go
index fbacb50a51a..c8d379c97dc 100644
--- a/pkg/integration/tests/patch_building/move_range_to_index.go
+++ b/pkg/integration/tests/patch_building/move_range_to_index.go
@@ -43,8 +43,8 @@ var MoveRangeToIndex = NewIntegrationTest(NewIntegrationTestArgs{
 
 		t.Views().Information().Content(Contains("Building patch"))
 
-		t.Views().PatchBuildingSecondary().Content(Contains("second line"))
-		t.Views().PatchBuildingSecondary().Content(Contains("file two content"))
+		t.Views().Secondary().Content(Contains("second line"))
+		t.Views().Secondary().Content(Contains("file two content"))
 
 		t.Common().SelectPatchOption(MatchesRegexp(`Move patch out into index$`))
 
diff --git a/pkg/integration/tests/patch_building/move_to_index.go b/pkg/integration/tests/patch_building/move_to_index.go
index af2e867c7dd..6eca8865f55 100644
--- a/pkg/integration/tests/patch_building/move_to_index.go
+++ b/pkg/integration/tests/patch_building/move_to_index.go
@@ -35,7 +35,7 @@ var MoveToIndex = NewIntegrationTest(NewIntegrationTestArgs{
 
 		t.Views().Information().Content(Contains("Building patch"))
 
-		t.Views().PatchBuildingSecondary().Content(Contains("+file1 content"))
+		t.Views().Secondary().Content(Contains("+file1 content"))
 
 		t.Common().SelectPatchOption(Contains("Move patch out into index"))
 
diff --git a/pkg/integration/tests/patch_building/move_to_index_partial.go b/pkg/integration/tests/patch_building/move_to_index_partial.go
index f3515520260..827a3aedb84 100644
--- a/pkg/integration/tests/patch_building/move_to_index_partial.go
+++ b/pkg/integration/tests/patch_building/move_to_index_partial.go
@@ -53,7 +53,7 @@ var MoveToIndexPartial = NewIntegrationTest(NewIntegrationTestArgs{
 			Tap(func() {
 				t.Views().Information().Content(Contains("Building patch"))
 
-				t.Views().PatchBuildingSecondary().
+				t.Views().Secondary().
 					ContainsLines(
 						Contains(`-first line`),
 						Contains(`+first line2`),
diff --git a/pkg/integration/tests/patch_building/move_to_index_works_even_if_noprefix_is_set.go b/pkg/integration/tests/patch_building/move_to_index_works_even_if_noprefix_is_set.go
index 391223c6099..8c2ba84b019 100644
--- a/pkg/integration/tests/patch_building/move_to_index_works_even_if_noprefix_is_set.go
+++ b/pkg/integration/tests/patch_building/move_to_index_works_even_if_noprefix_is_set.go
@@ -33,7 +33,7 @@ var MoveToIndexWorksEvenIfNoprefixIsSet = NewIntegrationTest(NewIntegrationTestA
 			).
 			PressPrimaryAction()
 
-		t.Views().PatchBuildingSecondary().Content(Contains("+file1 content"))
+		t.Views().Secondary().Content(Contains("+file1 content"))
 
 		t.Common().SelectPatchOption(Contains("Move patch out into index"))
 
diff --git a/pkg/integration/tests/patch_building/remove_from_commit.go b/pkg/integration/tests/patch_building/remove_from_commit.go
index bfa0925d6ba..fbd78fcf76d 100644
--- a/pkg/integration/tests/patch_building/remove_from_commit.go
+++ b/pkg/integration/tests/patch_building/remove_from_commit.go
@@ -35,7 +35,7 @@ var RemoveFromCommit = NewIntegrationTest(NewIntegrationTestArgs{
 
 		t.Views().Information().Content(Contains("Building patch"))
 
-		t.Views().PatchBuildingSecondary().Content(Contains("+file1 content"))
+		t.Views().Secondary().Content(Contains("+file1 content"))
 
 		t.Common().SelectPatchOption(Contains("Remove patch from original commit"))
 
diff --git a/pkg/integration/tests/stash/stash_staged_partial_file.go b/pkg/integration/tests/stash/stash_staged_partial_file.go
index 8219b55d335..5337eaee2fe 100644
--- a/pkg/integration/tests/stash/stash_staged_partial_file.go
+++ b/pkg/integration/tests/stash/stash_staged_partial_file.go
@@ -58,11 +58,12 @@ var StashStagedPartialFile = NewIntegrationTest(NewIntegrationTestArgs{
 			)
 
 		t.Views().Files().
+			Focus().
 			Lines(
 				Contains("file-staged"),
 			)
 
-		t.Views().Staging().
+		t.Views().Main().
 			Content(
 				Contains(" line1\n line2\n line3\n-line4\n+line4 mod"),
 			)
diff --git a/pkg/integration/tests/ui/range_select.go b/pkg/integration/tests/ui/range_select.go
index 37c63e1d00b..f9487632a1c 100644
--- a/pkg/integration/tests/ui/range_select.go
+++ b/pkg/integration/tests/ui/range_select.go
@@ -39,7 +39,7 @@ var RangeSelect = NewIntegrationTest(NewIntegrationTestArgs{
 		// separately)
 		// In both views we're going to have 10 lines starting from 'line 1' going down to
 		// 'line 10'.
-		fileContent := ""
+		fileContent := "staged\n"
 		total := 10
 		for i := 1; i <= total; i++ {
 			remaining := total - i + 1
@@ -47,10 +47,11 @@ var RangeSelect = NewIntegrationTest(NewIntegrationTestArgs{
 			shell.EmptyCommit(fmt.Sprintf("line %d", remaining))
 			fileContent = fmt.Sprintf("%sline %d\n", fileContent, i)
 		}
-		shell.CreateFile("file1", fileContent)
+		shell.CreateFileAndAdd("file1", "staged\n")
+		shell.UpdateFile("file1", fileContent)
 	},
 	Run: func(t *TestDriver, keys config.KeybindingConfig) {
-		assertRangeSelectBehaviour := func(v *ViewDriver, otherView *ViewDriver, lineIdxOfFirstItem int) {
+		assertRangeSelectBehaviour := func(v *ViewDriver, focusOtherView func(), lineIdxOfFirstItem int) {
 			v.
 				SelectedLines(
 					Contains("line 1"),
@@ -154,7 +155,7 @@ var RangeSelect = NewIntegrationTest(NewIntegrationTestArgs{
 				)
 
 			// Click in view, press shift+arrow -> nonsticky range
-			otherView.Focus()
+			focusOtherView()
 			v.Click(1, lineIdxOfFirstItem).
 				SelectedLines(
 					Contains("line 1"),
@@ -166,7 +167,7 @@ var RangeSelect = NewIntegrationTest(NewIntegrationTestArgs{
 				)
 		}
 
-		assertRangeSelectBehaviour(t.Views().Commits().Focus(), t.Views().Branches(), 0)
+		assertRangeSelectBehaviour(t.Views().Commits().Focus(), func() { t.Views().Branches().Focus() }, 0)
 
 		t.Views().Files().
 			Focus().
@@ -175,6 +176,6 @@ var RangeSelect = NewIntegrationTest(NewIntegrationTestArgs{
 			).
 			PressEnter()
 
-		assertRangeSelectBehaviour(t.Views().Staging().IsFocused(), t.Views().Files(), 6)
+		assertRangeSelectBehaviour(t.Views().Staging().IsFocused(), func() { t.Views().Staging().PressTab() }, 6)
 	},
 })
diff --git a/pkg/tasks/tasks_test.go b/pkg/tasks/tasks_test.go
index 89056807fd0..72da6d83c2a 100644
--- a/pkg/tasks/tasks_test.go
+++ b/pkg/tasks/tasks_test.go
@@ -52,7 +52,7 @@ func TestNewCmdTaskInstantStop(t *testing.T) {
 		return cmd, reader
 	}
 
-	fn := manager.NewCmdTask(start, "prefix\n", LinesToRead{20, -1}, onDone)
+	fn := manager.NewCmdTask(start, "prefix\n", LinesToRead{20, -1, nil}, onDone)
 
 	_ = fn(TaskOpts{Stop: stop, InitialContentLoaded: func() { task.Done() }})
 
@@ -115,7 +115,7 @@ func TestNewCmdTask(t *testing.T) {
 		return cmd, reader
 	}
 
-	fn := manager.NewCmdTask(start, "prefix\n", LinesToRead{20, -1}, onDone)
+	fn := manager.NewCmdTask(start, "prefix\n", LinesToRead{20, -1, nil}, onDone)
 	wg := sync.WaitGroup{}
 	wg.Add(1)
 	go func() {
@@ -184,37 +184,37 @@ func TestNewCmdTaskRefresh(t *testing.T) {
 		{
 			"total < initialRefreshAfter",
 			150,
-			LinesToRead{100, 120},
+			LinesToRead{100, 120, nil},
 			[]int{100},
 		},
 		{
 			"total == initialRefreshAfter",
 			150,
-			LinesToRead{100, 100},
+			LinesToRead{100, 100, nil},
 			[]int{100},
 		},
 		{
 			"total > initialRefreshAfter",
 			150,
-			LinesToRead{100, 50},
+			LinesToRead{100, 50, nil},
 			[]int{50, 100},
 		},
 		{
 			"initialRefreshAfter == -1",
 			150,
-			LinesToRead{100, -1},
+			LinesToRead{100, -1, nil},
 			[]int{100},
 		},
 		{
 			"totalTaskLines < initialRefreshAfter",
 			25,
-			LinesToRead{100, 50},
+			LinesToRead{100, 50, nil},
 			[]int{25},
 		},
 		{
 			"totalTaskLines between total and initialRefreshAfter",
 			75,
-			LinesToRead{100, 50},
+			LinesToRead{100, 50, nil},
 			[]int{50, 75},
 		},
 	}