# Understanding Lazygit's Window and Panel Management System Architecture

> Explore lazygit's window and panel management architecture. Understand the three-layer system including WindowHelper, WindowArrangementHelper, and the layout engine for efficient terminal UI organization.

- Repository: [Jesse Duffield/lazygit](https://github.com/jesseduffield/lazygit)
- Tags: architecture
- Published: 2026-03-02

---

**Lazygit organizes its terminal UI into logical windows mapped to gocui views through a three-layer architecture: WindowHelper manages view-to-window mappings, WindowArrangementHelper calculates dimensions using the boxlayout library, and the layout engine applies these coordinates to the actual gocui views.**

The lazygit window and panel management system provides a flexible, config-driven approach to terminal UI layout. Unlike traditional TUI applications that hardcode coordinates, lazygit uses a declarative system where logical **windows** (screen regions like "status" or "files") are separated from concrete **views** (the actual `gocui` boxes that render content). This architecture allows dynamic panel swapping, responsive resizing, and support for both portrait and landscape modes.

## Core Concepts: Windows, Views, and Contexts

At the heart of lazygit's architecture are three distinct concepts that work together to render the interface.

**Windows** represent logical regions on the screen. These are named slots such as `"status"`, `"files"`, `"commits"`, `"main"`, and `"extras"`. A window defines *where* content should appear, but not *what* content renders there.

**Views** are concrete instances of `*gocui.View` from the underlying `gocui` library. These are the actual boxes that handle rendering, input, and scrolling. A view can be moved between windows, allowing dynamic UI changes like when the commit-files view shifts between the side panel and main panel.

**Contexts** carry the window identity. Every UI panel in lazygit implements a context interface that embeds `BaseContext` from [`pkg/gui/context/base_context.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/gui/context/base_context.go). Each context stores a `windowName` field that determines which logical window it occupies:

```go
func (self *BaseContext) GetWindowName() string { return self.windowName }

```

This separation allows the layout engine to ask "what context is in window X?" and "what window is context Y in?" independently of the actual rendering coordinates.

## The Three-Layer Layout Engine

Lazygit's window and panel management system operates through three tightly-coupled layers that transform abstract UI state into concrete terminal coordinates.

### Layer 1: Window-to-View Mapping

The first layer maintains the relationship between logical windows and concrete views. This responsibility lives in [`pkg/gui/controllers/helpers/window_helper.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/gui/controllers/helpers/window_helper.go) within the `WindowHelper` struct.

`WindowHelper` stores the mapping in a thread-safe map called `WindowViewNameMap` of type `ThreadSafeMap[string,string]`. This map tracks which view currently occupies each window slot.

Key operations include retrieving the view for a window:

```go
func (self *WindowHelper) GetViewNameForWindow(window string) string {
    viewName, ok := self.windowViewNameMap().Get(window)
    if !ok {
        panic(fmt.Sprintf("Viewname not found for window: %s", window))
    }
    return viewName
}

```

When a context (panel) is activated, it registers its window and view through `SetWindowContext`:

```go
func (self *WindowHelper) SetWindowContext(c types.Context) {
    if c.IsTransient() {
        self.resetWindowContext(c)
    }
    self.windowViewNameMap().Set(c.GetWindowName(), c.GetViewName())
}

```

The helper also handles edge cases like moving views between windows. When a view moves to a new window (such as the commit-files view shifting panels), `resetWindowContext` updates the mapping for the previous window to ensure no window remains empty.

Additional helper methods like `CurrentWindow()`, `TopViewInWindow()`, and `SideWindows()` support navigation and layout decisions throughout the application.

### Layer 2: Dimension Calculation

The second layer computes exact screen coordinates for every window based on terminal size, user configuration, and current UI state. This logic resides in [`pkg/gui/controllers/helpers/window_arrangement_helper.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/gui/controllers/helpers/window_arrangement_helper.go).

Lazygit uses the **boxlayout** library (from lazycore) to describe the UI as a tree of boxes. Each box has either a **weight** (relative size) or a **fixed size**, allowing flexible responsive layouts.

The `WindowArrangementHelper.GetWindowDimensions()` method builds a `WindowArrangementArgs` struct containing:

- Terminal dimensions (`Width`, `Height`)
- User configuration (`UserConfig`) including panel widths, portrait mode settings, and split mode preferences
- Current focus state (`CurrentWindow`, `CurrentSideWindow`)
- UI state flags (`ShowExtrasWindow`, `InSearchPrompt`, `IsAnyModeActive`)

This struct feeds into the pure function `GetWindowDimensions(args)` which:

1. Determines **portrait vs landscape** mode via `shouldUsePortraitMode`
2. Calculates **weights** for side-panel and main-panel sections using `getMidSectionWeights`
3. Constructs a hierarchical `boxlayout.Box` tree modeling:
   - Side panels (status, files, branches, commits, stash) with accordion expansion support (`sidePanelChildren`)
   - Main panels (`mainPanelChildren`) supporting horizontal or vertical splits
   - Optional **extras** panel (command log) and bottom **info** line
4. Calls `boxlayout.ArrangeWindows` to convert the tree into `map[string]boxlayout.Dimensions`

```go
func GetWindowDimensions(args WindowArrangementArgs) map[string]boxlayout.Dimensions {
    // … build the box tree …
    layerOneWindows := boxlayout.ArrangeWindows(root, 0, 0, args.Width, args.Height)
    limitWindows   := boxlayout.ArrangeWindows(&boxlayout.Box{Window: "limit"}, 0, 0, args.Width, args.Height)
    return MergeMaps(layerOneWindows, limitWindows)
}

```

The resulting map associates every logical window name (e.g., `"main"`, `"files"`) with concrete coordinates (`X0, Y0, X1, Y1`).

### Layer 3: Layout Rendering

The third layer applies calculated dimensions to the actual `gocui` views and handles visibility. This occurs in [`pkg/gui/layout.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/gui/layout.go) within the `layout` method.

Every UI redraw (triggered by resize, focus change, or mode change) executes:

1. **Collect UI strings** such as `informationStr` and `appStatus`
2. **Retrieve dimensions** from the arrangement helper:

```go
viewDimensions := gui.getWindowDimensions(informationStr, appStatus) // ← WindowArrangementHelper

```

3. **Iterate over contexts** with controlled bounds. For each context:
   - Look up the window name via `context.GetWindowName()` (defined in `BaseContext`)
   - Retrieve saved dimensions for that window
   - Call `gocui.Gui.SetView` with new coordinates, or hide the view if the window is inactive
   - Queue re-render if size or width changed

4. **Handle transient pop-ups** by showing them only when their window is current:

```go
view.Visible = gui.helpers.Window.GetViewNameForWindow(context.GetWindowName()) == context.GetViewName()

```

5. **Execute post-layout functions** queued via `AfterLayout`, completing the render cycle.

## Dynamic View Management

The lazygit window and panel management system supports dynamic view relocation, allowing views to move between windows based on user interaction or application state.

### Moving Views Between Windows

A concrete example is the **commit-files view**, which can appear in either the side panel or the main panel depending on context. The movement process involves:

1. Updating the context's window name:

```go
commitFilesContext.SetWindowName("files") // move it into the side-panel

```

2. Updating the mapping via `WindowHelper`:

```go
gui.helpers.Window.SetWindowContext(commitFilesContext) // update the map

```

3. Triggering a layout refresh:

```go
gui.refreshSidePanels() // triggers a layout pass

```

The `SetWindowContext` call updates the internal `WindowViewNameMap`, ensuring the next layout pass places the view in the dimensions of the `"files"` window.

### Handling Transient Views

Transient views (such as confirmation dialogs or search prompts) use the visibility check in [`layout.go`](https://github.com/jesseduffield/lazygit/blob/main/layout.go) to appear only when active. The system compares the current view name for a window against the context's view name:

```go
view.Visible = gui.helpers.Window.GetViewNameForWindow(context.GetWindowName()) == context.GetViewName()

```

This ensures that when a transient context closes, its view becomes invisible, and the underlying window content returns to focus.

## Summary

The lazygit window and panel management system implements a sophisticated three-layer architecture that separates logical window management from physical rendering:

- **WindowHelper** ([`pkg/gui/controllers/helpers/window_helper.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/gui/controllers/helpers/window_helper.go)) maintains thread-safe mappings between window names and view names, enabling dynamic view relocation and focus management.
- **WindowArrangementHelper** ([`pkg/gui/controllers/helpers/window_arrangement_helper.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/gui/controllers/helpers/window_arrangement_helper.go)) computes exact screen coordinates using the boxlayout library, supporting responsive layouts for portrait/landscape modes and configurable panel widths.
- **Layout Engine** ([`pkg/gui/layout.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/gui/layout.go)) applies calculated dimensions to gocui views, handles visibility for transient pop-ups, and triggers re-renders when state changes.

This declarative approach allows lazygit to support complex UI behaviors—such as moving the commit-files view between panels or adapting to terminal resizing—without hardcoded coordinates or manual view positioning.

## Frequently Asked Questions

### How does lazygit distinguish between windows and views?

Lazygit treats **windows** as logical named slots on the screen (such as `"status"`, `"files"`, or `"main"`), while **views** are concrete `*gocui.View` instances that handle actual rendering and input. The `WindowHelper` maintains a mapping between these concepts using `WindowViewNameMap`, allowing views to move between windows while preserving the logical layout structure defined by the window arrangement helper.

### What determines the size and position of panels in lazygit?

Panel dimensions are calculated by `WindowArrangementHelper.GetWindowDimensions()` in [`pkg/gui/controllers/helpers/window_arrangement_helper.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/gui/controllers/helpers/window_arrangement_helper.go). This function uses the **boxlayout** library to construct a tree of boxes with weights or fixed sizes, considering terminal dimensions, user configuration (such as `SidePanelWidth`), current focus state, and whether portrait mode is active. The result is a map of window names to concrete coordinates (`X0, Y0, X1, Y1`).

### Can views be moved between different windows in lazygit?

Yes, views can be relocated between windows dynamically. This is achieved by updating the context's window name via `SetWindowName()`, calling `WindowHelper.SetWindowContext()` to update the internal mapping, and triggering a layout refresh. The commit-files view demonstrates this pattern, moving between the side panel and main panel depending on user interaction, without requiring changes to the layout calculation logic.

### How does lazygit handle window resizing and layout updates?

When the terminal resizes or UI state changes (focus shifts, mode changes), the `layout` method in [`pkg/gui/layout.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/gui/layout.go) executes. It retrieves current dimensions from `WindowArrangementHelper`, iterates over all contexts to apply new coordinates via `gocui.Gui.SetView`, and manages visibility for transient views. The system queues re-renders for views whose dimensions changed, ensuring the UI adapts smoothly to new terminal sizes without manual coordinate adjustments.