Skip to content

feat(native): split layouts as window tabs, with delegates beside the orchestrator - #821

Merged
omartelo merged 2 commits into
mainfrom
feat/native-split-layouts
Oct 18, 2026
Merged

omartelo merged 2 commits into
mainfrom
feat/native-split-layouts

Conversation

@omartelo

Copy link
Copy Markdown
Collaborator

What

Split layouts for the native window, designed from scratch rather than ported from the web's walls.

  • Layouts are tabs in the topbar strip, after the projects, and can mix projects. Rename one by double-clicking it or from its menu; a layout down to one pane closes.
  • Free split tree. Split the focused session right (Ctrl+Shift+G) or below (Ctrl+Shift+I) with a session picker, or drag a card from the sidebar onto a pane's edge. Drag a pane by its header onto another pane's edge to move it, or onto its middle to swap the two; Ctrl+Shift+R turns a pair between side by side and stacked. Drag the gap to resize, zoom the focused pane (Ctrl+Shift+M), cycle panes (Ctrl+Shift+F), take one out (Ctrl+Shift+W).
  • Dragging feels like a browser. A dragged card leaves the sidebar as a small pill under the pointer; resting it on another tab for 0.6 s brings that tab up. Drop zones only offer what fits: an edge whose pane would drop under the 480dp floor is not offered, and swap is only between panes of the layout.
  • Pane header: status, the project as an outlined pill only when the layout mixes projects, the session label, and the worktree only when the session runs in one.
  • Delegates beside the orchestrator (second commit). A session that opened others, in any project (originSessionId), offers them in the footer while it is in no layout: 3 delegates · Show beside opens a layout with it at the left and its delegates stacked at the right, as many as fit. In a layout its pane header reads 2 of 3 here · Add 1. A delegate in another layout is moved only after a confirm. A card in a layout names it.

How

  • layouts.go is the pure tree (paneNode, split/remove/move/rotate/stack), persisted under lich.native.layouts; the web's lich.panes is untouched and neither window reads the other's.
  • A session sits in one pane of one layout (one PTY, one size). Clicking its card shows it in its layout; its project tab shows it alone.
  • Drop zones are worked out from the pointer read by one handler over the whole window and where panes were drawn, not from Gio transfer targets: Enter reached targets laid out mid-drag only some of the time.
  • ui.SortCarry gains Hold (Gio dereferences a forgotten drag source after a tab switch takes the list off screen) and Release; SortList.Leaves lets a card leave the list without the list reshuffling.
  • The delegates room check (columnRoom) counts panes against the 180dp floor from the last frame's rects.

Notes

  • docs/ceilings.md gains a bullet on the native layouts: pane floor, exit band and file drop only on the focused pane, pinned cards can't be dragged, zones outside the carry, Hold, the four native-only hotkeys that a web rebinding drops, and the delegates footer item living outside the Footer layout.
  • All new strings land in the 7 native locales.

Verification

  • native/: gofmt -l ., go vet ./..., go test ./... (new tests for the tree, the model routing, drop zones, SortCarry, delegates).
  • Exercised in a disposable backend on Xvfb with screenshots: split, drag, swap, rotate, tab dwell, Show beside, Add, the move confirm.
  • Reviewed by the maintainer in a real window.

Layouts are tabs in the topbar strip, after the projects, and can mix projects. Each one is a binary split tree built by dragging a card or a pane header onto a pane's edges, or from the keyboard; the focused pane can be zoomed, rotated or unsplit, and a layout left with one pane closes. Layouts persist under lich.native.layouts, apart from the web's walls.
A session that opened others, in any project, offers them in the footer while it is in no layout: Show beside opens a layout with it at the left and its delegates stacked at the right, as many as fit the pane floor. In a layout its pane header counts the delegates there and adds the rest under the last one, asking first before taking one out of another layout. A card in a layout names it.
@omartelo
omartelo merged commit 61b8686 into main Oct 18, 2026
9 checks passed
@omartelo
omartelo deleted the feat/native-split-layouts branch October 18, 2026 01:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant