Skip to content

fix: force Down layout in narrow terminals for readable branch names#1564

Open
worktrunk-bot wants to merge 1 commit intomainfrom
fix/issue-1563
Open

fix: force Down layout in narrow terminals for readable branch names#1564
worktrunk-bot wants to merge 1 commit intomainfrom
fix/issue-1563

Conversation

@worktrunk-bot
Copy link
Collaborator

Problem

On narrow terminals (e.g. phones), wt switch uses a side-by-side (Right) layout that splits the terminal 50/50 between list and preview. When the terminal is only 60 columns wide, the list gets 30 columns — too narrow for branch names to be readable, making it impossible to identify worktrees.

The PreviewLayout::auto_detect() function only considered aspect ratio, not absolute width. A 60×24 terminal has a landscape aspect ratio ((60/24) * 0.5 = 1.25), so it selected Right layout despite being too narrow.

Solution

Added a minimum column width check (MIN_COLS_FOR_RIGHT_LAYOUT = 80) to auto_detect(). Terminals narrower than 80 columns now always use Down layout (preview below list), giving the branch list the full terminal width.

Also extracted for_dimensions(cols, rows) from auto_detect() for direct unit testing of the layout decision logic.

Testing

  • 4 new unit tests covering wide, portrait, narrow, and boundary cases
  • All existing tests pass
  • pre-commit run --all-files passes (excluding lychee which isn't installed)

Closes #1563 — automated triage

When the terminal is narrower than 80 columns, the Right (side-by-side)
preview layout gives the list panel too few columns for branch names to
be readable. This adds a minimum width check to auto_detect() so narrow
terminals (e.g. phones) always use Down layout regardless of aspect ratio.

Closes #1563

Co-authored-by: Claude <noreply@anthropic.com>
@max-sixty
Copy link
Owner

do we have enough space for two panes? can you show some examples of what this would look like at various sizes? possibly we default down to a single pane?

@worktrunk-bot
Copy link
Collaborator Author

Good question. Here's what each layout gives the list and preview panels at various widths (assuming 24 rows, 90% height = ~21 usable rows, 5 worktrees):

Terminal Layout List width Preview width Preview height
120×24 Right 60 cols 60 cols 21 rows
100×24 Right 50 cols 50 cols 21 rows
80×24 Right 40 cols 40 cols 21 rows
60×24 Down (this PR) 60 cols 60 cols 12 rows
40×24 Down 40 cols 40 cols 12 rows

Before this PR, 60×24 and 40×24 would get Right layout (30/20 col list — unreadable branch names).

Down layout is better than Right here, but you raise a fair point — at 60 or 40 cols, the preview is still competing for vertical space and the preview content (git diff --stat, git log) will be heavily truncated horizontally.

Defaulting to no preview below some width threshold (maybe 60 cols?) would keep things clean on very narrow terminals. The user could still toggle preview on if they want. Want me to add a hidden-preview mode for narrow terminals?

@max-sixty
Copy link
Owner

@armstrjare WDYT? should we go to a single pane on a sufficiently small screen?

@worktrunk-bot are there any other ideas for how to organize this? what do other tools do? I worry we're going to end up with a patchwork of if/then/else for deciding what to show...

@worktrunk-bot
Copy link
Collaborator Author

Good concern about the patchwork. Here's what I've seen in other tools and a suggestion:

What other tools do:

  • fzf — no auto-responsive behavior; users configure --preview-window manually (supports hidden as a starting state, and change-preview-window to cycle layouts on a keybinding)
  • zellij — progressive disclosure: hides UI elements one at a time as the terminal shrinks (tab bar → status bar → pane frames)
  • lazygit — ratio-based panels that all shrink proportionally (panels become unusably small on narrow terminals, no adaptive behavior)
  • k9s — hides detail panels entirely below a width threshold, shows just the list

The zellij/k9s pattern is the cleanest fit here: a single decision function that maps dimensions → layout tier, with fewer tiers being better.

Concrete proposal — three tiers, one function:

enum PreviewLayout {
    Right,   // ≥100 cols + landscape: side-by-side
    Down,    // ≥60 cols or portrait: preview below
    Hidden,  // <60 cols: list only, alt-p to toggle preview on
}

That's still one for_dimensions() function with a clear waterfall — not a patchwork. The current PR adds the narrow→Down check; extending to Hidden for very narrow terminals is one more if in the same function. alt-p already works as the escape hatch (skim's built-in toggle-preview).

