Make the side panels configurable#5702
Open
stefanhaller wants to merge 16 commits into
Open
Conversation
Record the working preference that calls which come up during implementation (and weren't settled in planning) should be raised and decided together, not made unilaterally and discovered later in the diff. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The three branches of sidePanelChildren each spelled out the five side windows by name, so the panel order lived in three places and the status/stash sizing special-cases were tangled into positional literals. Map each branch over one `windows` slice instead, and fold the normal-height special-cases (status's fixed height, stash's collapse-unless-focused) into a single per-window function. Behavior is unchanged; this isolates the ordering so it can later come from config. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The remote-branches context is a transient guest that takes over a host
window when you drill into a remote. SubCommits and CommitFiles already
adopt their parent's window via SetWindowName when shown; RemoteBranches
relied instead on its static window name ("branches") matching the remotes
context's window. That assumption only holds while remotes lives in the
branches panel. Adopt the parent's window like the other transient guests
so remote branches render in the right place once panels are configurable.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The jump-label prefixes were assigned to each side view by name, twice (once for the on case, once for off), so the panel-to-views grouping and the panel order were baked into 28 positional statements. Express the grouping once as a slice of view groups and loop over it, deriving each panel's label from its index. The label lookup is now bounds-checked, so it no longer assumes exactly as many jump bindings as panels. Behavior is unchanged; this prepares the grouping to come from config. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This adds the user-facing surface for configuring the side panels: their order, which ones are visible, and how tabs are grouped into panels. Each entry is either a single panel name or a list of names sharing one panel as tabs, mirroring how the Keybinding type accepts a scalar or a sequence; the JSON schema restricts the names to the known set so editors can offer completion and catch typos. The default reproduces today's layout exactly. Validation rejects unknown or duplicated names, and requires the files, branches, and commits panels to always be present: a lot of code focuses those directly (e.g. after resolving a conflict or popping a stash), so allowing them to be hidden would let that code focus a hidden panel. Nothing reads the option yet; the layout still uses the hard-coded order. Wiring follows in a later commit so the inert surface (and its generated docs and schema) can be reviewed on its own. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The number of side panels is about to become configurable, so a fixed count of jump-to-panel keys no longer makes sense: a user who configures six panels shouldn't be forced to also extend jumpToBlock, and one who hides a panel shouldn't have to trim it. Drop the count check entirely (individual keys are still validated) and assign keys to panels positionally, for as many panels as there are keys. Surplus panels go without a jump key but remain reachable via the next/previous-panel keys. This also removes the log.Fatal that the count check guarded against. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the hard-coded side panel order, tab groupings, and window assignments with values resolved from the gui.sidePanels config. The panel order (SideWindows and the layout boxes), the tab strips (viewTabMap), the per-context window names, each window's default view, and the jump-label groups all now come from the config rather than from five separate hard-coded lists. A panel's window name is the name of its first tab, and panels not listed in the config get their own window name so their views stay hidden instead of overlapping a visible panel. Three small lookups translate config names into views, tab titles, and contexts; a test keeps them in sync with the set of valid names. The lookups are split this way (rather than one resolver) because configureViewProperties runs before the context tree exists, so the title/view lookups must not depend on it. The config is applied to a repo's contexts via applySidePanelConfig on every repo entry, including the cached-repo path: a repo's per-repo config can differ from the previously visited one's, so each repo's contexts must be (re)assigned from its own config rather than kept from when they were first built. With the default config this reproduces today's layout exactly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
These two views only ever appeared as tabs (of the files and commits panels), so unlike the other side views they had no title set; the tab strip supplied their label. Once a tab can be promoted to its own panel they can appear without a tab strip, so set their titles like the others. This has no effect in the default layout, where both are always tabs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
In squashed mode (short terminals) the unfocused side panels each reserve a row and the focused panel takes whatever is left, so once the unfocused panels' rows fill the height the focused panel collapses to nothing and panels below it render off-screen. The fixed floor of 9 was tuned for five panels; with the panel count now configurable (and promotion allowing up to ten), grow the floor by one per panel so we show the "not enough space" view instead of a broken layout. Five panels still floor at 9. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Within a window the visible tab is whichever view sits on top in the z-order, and onRepoViewReset establishes that z-order from a fixed list that needn't agree with the configured tab order. After ordering the views, bring each panel's first configured tab to the top so that, for a panel whose tabs have been reordered, the configured first tab is the one shown before the panel is focused. No effect on the default layout. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cover the three things gui.sidePanels enables: reordering the panels (swapping branches and commits, checked via their jump keys), hiding a panel (omitting stash, checked by cycling past the last panel and wrapping to the first), and promoting a tab to its own panel (worktrees becomes a top-level panel reachable by a jump key, and the files panel's remaining tabs cycle straight to submodules). The tests drive focus with explicit jump keys rather than ViewDriver.Focus, which assumes the default panel layout. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The tab-assignment loop only ever set a view's tabs; it never cleared them. That was fine when the groupings were fixed, but with gui.sidePanels a config reload can turn a tab into a standalone panel, and the old tab strip would linger on its title. Index the tab strips by view name and assign to every view, so views that dropped out of a multi-tab panel get their tabs cleared. No change for a given config; this only matters across a reload. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Lazygit reloads changed config files when its terminal window regains focus, but the test harness had no way to simulate that focus event, so the live config-reload path was untestable. Add a focus event to the replayed-events queue and expose it through the GuiDriver as FocusIn. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Side panel tabs share a window, so which tab is shown is decided by view z-order rather than the visibility flag (every tab in a window is 'visible'). Tests had no way to assert which tab is actually drawn in front, which is distinct from which view has keyboard focus. Expose the window's top view and add an IsActiveTab assertion built on it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When the config file changes and lazygit regains focus it reloads the config, but the side panel window assignments, default views, tab strips, and z-order were only ever set up on repo entry, so a changed sidePanels wouldn't take effect until restart. Re-apply it from the reload path: reassign windows and default views and restore each panel's default tab. The focused panel needs care: resetting it to its default tab would leave the focused tab hidden behind that default tab, so the panel looks unfocused even though its tab is selected. Re-focus the current context so its tab stays shown and highlighted; only when the new config hides the focused panel entirely do we move focus to the default side panel. Tab strips are already refreshed via configureViewProperties.
Exercises the path the live reload relies on: a per-repo lazygit.yml sets a different side panel layout, and switching between repos re-applies each one's own layout (the new-repo path for the cloned repo, the cached-repo path on switching back). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
7 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Lazygit's left-hand side panels — and the tabs within them — have always had a fixed arrangement. This adds a
gui.sidePanelsoption so you can lay them out the way you want.You can:
How it works
gui.sidePanelsis a top-to-bottom list. Each entry is a list of names that share one panel as tabs. The default isIf you want to promote
worktreesto a side panel of its own, and never want to see stashes, use