Repository navigation
Conversation
Three numbers decided how the main window was divided: a 230px sidebar, a 6:4 graph against the detail pane, and a 280px file list inside the working directory. Each was a guess about a window nobody had seen, and no window could argue with it — a long commit message on a laptop screen had four tenths of the height and no way to ask for more. Every divider between those panes is draggable now, the file list included. ## Two units, and the reason for each The sidebar and the file list are pixels: what they need is set by the length of a branch name or a path, and that does not grow because the window did. The graph and the detail pane are a fraction of the height they share, because they are two halves of one view — a taller window should keep the graph the subject and the commit under it the footnote. Nothing can be dragged out of existence. The pixel splits stop at a minimum and at half the space they divide; the fraction keeps 15% for each side. A pane with no height has no divider left to drag it back with. ## How a drag arrives `mouse_area` reports that a press happened, not where, so the first pointer movement after a grab is the anchor and every size is a delta from it. That is what makes `layout.rs` testable without a window: nothing in it knows where anything is on screen. The pointer subscription exists only while a divider is held — following every mouse movement for a session to serve a drag nobody is doing is the most expensive subscription this could have had. `extent` — the size of the space being divided — is the one thing the arithmetic cannot derive, so the view measures it with `responsive` and the grab carries it. ## Where it is written `state.toml`, beside the window's own geometry, on release rather than per frame. It is state, not a setting: it changes by dragging, and nobody is going to open a file to choose a number of pixels. It is clamped again on the way in, so a hand-edited file cannot open hideGit with a sidebar wider than the window. A state file written before this exists loads unchanged. Double-clicking a divider restores it, which is why none of this needs a settings entry: the control that moved it is the control that undoes it. ## What it cost `staging::view` reached fourteen arguments — already flagged in #86 as wanting a struct, and still not this change. Its file list became its own function, because the row it sits in is built inside a `responsive` closure that may run more than once and therefore cannot consume anything. ## Tests Thirteen on the arithmetic, three on the divider widget — including that a press beside it grabs nothing — and five driving `update`, one of which holds that a divider dragged inside the working directory resizes the window's layout rather than the repository's. Two on the config file: a round trip, and a `state.toml` from before the dividers existed.
This was referenced Sep 17, 2026
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.
Stacked on #86 — GitHub will retarget this to
mainwhen that merges.Three numbers decided how the main window was divided: a 230px sidebar, a 6:4 graph against the detail pane, and a 280px file list inside the working directory. Each was a guess about a window nobody had seen, and no window could argue with it. Every divider between those panes is draggable now.
Two units, and the reason for each
Nothing can be dragged out of existence: the pixel splits stop at a minimum and at half the space they divide, the fraction keeps 15% for each side. A pane with no height has no divider left to drag it back with.
How a drag arrives
mouse_areareports that a press happened, not where, so the first pointer movement after a grab is the anchor and every size is a delta from it. That is what makeslayout.rstestable without a window — nothing in it knows where anything is on screen.The pointer subscription exists only while a divider is held. Following every mouse movement for a whole session to serve a drag nobody is doing would be the most expensive subscription in the application.
extent— the size of the space being divided — is the one thing pointer deltas cannot give, so the view measures it withresponsiveand the grab carries it.Where it is written
state.toml, beside the window's own geometry, on release rather than per frame. State, not a setting: it changes by dragging, and nobody is going to open a file to choose a number of pixels. Clamped again on the way in, so a hand-edited file cannot open hideGit with a sidebar wider than the window. Astate.tomlwritten before this existed loads unchanged — there is a test for that, becausedeny_unknown_fieldscuts both ways and losing the whole session over a missing table would be a poor upgrade.Double-clicking a divider restores it, which is why none of this needs a settings entry.
What it cost, said rather than hidden
staging::viewreached fourteen arguments. #86 already flagged that bundling them into a struct is the right answer and is not this change; that is still true.Its file list became its own function, because the row it sits in is now built inside a
responsiveclosure that may run more than once per frame and therefore cannot consume anything.Tests
layout.rs(13)NaNfrom the pointer, and a config file asking for the impossiblewidget/split.rs(3)app.rs(5)update, that a divider dragged inside the working directory resizes the window's layout, and that switching tabs does not take the sidebar width with itconfig.rs(2)state.tomlfrom before the dividers existedNot hand-tested in a window yet
Everything above is the headless suite. The drag itself — pointer capture, the resize cursor, whether the hairline is comfortable to hit — wants a look in a real window before this merges.
Gate
cargo fmt --all -- --check,cargo clippy --workspace --all-targets --all-features -- -D warnings,cargo test --workspace— 955 tests, all green.