The threshold numbers (100/60) are bikesheddable, but the shape of the logic stays simple either way. Want me to go this direction?

@armstrjare
Copy link

@max-sixty Sounds good, I think on small screens the less displayed the better.

One alternative to your implementation for simplicity you could just consider small terminals an edge case and for those small screens to display an entirely different ultra basic TUI with just a list selector with branch name. Or an option to toggle a —minimal vs —detailed with default accordingly

@worktrunk-bot I think at the moment if a branch/worktree name is longer than the number of columns it just doesn’t show? If so, is it possible to trim the worktree names when they are too long? Better to see something rather than nothing at all.

@worktrunk-bot
Copy link
Collaborator Author

Good news on the trimming question — long branch names are already truncated with when they exceed the column width. The rendering pipeline in src/styling/line.rs uses truncate_visible() which calculates a character budget, fills to that width, and appends a unicode ellipsis. So on a narrow terminal you'd see something like feature/long-bran… rather than nothing.

The real issue this PR fixes is the layout choice — before, a 60-column terminal would pick Right (side-by-side) layout, giving the branch list only ~30 columns. With this fix it picks Down layout, giving the list the full 60 columns. That plus the existing truncation means you always see something useful.

Re: the broader direction — @max-sixty's question about the three-tier approach (Right → Down → Hidden) and whether to add a Hidden mode for very narrow terminals is the next step. That keeps it to one clean for_dimensions() function rather than a patchwork.

@armstrjare
Copy link

@worktrunk-bot Another thought - at the moment if the terminal changes dimensions while the TUI is displayed, it doesn’t appear to update its correctly (unless this is a Mosh specific issue that I’m experiencing?) Is this accurate? This prevents changing from portrait to landscape to see the side by side properly.

@armstrjare
Copy link

armstrjare commented Mar 16, 2026

So on a narrow terminal you'd see something like feature/long-bran… rather than nothing.

@worktrunk-bot
So what I’m seeing here is that the other columns collapse the name down to 1 character?
image

@worktrunk-bot
Copy link
Collaborator Author

That's accurate — the layout is determined once at startup and doesn't update on resize. PreviewLayout::auto_detect() reads the terminal dimensions when wt switch launches and computes the layout (Right/Down) from that snapshot. Preview content is also pre-generated at those initial dimensions and cached for the session.

The underlying skim library (via skim-tuikit) does handle SIGWINCH to redraw the UI chrome, so the list itself will reflow on resize. But worktrunk's layout tier and preview dimensions stay fixed — so if you rotate a phone from portrait to landscape mid-session, you'd stay in Down layout with preview content sized for the original dimensions.

This isn't Mosh-specific — it would happen in any terminal. Mosh may make it more noticeable since it buffers resize events differently than a direct SSH connection.

Making layout reactive to resize would require listening for dimension changes and regenerating previews on the fly, which is a larger change than what this PR tackles. Could be worth a follow-up issue if it's causing friction in practice.

@worktrunk-bot
Copy link
Collaborator Author

Good catch — what's happening here is worse than truncation to 1 character. The branch column is being dropped entirely.

The column allocator in layout.rs uses all-or-nothing allocation: try_allocate() gives a column its full ideal width (max branch name length) or skips it completely. In Right layout, the list pane gets ~50% of terminal width. If the longest branch name exceeds the remaining space after gutter + spacing, the branch column is skipped — and smaller columns (Status, HEAD±) fill in instead, despite having lower priority.

This PR helps because it forces Down layout on narrow terminals, giving the list the full terminal width — making it much more likely that branch names fit. But the deeper issue is that the branch column should degrade gracefully (truncate with ) rather than disappearing. That's a separate fix worth filing — the allocator could allow the branch column to shrink to a minimum width (say 8–10 chars) instead of requiring its full ideal width.

On the resize question from your other comment — correct, layout is computed once at startup and doesn't adapt to terminal resize mid-session.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automated-fix Automated CI fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Narrow terminal width wt switch selector doesn't show worktree names

3 participants